lighthouse 9.0.0-dev.20211121 → 9.1.0
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/changelog.md +70 -0
- package/dist/report/bundle.esm.js +17 -3
- package/dist/report/flow.js +34 -23
- package/dist/report/standalone.js +3 -3
- package/flow-report/api.ts +18 -0
- package/flow-report/assets/standalone-flow-template.html +0 -1
- package/flow-report/{standalone-flow.tsx → clients/standalone.ts} +4 -5
- package/flow-report/src/app.tsx +20 -12
- package/flow-report/src/sidebar/flow.tsx +1 -3
- package/flow-report/src/sidebar/sidebar.tsx +1 -3
- package/flow-report/src/topbar.tsx +22 -13
- package/flow-report/src/util.ts +7 -0
- package/flow-report/src/wrappers/report.tsx +1 -0
- package/flow-report/src/wrappers/styles.tsx +16 -0
- package/flow-report/test/topbar-test.tsx +22 -6
- package/flow-report/types/flow-report.d.ts +4 -0
- package/lighthouse-cli/cli-flags.js +4 -4
- package/lighthouse-core/audits/audit.js +39 -7
- package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +1 -26
- package/lighthouse-core/audits/deprecations.js +10 -14
- package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +5 -4
- package/lighthouse-core/audits/dobetterweb/js-libraries.js +1 -1
- package/lighthouse-core/audits/dobetterweb/no-document-write.js +6 -4
- package/lighthouse-core/audits/dobetterweb/notification-on-start.js +6 -4
- package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +6 -4
- package/lighthouse-core/audits/errors-in-console.js +7 -4
- package/lighthouse-core/audits/no-unload-listeners.js +9 -10
- package/lighthouse-core/audits/seo/canonical.js +0 -14
- package/lighthouse-core/audits/seo/font-size.js +4 -2
- package/lighthouse-core/audits/seo/is-crawlable.js +2 -2
- package/lighthouse-core/audits/third-party-summary.js +2 -2
- package/lighthouse-core/audits/valid-source-maps.js +1 -1
- package/lighthouse-core/audits/violation-audit.js +10 -3
- package/lighthouse-core/computed/js-bundles.js +1 -3
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +3 -3
- package/lighthouse-core/gather/driver/execution-context.js +1 -1
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +7 -7
- package/lighthouse-core/gather/gatherers/stacks.js +0 -3
- package/lighthouse-core/lib/stack-packs.js +12 -8
- package/lighthouse-core/runner.js +7 -7
- package/package.json +4 -6
- package/report/generator/file-namer.js +12 -1
- package/report/generator/flow-report-assets.js +2 -1
- package/report/generator/report-assets.js +2 -5
- package/report/generator/report-generator.js +0 -1
- package/report/renderer/api.js +15 -1
- package/report/renderer/report-renderer.js +4 -1
- package/report/renderer/report-ui-features.js +1 -1
- package/report/renderer/swap-locale-feature.js +0 -5
- package/report/test/clients/bundle-test.js +2 -2
- package/report/test/renderer/report-ui-features-test.js +0 -1
- package/report/types/report-renderer.d.ts +2 -0
- package/shared/localization/locales/ar-XB.json +0 -3
- package/shared/localization/locales/ar.json +0 -3
- package/shared/localization/locales/bg.json +17 -20
- package/shared/localization/locales/ca.json +0 -3
- package/shared/localization/locales/cs.json +0 -3
- package/shared/localization/locales/da.json +16 -19
- package/shared/localization/locales/de.json +0 -3
- package/shared/localization/locales/el.json +0 -3
- package/shared/localization/locales/en-GB.json +19 -22
- package/shared/localization/locales/en-US.json +30 -3
- package/shared/localization/locales/en-XA.json +0 -3
- package/shared/localization/locales/en-XL.json +30 -3
- package/shared/localization/locales/es-419.json +17 -20
- package/shared/localization/locales/es.json +0 -3
- package/shared/localization/locales/fi.json +16 -19
- package/shared/localization/locales/fil.json +33 -36
- package/shared/localization/locales/fr.json +16 -19
- package/shared/localization/locales/he.json +16 -19
- package/shared/localization/locales/hi.json +0 -3
- package/shared/localization/locales/hr.json +16 -19
- package/shared/localization/locales/hu.json +16 -19
- package/shared/localization/locales/id.json +16 -19
- package/shared/localization/locales/it.json +37 -40
- package/shared/localization/locales/ja.json +16 -19
- package/shared/localization/locales/ko.json +16 -19
- package/shared/localization/locales/lt.json +17 -20
- package/shared/localization/locales/lv.json +0 -3
- package/shared/localization/locales/nl.json +16 -19
- package/shared/localization/locales/no.json +17 -20
- package/shared/localization/locales/pl.json +16 -19
- package/shared/localization/locales/pt-PT.json +37 -40
- package/shared/localization/locales/pt.json +16 -19
- package/shared/localization/locales/ro.json +0 -3
- package/shared/localization/locales/ru.json +16 -19
- package/shared/localization/locales/sk.json +0 -3
- package/shared/localization/locales/sl.json +0 -3
- package/shared/localization/locales/sr-Latn.json +16 -19
- package/shared/localization/locales/sr.json +16 -19
- package/shared/localization/locales/sv.json +17 -20
- package/shared/localization/locales/ta.json +0 -3
- package/shared/localization/locales/te.json +0 -3
- package/shared/localization/locales/th.json +0 -3
- package/shared/localization/locales/tr.json +0 -3
- package/shared/localization/locales/uk.json +17 -20
- package/shared/localization/locales/vi.json +0 -3
- package/shared/localization/locales/zh-HK.json +0 -3
- package/shared/localization/locales/zh-TW.json +0 -3
- package/shared/localization/locales/zh.json +0 -3
- package/types/externs.d.ts +5 -24
|
@@ -38,16 +38,18 @@ class NotificationOnStart extends ViolationAudit {
|
|
|
38
38
|
failureTitle: str_(UIStrings.failureTitle),
|
|
39
39
|
description: str_(UIStrings.description),
|
|
40
40
|
supportedModes: ['navigation'],
|
|
41
|
-
requiredArtifacts: ['ConsoleMessages'],
|
|
41
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'ScriptElements'],
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* @param {LH.Artifacts} artifacts
|
|
47
|
-
* @
|
|
47
|
+
* @param {LH.Audit.Context} context
|
|
48
|
+
* @return {Promise<LH.Audit.Product>}
|
|
48
49
|
*/
|
|
49
|
-
static audit(artifacts) {
|
|
50
|
-
const results =
|
|
50
|
+
static async audit(artifacts, context) {
|
|
51
|
+
const results =
|
|
52
|
+
await ViolationAudit.getViolationResults(artifacts, context, /notification permission/);
|
|
51
53
|
|
|
52
54
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
53
55
|
const headings = [
|
|
@@ -37,16 +37,18 @@ 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'],
|
|
40
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'ScriptElements'],
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* @param {LH.Artifacts} artifacts
|
|
46
|
-
* @
|
|
46
|
+
* @param {LH.Audit.Context} context
|
|
47
|
+
* @return {Promise<LH.Audit.Product>}
|
|
47
48
|
*/
|
|
48
|
-
static audit(artifacts) {
|
|
49
|
-
const results =
|
|
49
|
+
static async audit(artifacts, context) {
|
|
50
|
+
const results =
|
|
51
|
+
await ViolationAudit.getViolationResults(artifacts, context, /passive event listener/);
|
|
50
52
|
|
|
51
53
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
52
54
|
const headings = [
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
const log = require('lighthouse-logger');
|
|
14
14
|
const Audit = require('./audit.js');
|
|
15
|
+
const JsBundles = require('../computed/js-bundles.js');
|
|
15
16
|
const i18n = require('../lib/i18n/i18n.js');
|
|
16
17
|
|
|
17
18
|
const UIStrings = {
|
|
@@ -39,7 +40,7 @@ class ErrorLogs extends Audit {
|
|
|
39
40
|
title: str_(UIStrings.title),
|
|
40
41
|
failureTitle: str_(UIStrings.failureTitle),
|
|
41
42
|
description: str_(UIStrings.description),
|
|
42
|
-
requiredArtifacts: ['ConsoleMessages'],
|
|
43
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'ScriptElements'],
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -75,20 +76,22 @@ class ErrorLogs extends Audit {
|
|
|
75
76
|
/**
|
|
76
77
|
* @param {LH.Artifacts} artifacts
|
|
77
78
|
* @param {LH.Audit.Context} context
|
|
78
|
-
* @return {LH.Audit.Product}
|
|
79
|
+
* @return {Promise<LH.Audit.Product>}
|
|
79
80
|
*/
|
|
80
|
-
static audit(artifacts, context) {
|
|
81
|
+
static async audit(artifacts, context) {
|
|
81
82
|
/** @type {AuditOptions} */
|
|
82
83
|
const auditOptions = context.options;
|
|
84
|
+
const bundles = await JsBundles.request(artifacts, context);
|
|
83
85
|
|
|
84
86
|
/** @type {Array<{source: string, description: string|undefined, sourceLocation: LH.Audit.Details.SourceLocationValue|undefined}>} */
|
|
85
87
|
const consoleRows = artifacts.ConsoleMessages
|
|
86
88
|
.filter(item => item.level === 'error')
|
|
87
89
|
.map(item => {
|
|
90
|
+
const bundle = bundles.find(bundle => bundle.script.src === item.url);
|
|
88
91
|
return {
|
|
89
92
|
source: item.source,
|
|
90
93
|
description: item.text,
|
|
91
|
-
sourceLocation: Audit.makeSourceLocationFromConsoleMessage(item),
|
|
94
|
+
sourceLocation: Audit.makeSourceLocationFromConsoleMessage(item, bundle),
|
|
92
95
|
};
|
|
93
96
|
});
|
|
94
97
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const Audit = require('./audit.js');
|
|
9
|
+
const JsBundles = require('../computed/js-bundles.js');
|
|
9
10
|
const i18n = require('./../lib/i18n/i18n.js');
|
|
10
11
|
|
|
11
12
|
const UIStrings = {
|
|
@@ -29,15 +30,16 @@ class NoUnloadListeners extends Audit {
|
|
|
29
30
|
title: str_(UIStrings.title),
|
|
30
31
|
failureTitle: str_(UIStrings.failureTitle),
|
|
31
32
|
description: str_(UIStrings.description),
|
|
32
|
-
requiredArtifacts: ['GlobalListeners', 'JsUsage'],
|
|
33
|
+
requiredArtifacts: ['GlobalListeners', 'JsUsage', 'SourceMaps', 'ScriptElements'],
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* @param {LH.Artifacts} artifacts
|
|
38
|
-
* @
|
|
39
|
+
* @param {LH.Audit.Context} context
|
|
40
|
+
* @return {Promise<LH.Audit.Product>}
|
|
39
41
|
*/
|
|
40
|
-
static audit(artifacts) {
|
|
42
|
+
static async audit(artifacts, context) {
|
|
41
43
|
const unloadListeners = artifacts.GlobalListeners.filter(l => l.type === 'unload');
|
|
42
44
|
if (!unloadListeners.length) {
|
|
43
45
|
return {
|
|
@@ -45,6 +47,8 @@ class NoUnloadListeners extends Audit {
|
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
const bundles = await JsBundles.request(artifacts, context);
|
|
51
|
+
|
|
48
52
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
49
53
|
const headings = [
|
|
50
54
|
{key: 'source', itemType: 'source-location', text: str_(i18n.UIStrings.columnSource)},
|
|
@@ -74,14 +78,9 @@ class NoUnloadListeners extends Audit {
|
|
|
74
78
|
};
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
const bundle = bundles.find(bundle => bundle.script.src === url);
|
|
77
82
|
return {
|
|
78
|
-
source:
|
|
79
|
-
type: 'source-location',
|
|
80
|
-
url,
|
|
81
|
-
urlProvider: 'network',
|
|
82
|
-
line: listener.lineNumber,
|
|
83
|
-
column: listener.columnNumber,
|
|
84
|
-
},
|
|
83
|
+
source: Audit.makeSourceLocation(url, listener.lineNumber, listener.columnNumber, bundle),
|
|
85
84
|
};
|
|
86
85
|
});
|
|
87
86
|
|
|
@@ -38,11 +38,6 @@ const UIStrings = {
|
|
|
38
38
|
* @example {https://example.com/} url
|
|
39
39
|
*/
|
|
40
40
|
explanationPointsElsewhere: 'Points to another `hreflang` location ({url})',
|
|
41
|
-
/**
|
|
42
|
-
* @description Explanatory message stating that there was a failure in an audit caused by a URL pointing to a different domain.
|
|
43
|
-
* @example {https://example.com/} url
|
|
44
|
-
* */
|
|
45
|
-
explanationDifferentDomain: 'Points to a different domain ({url})',
|
|
46
41
|
/** Explanatory message stating that the page's canonical URL was pointing to the domain's root URL, which is a common mistake. "points" refers to the action of the 'rel=canonical' referencing another link. "root" refers to the starting/home page of the website. "domain" refers to the registered domain name of the website. */
|
|
47
42
|
explanationRoot: 'Points to the domain\'s root URL (the homepage), ' +
|
|
48
43
|
'instead of an equivalent page of content',
|
|
@@ -174,15 +169,6 @@ class Canonical extends Audit {
|
|
|
174
169
|
};
|
|
175
170
|
}
|
|
176
171
|
|
|
177
|
-
// bing and yahoo don't allow canonical URLs pointing to different domains, it's also
|
|
178
|
-
// a common mistake to publish a page with canonical pointing to e.g. a test domain or localhost
|
|
179
|
-
if (!URL.rootDomainsMatch(canonicalURL, baseURL)) {
|
|
180
|
-
return {
|
|
181
|
-
score: 0,
|
|
182
|
-
explanation: str_(UIStrings.explanationDifferentDomain, {url: canonicalURL.href}),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
172
|
// another common mistake is to have canonical pointing from all pages of the website to its root
|
|
187
173
|
if (
|
|
188
174
|
canonicalURL.origin === baseURL.origin &&
|
|
@@ -196,9 +196,11 @@ function findStyleRuleSource(baseURL, styleDeclaration, parentNode) {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
const
|
|
199
|
+
const source = Audit.makeSourceLocation(stylesheet.sourceURL, line, column);
|
|
200
|
+
source.urlProvider = urlProvider;
|
|
201
|
+
|
|
200
202
|
return {
|
|
201
|
-
source
|
|
203
|
+
source,
|
|
202
204
|
selector,
|
|
203
205
|
};
|
|
204
206
|
}
|
|
@@ -126,9 +126,9 @@ class IsCrawlable extends Audit {
|
|
|
126
126
|
const line = robotsTxt.getMatchingLineNumber(mainResource.url) || 1;
|
|
127
127
|
blockingDirectives.push({
|
|
128
128
|
source: {
|
|
129
|
-
type: /** @type {
|
|
129
|
+
type: /** @type {const} */ ('source-location'),
|
|
130
130
|
url: robotsFileUrl.href,
|
|
131
|
-
urlProvider: /** @type {
|
|
131
|
+
urlProvider: /** @type {const} */ ('network'),
|
|
132
132
|
line: line - 1,
|
|
133
133
|
column: 0,
|
|
134
134
|
},
|
|
@@ -216,12 +216,12 @@ class ThirdPartySummary extends Audit {
|
|
|
216
216
|
return {
|
|
217
217
|
...stats,
|
|
218
218
|
entity: {
|
|
219
|
-
type: /** @type {
|
|
219
|
+
type: /** @type {const} */ ('link'),
|
|
220
220
|
text: entity.name,
|
|
221
221
|
url: entity.homepage || '',
|
|
222
222
|
},
|
|
223
223
|
subItems: {
|
|
224
|
-
type: /** @type {
|
|
224
|
+
type: /** @type {const} */ ('subitems'),
|
|
225
225
|
items: ThirdPartySummary.makeSubItems(entity, summaries, stats),
|
|
226
226
|
},
|
|
227
227
|
};
|
|
@@ -110,7 +110,7 @@ class ValidSourceMaps extends Audit {
|
|
|
110
110
|
scriptUrl: scriptElement.src,
|
|
111
111
|
sourceMapUrl: sourceMap && sourceMap.sourceMapUrl,
|
|
112
112
|
subItems: {
|
|
113
|
-
type: /** @type {
|
|
113
|
+
type: /** @type {const} */ ('subitems'),
|
|
114
114
|
items: errors,
|
|
115
115
|
},
|
|
116
116
|
});
|
|
@@ -6,14 +6,18 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const Audit = require('./audit.js');
|
|
9
|
+
const JsBundles = require('../computed/js-bundles.js');
|
|
9
10
|
|
|
10
11
|
class ViolationAudit extends Audit {
|
|
11
12
|
/**
|
|
12
13
|
* @param {LH.Artifacts} artifacts
|
|
14
|
+
* @param {LH.Audit.Context} context
|
|
13
15
|
* @param {RegExp} pattern
|
|
14
|
-
* @return {Array<{source: LH.Audit.Details.SourceLocationValue}
|
|
16
|
+
* @return {Promise<Array<{source: LH.Audit.Details.SourceLocationValue}>>}
|
|
15
17
|
*/
|
|
16
|
-
static getViolationResults(artifacts, pattern) {
|
|
18
|
+
static async getViolationResults(artifacts, context, pattern) {
|
|
19
|
+
const bundles = await JsBundles.request(artifacts, context);
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* @template T
|
|
19
23
|
* @param {T} value
|
|
@@ -26,7 +30,10 @@ class ViolationAudit extends Audit {
|
|
|
26
30
|
const seen = new Set();
|
|
27
31
|
return artifacts.ConsoleMessages
|
|
28
32
|
.filter(entry => entry.url && entry.source === 'violation' && pattern.test(entry.text))
|
|
29
|
-
.map(
|
|
33
|
+
.map(entry => {
|
|
34
|
+
const bundle = bundles.find(bundle => bundle.script.src === entry.url);
|
|
35
|
+
return Audit.makeSourceLocationFromConsoleMessage(entry, bundle);
|
|
36
|
+
})
|
|
30
37
|
.filter(filterUndefined)
|
|
31
38
|
.filter(source => {
|
|
32
39
|
// Filter out duplicate entries since they are not differentiable to the user
|
|
@@ -98,9 +98,7 @@ class JSBundles {
|
|
|
98
98
|
|
|
99
99
|
const compiledUrl = SourceMap.scriptUrl || 'compiled.js';
|
|
100
100
|
const mapUrl = SourceMap.sourceMapUrl || 'compiled.js.map';
|
|
101
|
-
|
|
102
|
-
const rawMapForCdt = /** @type {any} */ (rawMap);
|
|
103
|
-
const map = new SDK.TextSourceMap(compiledUrl, mapUrl, rawMapForCdt);
|
|
101
|
+
const map = new SDK.TextSourceMap(compiledUrl, mapUrl, rawMap);
|
|
104
102
|
|
|
105
103
|
const content = scriptElement && scriptElement.content ? scriptElement.content : '';
|
|
106
104
|
const sizes = computeGeneratedFileSizes(map, content);
|
|
@@ -142,8 +142,8 @@ async function _collectDebugData(navigationContext, phaseState) {
|
|
|
142
142
|
/**
|
|
143
143
|
* @param {NavigationContext} navigationContext
|
|
144
144
|
* @param {PhaseState} phaseState
|
|
145
|
-
* @param {
|
|
146
|
-
* @param {
|
|
145
|
+
* @param {Awaited<ReturnType<typeof _setupNavigation>>} setupResult
|
|
146
|
+
* @param {Awaited<ReturnType<typeof _navigate>>} navigateResult
|
|
147
147
|
* @return {Promise<{finalUrl: string, artifacts: Partial<LH.GathererArtifacts>, warnings: Array<LH.IcuMessage>, pageLoadError: LH.LighthouseError | undefined}>}
|
|
148
148
|
*/
|
|
149
149
|
async function _computeNavigationResult(
|
|
@@ -195,7 +195,7 @@ async function _computeNavigationResult(
|
|
|
195
195
|
async function _navigation(navigationContext) {
|
|
196
196
|
const artifactState = getEmptyArtifactState();
|
|
197
197
|
const phaseState = {
|
|
198
|
-
gatherMode: /** @type {
|
|
198
|
+
gatherMode: /** @type {const} */ ('navigation'),
|
|
199
199
|
driver: navigationContext.driver,
|
|
200
200
|
computedCache: navigationContext.computedCache,
|
|
201
201
|
artifactDefinitions: navigationContext.navigation.artifacts,
|
|
@@ -166,7 +166,7 @@ class ExecutionContext {
|
|
|
166
166
|
* @param {{args: T, useIsolation?: boolean, deps?: Array<Function|string>}} options `args` should
|
|
167
167
|
* match the args of `mainFn`, and can be any serializable value. `deps` are functions that must be
|
|
168
168
|
* defined for `mainFn` to work.
|
|
169
|
-
* @return {
|
|
169
|
+
* @return {Promise<Awaited<R>>}
|
|
170
170
|
*/
|
|
171
171
|
evaluate(mainFn, options) {
|
|
172
172
|
const argsSerialized = ExecutionContext.serializeArguments(options.args);
|
|
@@ -16,19 +16,19 @@ const pageFunctions = require('../../lib/page-functions.js');
|
|
|
16
16
|
const FULL_PAGE_SCREENSHOT_QUALITY = 30;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* @
|
|
19
|
+
* @template {string} S
|
|
20
|
+
* @param {S} str
|
|
20
21
|
*/
|
|
21
|
-
function
|
|
22
|
-
return str.replace(/(-\w)/g, m => m[1].toUpperCase());
|
|
22
|
+
function kebabCaseToCamelCase(str) {
|
|
23
|
+
return /** @type {KebabToCamelCase<S>} */ (str.replace(/(-\w)/g, m => m[1].toUpperCase()));
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/* c8 ignore start */
|
|
26
27
|
|
|
27
28
|
// eslint-disable-next-line no-inner-declarations
|
|
28
29
|
function getObservedDeviceMetrics() {
|
|
29
|
-
// Convert the Web API's
|
|
30
|
-
const screenOrientationType =
|
|
31
|
-
snakeCaseToCamelCase(window.screen.orientation.type));
|
|
30
|
+
// Convert the Web API's kebab case (landscape-primary) to camel case (landscapePrimary).
|
|
31
|
+
const screenOrientationType = kebabCaseToCamelCase(window.screen.orientation.type);
|
|
32
32
|
return {
|
|
33
33
|
width: document.documentElement.clientWidth,
|
|
34
34
|
height: document.documentElement.clientHeight,
|
|
@@ -168,7 +168,7 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
168
168
|
observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
|
|
169
169
|
args: [],
|
|
170
170
|
useIsolation: true,
|
|
171
|
-
deps: [
|
|
171
|
+
deps: [kebabCaseToCamelCase],
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
|
|
@@ -82,9 +82,6 @@ class Stacks extends FRGatherer {
|
|
|
82
82
|
constructor() {
|
|
83
83
|
super();
|
|
84
84
|
|
|
85
|
-
// Because this file uses `fs.readFile` it gets parsed by a different branch of the browserify internals
|
|
86
|
-
// that cannot handle the latest ECMAScript features.
|
|
87
|
-
// See https://github.com/GoogleChrome/lighthouse/issues/12134
|
|
88
85
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
89
86
|
this.meta = {
|
|
90
87
|
supportedModes: ['snapshot', 'navigation'],
|
|
@@ -23,14 +23,6 @@ const stackPacksToInclude = [
|
|
|
23
23
|
packId: 'drupal',
|
|
24
24
|
requiredStacks: ['js:drupal'],
|
|
25
25
|
},
|
|
26
|
-
{
|
|
27
|
-
packId: 'react',
|
|
28
|
-
requiredStacks: ['js:react'],
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
packId: 'angular',
|
|
32
|
-
requiredStacks: ['js:@angular/core'],
|
|
33
|
-
},
|
|
34
26
|
{
|
|
35
27
|
packId: 'amp',
|
|
36
28
|
requiredStacks: ['js:amp'],
|
|
@@ -47,6 +39,18 @@ const stackPacksToInclude = [
|
|
|
47
39
|
packId: 'joomla',
|
|
48
40
|
requiredStacks: ['js:joomla'],
|
|
49
41
|
},
|
|
42
|
+
{
|
|
43
|
+
packId: 'next.js',
|
|
44
|
+
requiredStacks: ['js:next'],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
packId: 'angular',
|
|
48
|
+
requiredStacks: ['js:@angular/core'],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
packId: 'react',
|
|
52
|
+
requiredStacks: ['js:react'],
|
|
53
|
+
},
|
|
50
54
|
];
|
|
51
55
|
|
|
52
56
|
/**
|
|
@@ -132,7 +132,13 @@ class Runner {
|
|
|
132
132
|
|
|
133
133
|
/** @type {LH.RawIcu<LH.Result>} */
|
|
134
134
|
const i18nLhr = {
|
|
135
|
+
lighthouseVersion,
|
|
136
|
+
requestedUrl,
|
|
137
|
+
finalUrl: artifacts.URL.finalUrl,
|
|
138
|
+
fetchTime: artifacts.fetchTime,
|
|
135
139
|
gatherMode: artifacts.GatherContext.gatherMode,
|
|
140
|
+
runtimeError: Runner.getArtifactRuntimeError(artifacts),
|
|
141
|
+
runWarnings: lighthouseRunWarnings,
|
|
136
142
|
userAgent: artifacts.HostUserAgent,
|
|
137
143
|
environment: {
|
|
138
144
|
networkUserAgent: artifacts.NetworkUserAgent,
|
|
@@ -140,22 +146,16 @@ class Runner {
|
|
|
140
146
|
benchmarkIndex: artifacts.BenchmarkIndex,
|
|
141
147
|
credits,
|
|
142
148
|
},
|
|
143
|
-
lighthouseVersion,
|
|
144
|
-
fetchTime: artifacts.fetchTime,
|
|
145
|
-
requestedUrl: requestedUrl,
|
|
146
|
-
finalUrl: artifacts.URL.finalUrl,
|
|
147
|
-
runWarnings: lighthouseRunWarnings,
|
|
148
|
-
runtimeError: Runner.getArtifactRuntimeError(artifacts),
|
|
149
149
|
audits: auditResultsById,
|
|
150
150
|
configSettings: settings,
|
|
151
151
|
categories,
|
|
152
152
|
categoryGroups: runOpts.config.groups || undefined,
|
|
153
|
+
stackPacks: stackPacks.getStackPacks(artifacts.Stacks),
|
|
153
154
|
timing: this._getTiming(artifacts),
|
|
154
155
|
i18n: {
|
|
155
156
|
rendererFormattedStrings: format.getRendererFormattedStrings(settings.locale),
|
|
156
157
|
icuMessagePaths: {},
|
|
157
158
|
},
|
|
158
|
-
stackPacks: stackPacks.getStackPacks(artifacts.Stacks),
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
// Replace ICU message references with localized strings; save replaced paths in lhr.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -108,7 +108,6 @@
|
|
|
108
108
|
"@testing-library/preact": "^2.0.1",
|
|
109
109
|
"@testing-library/preact-hooks": "^1.1.0",
|
|
110
110
|
"@types/archiver": "^2.1.2",
|
|
111
|
-
"@types/browserify": "^12.0.36",
|
|
112
111
|
"@types/chrome": "^0.0.154",
|
|
113
112
|
"@types/configstore": "^4.0.0",
|
|
114
113
|
"@types/cpy": "^5.1.0",
|
|
@@ -138,7 +137,6 @@
|
|
|
138
137
|
"acorn": "^8.5.0",
|
|
139
138
|
"angular": "^1.7.4",
|
|
140
139
|
"archiver": "^3.0.0",
|
|
141
|
-
"browserify": "^17.0.0",
|
|
142
140
|
"c8": "^7.4.0",
|
|
143
141
|
"chalk": "^2.4.1",
|
|
144
142
|
"chrome-devtools-frontend": "1.0.922924",
|
|
@@ -181,8 +179,8 @@
|
|
|
181
179
|
"tabulator-tables": "^4.9.3",
|
|
182
180
|
"terser": "^5.3.8",
|
|
183
181
|
"ts-jest": "^27.0.4",
|
|
184
|
-
"typed-query-selector": "^2.
|
|
185
|
-
"typescript": "4.
|
|
182
|
+
"typed-query-selector": "^2.6.1",
|
|
183
|
+
"typescript": "^4.5.2",
|
|
186
184
|
"wait-for-expect": "^3.0.2",
|
|
187
185
|
"webtreemap-cdt": "^3.2.1"
|
|
188
186
|
},
|
|
@@ -198,7 +196,7 @@
|
|
|
198
196
|
"jpeg-js": "^0.4.1",
|
|
199
197
|
"js-library-detector": "^6.4.0",
|
|
200
198
|
"lighthouse-logger": "^1.3.0",
|
|
201
|
-
"lighthouse-stack-packs": "^1.
|
|
199
|
+
"lighthouse-stack-packs": "^1.6.0",
|
|
202
200
|
"lodash.clonedeep": "^4.5.0",
|
|
203
201
|
"lodash.get": "^4.4.2",
|
|
204
202
|
"lodash.isequal": "^4.5.0",
|
|
@@ -43,4 +43,15 @@ function getLhrFilenamePrefix(lhr) {
|
|
|
43
43
|
return getFilenamePrefix(hostname, lhr.fetchTime);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Generate a filenamePrefix of name_YYYY-MM-DD_HH-MM-SS.
|
|
48
|
+
* @param {{name: string, steps: Array<{lhr: {fetchTime: string}}>}} flowResult
|
|
49
|
+
* @return {string}
|
|
50
|
+
*/
|
|
51
|
+
function getFlowResultFilenamePrefix(flowResult) {
|
|
52
|
+
const lhr = flowResult.steps[0].lhr;
|
|
53
|
+
const name = flowResult.name.replace(/\s/g, '-');
|
|
54
|
+
return getFilenamePrefix(name, lhr.fetchTime);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {getLhrFilenamePrefix, getFilenamePrefix, getFlowResultFilenamePrefix};
|
|
@@ -9,12 +9,13 @@ const fs = require('fs');
|
|
|
9
9
|
|
|
10
10
|
/* eslint-disable max-len */
|
|
11
11
|
const FLOW_REPORT_TEMPLATE = fs.readFileSync(`${__dirname}/../../flow-report/assets/standalone-flow-template.html`, 'utf8');
|
|
12
|
+
const REGULAR_REPORT_CSS = fs.readFileSync(__dirname + '/../assets/styles.css', 'utf8');
|
|
12
13
|
const FLOW_REPORT_CSS = fs.readFileSync(`${__dirname}/../../flow-report/assets/styles.css`, 'utf8');
|
|
13
14
|
const FLOW_REPORT_JAVASCRIPT = fs.readFileSync(`${__dirname}/../../dist/report/flow.js`, 'utf8');
|
|
14
15
|
/* eslint-enable max-len */
|
|
15
16
|
|
|
16
17
|
module.exports = {
|
|
17
18
|
FLOW_REPORT_TEMPLATE,
|
|
18
|
-
FLOW_REPORT_CSS,
|
|
19
|
+
FLOW_REPORT_CSS: [REGULAR_REPORT_CSS, FLOW_REPORT_CSS].join('\n'),
|
|
19
20
|
FLOW_REPORT_JAVASCRIPT,
|
|
20
21
|
};
|
|
@@ -12,15 +12,12 @@ const flowReportAssets = require('./flow-report-assets.js');
|
|
|
12
12
|
const REPORT_TEMPLATE = fs.readFileSync(__dirname + '/../assets/standalone-template.html',
|
|
13
13
|
'utf8');
|
|
14
14
|
const REPORT_JAVASCRIPT = fs.readFileSync(__dirname + '/../../dist/report/standalone.js', 'utf8');
|
|
15
|
-
const REPORT_CSS = fs.readFileSync(__dirname + '/../assets/styles.css', 'utf8');
|
|
16
15
|
|
|
17
|
-
// Changes to this export interface should be reflected in build/build-dt-report-resources.js
|
|
18
|
-
// and clients/devtools-report-assets.js
|
|
19
16
|
module.exports = {
|
|
20
17
|
REPORT_TEMPLATE,
|
|
21
18
|
REPORT_JAVASCRIPT,
|
|
22
|
-
REPORT_CSS,
|
|
23
19
|
// Flow report assets are not needed for every bundle.
|
|
24
|
-
//
|
|
20
|
+
// Replacing/ignoring flow-report-assets.js (e.g. `rollupPlugins.shim`) will
|
|
21
|
+
// remove the flow assets from the bundle.
|
|
25
22
|
...flowReportAssets,
|
|
26
23
|
};
|
|
@@ -70,7 +70,6 @@ class ReportGenerator {
|
|
|
70
70
|
{search: '%%LIGHTHOUSE_FLOW_JSON%%', replacement: sanitizedJson},
|
|
71
71
|
{search: '%%LIGHTHOUSE_FLOW_JAVASCRIPT%%', replacement: htmlReportAssets.FLOW_REPORT_JAVASCRIPT},
|
|
72
72
|
{search: '/*%%LIGHTHOUSE_FLOW_CSS%%*/', replacement: htmlReportAssets.FLOW_REPORT_CSS},
|
|
73
|
-
{search: '/*%%LIGHTHOUSE_CSS%%*/', replacement: htmlReportAssets.REPORT_CSS},
|
|
74
73
|
/* eslint-enable max-len */
|
|
75
74
|
]);
|
|
76
75
|
}
|
package/report/renderer/api.js
CHANGED
|
@@ -63,4 +63,18 @@ function convertMarkdownCodeSnippets(markdownText) {
|
|
|
63
63
|
return dom.convertMarkdownCodeSnippets(markdownText);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* @return {DocumentFragment}
|
|
68
|
+
*/
|
|
69
|
+
function createStylesElement() {
|
|
70
|
+
const dom = new DOM(document, document.documentElement);
|
|
71
|
+
return dom.createComponent('styles');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
renderReport,
|
|
76
|
+
renderCategoryScore,
|
|
77
|
+
saveFile,
|
|
78
|
+
convertMarkdownCodeSnippets,
|
|
79
|
+
createStylesElement,
|
|
80
|
+
};
|
|
@@ -29,6 +29,7 @@ import {PerformanceCategoryRenderer} from './performance-category-renderer.js';
|
|
|
29
29
|
import {PwaCategoryRenderer} from './pwa-category-renderer.js';
|
|
30
30
|
import {Util} from './util.js';
|
|
31
31
|
|
|
32
|
+
|
|
32
33
|
export class ReportRenderer {
|
|
33
34
|
/**
|
|
34
35
|
* @param {DOM} dom
|
|
@@ -322,7 +323,9 @@ export class ReportRenderer {
|
|
|
322
323
|
categoryRenderer.injectFinalScreenshot(categories, report.audits, scoreScale);
|
|
323
324
|
|
|
324
325
|
const reportFragment = this._dom.createFragment();
|
|
325
|
-
|
|
326
|
+
if (!this._opts.omitGlobalStyles) {
|
|
327
|
+
reportFragment.append(this._dom.createComponent('styles'));
|
|
328
|
+
}
|
|
326
329
|
|
|
327
330
|
if (!this._opts.omitTopbar) {
|
|
328
331
|
reportFragment.appendChild(this._renderReportTopbar(report));
|
|
@@ -154,7 +154,7 @@ export class ReportUIFeatures {
|
|
|
154
154
|
if (this._topbar) {
|
|
155
155
|
this._topbar.resetUIState();
|
|
156
156
|
}
|
|
157
|
-
return `<!doctype html><body>${this._dom.rootEl.outerHTML}`;
|
|
157
|
+
return `<!doctype html><body style="margin: 0">${this._dom.rootEl.outerHTML}`;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
@@ -53,13 +53,8 @@ export class SwapLocaleFeature {
|
|
|
53
53
|
optionEl.value = locale;
|
|
54
54
|
if (locale === currentLocale) optionEl.selected = true;
|
|
55
55
|
|
|
56
|
-
// @ts-expect-error: waiting for typescript 4.5. Might need to add "ES2020.Intl"
|
|
57
|
-
// to tsconfig libs.
|
|
58
|
-
// https://github.com/microsoft/TypeScript/pull/44022#issuecomment-915087098
|
|
59
56
|
if (window.Intl && Intl.DisplayNames) {
|
|
60
|
-
// @ts-expect-error
|
|
61
57
|
const currentLocaleDisplay = new Intl.DisplayNames([currentLocale], {type: 'language'});
|
|
62
|
-
// @ts-expect-error
|
|
63
58
|
const optionLocaleDisplay = new Intl.DisplayNames([locale], {type: 'language'});
|
|
64
59
|
|
|
65
60
|
const optionLocaleName = optionLocaleDisplay.of(locale);
|
|
@@ -30,6 +30,7 @@ describe('lighthouseRenderer bundle', () => {
|
|
|
30
30
|
|
|
31
31
|
global.window = global.self = window;
|
|
32
32
|
global.window.requestAnimationFrame = fn => fn();
|
|
33
|
+
global.HTMLInputElement = window.HTMLInputElement;
|
|
33
34
|
// Stub out matchMedia for Node.
|
|
34
35
|
global.self.matchMedia = function() {
|
|
35
36
|
return {
|
|
@@ -44,9 +45,9 @@ describe('lighthouseRenderer bundle', () => {
|
|
|
44
45
|
|
|
45
46
|
afterAll(() => {
|
|
46
47
|
global.window = global.self = undefined;
|
|
48
|
+
global.HTMLInputElement = undefined;
|
|
47
49
|
});
|
|
48
50
|
|
|
49
|
-
|
|
50
51
|
it('renders an LHR to DOM', () => {
|
|
51
52
|
const lhr = /** @type {LH.Result} */ JSON.parse(sampleResultsStr);
|
|
52
53
|
const reportContainer = document.body;
|
|
@@ -58,7 +59,6 @@ describe('lighthouseRenderer bundle', () => {
|
|
|
58
59
|
const features = new lighthouseRenderer.ReportUIFeatures(renderer._dom);
|
|
59
60
|
features.initFeatures(lhr);
|
|
60
61
|
|
|
61
|
-
|
|
62
62
|
// Check that the report exists and has some content.
|
|
63
63
|
expect(reportContainer instanceof document.defaultView.Element).toBeTruthy();
|
|
64
64
|
expect(reportContainer.outerHTML.length).toBeGreaterThan(50000);
|
|
@@ -60,7 +60,6 @@ describe('ReportUIFeatures', () => {
|
|
|
60
60
|
|
|
61
61
|
global.HTMLElement = document.window.HTMLElement;
|
|
62
62
|
global.HTMLInputElement = document.window.HTMLInputElement;
|
|
63
|
-
global.HTMLInputElement = document.window.HTMLInputElement;
|
|
64
63
|
|
|
65
64
|
global.window = document.window;
|
|
66
65
|
global.window.requestAnimationFrame = fn => fn();
|
|
@@ -17,6 +17,8 @@ declare module Renderer {
|
|
|
17
17
|
|
|
18
18
|
/** Disable the topbar UI component */
|
|
19
19
|
omitTopbar?: boolean;
|
|
20
|
+
/** Prevent injection of report styles. Set to true if these styles are already included by the environment. */
|
|
21
|
+
omitGlobalStyles?: boolean;
|
|
20
22
|
/**
|
|
21
23
|
* Convert report anchor links to a different format.
|
|
22
24
|
* Flow report uses this to convert `#seo` to `#index=0&anchor=seo`.
|
|
@@ -1292,9 +1292,6 @@
|
|
|
1292
1292
|
"lighthouse-core/audits/seo/canonical.js | explanationConflict": {
|
|
1293
1293
|
"message": "Multiple conflicting URLs ({urlList})"
|
|
1294
1294
|
},
|
|
1295
|
-
"lighthouse-core/audits/seo/canonical.js | explanationDifferentDomain": {
|
|
1296
|
-
"message": "Points to a different domain ({url})"
|
|
1297
|
-
},
|
|
1298
1295
|
"lighthouse-core/audits/seo/canonical.js | explanationInvalid": {
|
|
1299
1296
|
"message": "Invalid URL ({url})"
|
|
1300
1297
|
},
|