lighthouse 9.5.0-dev.20220717 → 9.5.0-dev.20220720

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.
@@ -8,13 +8,13 @@
8
8
 
9
9
  set -eux
10
10
 
11
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
12
+ LH_ROOT="$SCRIPT_DIR/../.."
13
+
11
14
  ARGS=(
12
15
  --testMatch='{flow-report/**/*-test.ts,flow-report/**/*-test.tsx}'
13
- --require=flow-report/test/setup/env-setup.ts
14
- --loader=@esbuild-kit/esm-loader
15
- # util-test.tsx won't finish on its own because of an open MessagePort, so help it out.
16
- # See https://github.com/jsdom/jsdom/issues/2448#issuecomment-802288244
17
- --exit
16
+ --require="$LH_ROOT/flow-report/test/setup/env-setup.ts"
18
17
  )
19
18
 
20
- yarn mocha ${ARGS[*]} "$@"
19
+ cd "$LH_ROOT"
20
+ node --loader=@esbuild-kit/esm-loader lighthouse-core/test/scripts/run-mocha-tests.js ${ARGS[*]} "$@"
@@ -21,32 +21,35 @@ fs.writeFileSync(`${LH_ROOT}/node_modules/@testing-library/preact/dist/esm/packa
21
21
  fs.writeFileSync(`${LH_ROOT}/node_modules/@testing-library/preact-hooks/src/package.json`,
22
22
  '{"type": "module"}');
23
23
 
24
- // @ts-expect-error
25
- global.React = preact;
26
-
27
- export default {
28
- mochaHooks: {
29
- beforeEach() {
30
- const {window} = new JSDOM(undefined, {
31
- url: 'file:///Users/example/report.html/',
32
- });
33
- global.window = window as any;
34
- global.document = window.document;
35
- global.location = window.location;
36
- global.self = global.window;
37
-
38
- // Use JSDOM types as necessary.
39
- global.Blob = window.Blob;
40
- global.HTMLInputElement = window.HTMLInputElement;
41
-
42
- // Functions not implemented in JSDOM.
43
- window.Element.prototype.scrollIntoView = jestMock.fn();
44
- global.self.matchMedia = jestMock.fn<any, any>(() => ({
45
- addListener: jestMock.fn(),
46
- }));
47
-
48
- // @ts-expect-error: for @testing-library/preact-hooks
49
- global.MessageChannel = MessageChannel;
50
- },
24
+ const rootHooks = {
25
+ beforeAll() {
26
+ // @ts-expect-error
27
+ global.React = preact;
51
28
  },
29
+ beforeEach() {
30
+ const {window} = new JSDOM(undefined, {
31
+ url: 'file:///Users/example/report.html/',
32
+ });
33
+ global.window = window as any;
34
+ global.document = window.document;
35
+ global.location = window.location;
36
+ global.self = global.window;
37
+
38
+ // Use JSDOM types as necessary.
39
+ global.Blob = window.Blob;
40
+ global.HTMLInputElement = window.HTMLInputElement;
41
+
42
+ // Functions not implemented in JSDOM.
43
+ window.Element.prototype.scrollIntoView = jestMock.fn();
44
+ global.self.matchMedia = jestMock.fn<any, any>(() => ({
45
+ addListener: jestMock.fn(),
46
+ }));
47
+
48
+ // @ts-expect-error: for @testing-library/preact-hooks
49
+ global.MessageChannel = MessageChannel;
50
+ },
51
+ };
52
+
53
+ export {
54
+ rootHooks,
52
55
  };
@@ -42,14 +42,18 @@ async function setup() {
42
42
  * CHROME_PATH determines which Chrome is used–otherwise the default is puppeteer's chrome binary.
43
43
  * @param {string} url
44
44
  * @param {LH.Config.Json=} configJson
45
- * @param {{isDebug?: boolean}=} testRunnerOptions
45
+ * @param {{isDebug?: boolean, useLegacyNavigation?: boolean}=} testRunnerOptions
46
46
  * @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts, log: string}>}
47
47
  */
48
48
  async function runLighthouse(url, configJson, testRunnerOptions = {}) {
49
49
  const chromeFlags = [
50
50
  `--custom-devtools-frontend=file://${devtoolsDir}/out/Default/gen/front_end`,
51
51
  ];
52
- const {lhr, artifacts, logs} = await testUrlFromDevtools(url, configJson, chromeFlags);
52
+ const {lhr, artifacts, logs} = await testUrlFromDevtools(url, {
53
+ config: configJson,
54
+ chromeFlags,
55
+ useLegacyNavigation: testRunnerOptions.useLegacyNavigation,
56
+ });
53
57
 
54
58
  if (testRunnerOptions.isDebug) {
55
59
  const outputDir = fs.mkdtempSync(os.tmpdir() + '/lh-smoke-cdt-runner-');
@@ -5,7 +5,7 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- import defaultConfig from './default-config.js';
8
+ import legacyDefaultConfig from './legacy-default-config.js';
9
9
  import * as constants from './constants.js';
10
10
  import format from '../../shared/localization/format.js';
11
11
  import * as validation from './../fraggle-rock/config/validation.js';
@@ -24,7 +24,7 @@ import {
24
24
  } from './config-helpers.js';
25
25
  import {getModuleDirectory} from '../../esm-utils.js';
26
26
 
27
- const defaultConfigPath = './default-config.js';
27
+ const defaultConfigPath = './legacy-default-config.js';
28
28
 
29
29
  /** @typedef {typeof import('../gather/gatherers/gatherer.js').Gatherer} GathererConstructor */
30
30
  /** @typedef {InstanceType<GathererConstructor>} Gatherer */
@@ -168,7 +168,7 @@ class Config {
168
168
  let configPath = flags?.configPath;
169
169
 
170
170
  if (!configJSON) {
171
- configJSON = defaultConfig;
171
+ configJSON = legacyDefaultConfig;
172
172
  configPath = path.resolve(getModuleDirectory(import.meta), defaultConfigPath);
173
173
  }
174
174
 
@@ -184,7 +184,7 @@ class Config {
184
184
  if (configJSON.extends !== 'lighthouse:default') {
185
185
  throw new Error('`lighthouse:default` is the only valid extension method.');
186
186
  }
187
- configJSON = Config.extendConfigJSON(deepCloneConfigJson(defaultConfig), configJSON);
187
+ configJSON = Config.extendConfigJSON(deepCloneConfigJson(legacyDefaultConfig), configJSON);
188
188
  }
189
189
 
190
190
  // The directory of the config path, if one was provided.
@@ -123,56 +123,160 @@ const UIStrings = {
123
123
 
124
124
  const str_ = i18n.createMessageInstanceIdFn(import.meta.url, UIStrings);
125
125
 
126
+ // Ensure all artifact IDs match the typedefs.
127
+ /** @type {Record<keyof LH.FRArtifacts, string>} */
128
+ const artifacts = {
129
+ DevtoolsLog: '',
130
+ Trace: '',
131
+ Accessibility: '',
132
+ AnchorElements: '',
133
+ CacheContents: '',
134
+ ConsoleMessages: '',
135
+ CSSUsage: '',
136
+ Doctype: '',
137
+ DOMStats: '',
138
+ EmbeddedContent: '',
139
+ FontSize: '',
140
+ Inputs: '',
141
+ FullPageScreenshot: '',
142
+ GlobalListeners: '',
143
+ IFrameElements: '',
144
+ ImageElements: '',
145
+ InstallabilityErrors: '',
146
+ InspectorIssues: '',
147
+ JsUsage: '',
148
+ LinkElements: '',
149
+ MainDocumentContent: '',
150
+ MetaElements: '',
151
+ NetworkUserAgent: '',
152
+ OptimizedImages: '',
153
+ PasswordInputsWithPreventedPaste: '',
154
+ ResponseCompression: '',
155
+ RobotsTxt: '',
156
+ ServiceWorker: '',
157
+ ScriptElements: '',
158
+ Scripts: '',
159
+ SourceMaps: '',
160
+ Stacks: '',
161
+ TagsBlockingFirstPaint: '',
162
+ TapTargets: '',
163
+ TraceElements: '',
164
+ ViewportDimensions: '',
165
+ WebAppManifest: '',
166
+ devtoolsLogs: '',
167
+ traces: '',
168
+ };
169
+
170
+ for (const key of Object.keys(artifacts)) {
171
+ artifacts[/** @type {keyof typeof artifacts} */ (key)] = key;
172
+ }
173
+
126
174
  /** @type {LH.Config.Json} */
127
175
  const defaultConfig = {
128
176
  settings: constants.defaultSettings,
129
- passes: [{
130
- passName: 'defaultPass',
131
- recordTrace: true,
132
- useThrottling: true,
133
- pauseAfterFcpMs: 1000,
134
- pauseAfterLoadMs: 1000,
135
- networkQuietThresholdMs: 1000,
136
- cpuQuietThresholdMs: 1000,
137
- gatherers: [
138
- 'css-usage',
139
- 'js-usage',
140
- 'viewport-dimensions',
141
- 'console-messages',
142
- 'anchor-elements',
143
- 'image-elements',
144
- 'link-elements',
145
- 'meta-elements',
146
- 'script-elements',
147
- 'scripts',
148
- 'iframe-elements',
149
- 'inputs',
150
- 'main-document-content',
151
- 'global-listeners',
152
- 'dobetterweb/doctype',
153
- 'dobetterweb/domstats',
154
- 'dobetterweb/optimized-images',
155
- 'dobetterweb/password-inputs-with-prevented-paste',
156
- 'dobetterweb/response-compression',
157
- 'dobetterweb/tags-blocking-first-paint',
158
- 'seo/font-size',
159
- 'seo/embedded-content',
160
- 'seo/robots-txt',
161
- 'seo/tap-targets',
162
- 'accessibility',
163
- 'trace-elements',
164
- 'inspector-issues',
165
- 'source-maps',
166
- 'full-page-screenshot',
167
- ],
168
- },
169
- {
170
- passName: 'offlinePass',
171
- loadFailureMode: 'ignore',
172
- gatherers: [
173
- 'service-worker',
174
- ],
175
- }],
177
+ artifacts: [
178
+ // Artifacts which can be depended on come first.
179
+ {id: artifacts.DevtoolsLog, gatherer: 'devtools-log'},
180
+ {id: artifacts.Trace, gatherer: 'trace'},
181
+
182
+ {id: artifacts.Accessibility, gatherer: 'accessibility'},
183
+ {id: artifacts.AnchorElements, gatherer: 'anchor-elements'},
184
+ {id: artifacts.CacheContents, gatherer: 'cache-contents'},
185
+ {id: artifacts.ConsoleMessages, gatherer: 'console-messages'},
186
+ {id: artifacts.CSSUsage, gatherer: 'css-usage'},
187
+ {id: artifacts.Doctype, gatherer: 'dobetterweb/doctype'},
188
+ {id: artifacts.DOMStats, gatherer: 'dobetterweb/domstats'},
189
+ {id: artifacts.EmbeddedContent, gatherer: 'seo/embedded-content'},
190
+ {id: artifacts.FontSize, gatherer: 'seo/font-size'},
191
+ {id: artifacts.Inputs, gatherer: 'inputs'},
192
+ {id: artifacts.GlobalListeners, gatherer: 'global-listeners'},
193
+ {id: artifacts.IFrameElements, gatherer: 'iframe-elements'},
194
+ {id: artifacts.ImageElements, gatherer: 'image-elements'},
195
+ {id: artifacts.InstallabilityErrors, gatherer: 'installability-errors'},
196
+ {id: artifacts.InspectorIssues, gatherer: 'inspector-issues'},
197
+ {id: artifacts.JsUsage, gatherer: 'js-usage'},
198
+ {id: artifacts.LinkElements, gatherer: 'link-elements'},
199
+ {id: artifacts.MainDocumentContent, gatherer: 'main-document-content'},
200
+ {id: artifacts.MetaElements, gatherer: 'meta-elements'},
201
+ {id: artifacts.NetworkUserAgent, gatherer: 'network-user-agent'},
202
+ {id: artifacts.OptimizedImages, gatherer: 'dobetterweb/optimized-images'},
203
+ {id: artifacts.PasswordInputsWithPreventedPaste, gatherer: 'dobetterweb/password-inputs-with-prevented-paste'},
204
+ {id: artifacts.ResponseCompression, gatherer: 'dobetterweb/response-compression'},
205
+ {id: artifacts.RobotsTxt, gatherer: 'seo/robots-txt'},
206
+ {id: artifacts.ServiceWorker, gatherer: 'service-worker'},
207
+ {id: artifacts.ScriptElements, gatherer: 'script-elements'},
208
+ {id: artifacts.Scripts, gatherer: 'scripts'},
209
+ {id: artifacts.SourceMaps, gatherer: 'source-maps'},
210
+ {id: artifacts.Stacks, gatherer: 'stacks'},
211
+ {id: artifacts.TagsBlockingFirstPaint, gatherer: 'dobetterweb/tags-blocking-first-paint'},
212
+ {id: artifacts.TapTargets, gatherer: 'seo/tap-targets'},
213
+ {id: artifacts.TraceElements, gatherer: 'trace-elements'},
214
+ {id: artifacts.ViewportDimensions, gatherer: 'viewport-dimensions'},
215
+ {id: artifacts.WebAppManifest, gatherer: 'web-app-manifest'},
216
+
217
+ // Artifact copies are renamed for compatibility with legacy artifacts.
218
+ {id: artifacts.devtoolsLogs, gatherer: 'devtools-log-compat'},
219
+ {id: artifacts.traces, gatherer: 'trace-compat'},
220
+
221
+ // FullPageScreenshot comes at the very end so all other node analysis is captured.
222
+ {id: artifacts.FullPageScreenshot, gatherer: 'full-page-screenshot'},
223
+ ],
224
+ navigations: [
225
+ {
226
+ id: 'default',
227
+ pauseAfterFcpMs: 1000,
228
+ pauseAfterLoadMs: 1000,
229
+ networkQuietThresholdMs: 1000,
230
+ cpuQuietThresholdMs: 1000,
231
+ artifacts: [
232
+ // Artifacts which can be depended on come first.
233
+ artifacts.DevtoolsLog,
234
+ artifacts.Trace,
235
+
236
+ artifacts.Accessibility,
237
+ artifacts.AnchorElements,
238
+ artifacts.CacheContents,
239
+ artifacts.ConsoleMessages,
240
+ artifacts.CSSUsage,
241
+ artifacts.Doctype,
242
+ artifacts.DOMStats,
243
+ artifacts.EmbeddedContent,
244
+ artifacts.FontSize,
245
+ artifacts.Inputs,
246
+ artifacts.GlobalListeners,
247
+ artifacts.IFrameElements,
248
+ artifacts.ImageElements,
249
+ artifacts.InstallabilityErrors,
250
+ artifacts.InspectorIssues,
251
+ artifacts.JsUsage,
252
+ artifacts.LinkElements,
253
+ artifacts.MainDocumentContent,
254
+ artifacts.MetaElements,
255
+ artifacts.NetworkUserAgent,
256
+ artifacts.OptimizedImages,
257
+ artifacts.PasswordInputsWithPreventedPaste,
258
+ artifacts.ResponseCompression,
259
+ artifacts.RobotsTxt,
260
+ artifacts.ServiceWorker,
261
+ artifacts.ScriptElements,
262
+ artifacts.Scripts,
263
+ artifacts.SourceMaps,
264
+ artifacts.Stacks,
265
+ artifacts.TagsBlockingFirstPaint,
266
+ artifacts.TapTargets,
267
+ artifacts.TraceElements,
268
+ artifacts.ViewportDimensions,
269
+ artifacts.WebAppManifest,
270
+
271
+ // Compat artifacts come last.
272
+ artifacts.devtoolsLogs,
273
+ artifacts.traces,
274
+
275
+ // FullPageScreenshot comes at the very end so all other node analysis is captured.
276
+ artifacts.FullPageScreenshot,
277
+ ],
278
+ },
279
+ ],
176
280
  audits: [
177
281
  'is-on-https',
178
282
  'service-worker',
@@ -186,6 +290,7 @@ const defaultConfig = {
186
290
  'metrics/total-blocking-time',
187
291
  'metrics/max-potential-fid',
188
292
  'metrics/cumulative-layout-shift',
293
+ 'metrics/experimental-interaction-to-next-paint',
189
294
  'errors-in-console',
190
295
  'server-response-time',
191
296
  'metrics/interactive',
@@ -302,6 +407,7 @@ const defaultConfig = {
302
407
  'byte-efficiency/efficient-animated-content',
303
408
  'byte-efficiency/duplicated-javascript',
304
409
  'byte-efficiency/legacy-javascript',
410
+ 'byte-efficiency/uses-responsive-images-snapshot',
305
411
  'dobetterweb/doctype',
306
412
  'dobetterweb/charset',
307
413
  'dobetterweb/dom-size',
@@ -326,8 +432,8 @@ const defaultConfig = {
326
432
  'seo/plugins',
327
433
  'seo/canonical',
328
434
  'seo/manual/structured-data',
435
+ 'work-during-interaction',
329
436
  ],
330
-
331
437
  groups: {
332
438
  'metrics': {
333
439
  title: str_(UIStrings.metricGroupTitle),
@@ -420,6 +526,7 @@ const defaultConfig = {
420
526
  {id: 'total-blocking-time', weight: 30, group: 'metrics', acronym: 'TBT', relevantAudits: metricsToAudits.tbtRelevantAudits},
421
527
  {id: 'largest-contentful-paint', weight: 25, group: 'metrics', acronym: 'LCP', relevantAudits: metricsToAudits.lcpRelevantAudits},
422
528
  {id: 'cumulative-layout-shift', weight: 15, group: 'metrics', acronym: 'CLS', relevantAudits: metricsToAudits.clsRelevantAudits},
529
+ {id: 'experimental-interaction-to-next-paint', weight: 0, group: 'metrics', acronym: 'INP', relevantAudits: metricsToAudits.inpRelevantAudits},
423
530
 
424
531
  // These are our "invisible" metrics. Not displayed, but still in the LHR.
425
532
  {id: 'max-potential-fid', weight: 0, group: 'hidden'},
@@ -466,6 +573,8 @@ const defaultConfig = {
466
573
  {id: 'unsized-images', weight: 0},
467
574
  {id: 'viewport', weight: 0},
468
575
  {id: 'no-unload-listeners', weight: 0},
576
+ {id: 'uses-responsive-images-snapshot', weight: 0},
577
+ {id: 'work-during-interaction', weight: 0},
469
578
 
470
579
  // Budget audits.
471
580
  {id: 'performance-budget', weight: 0, group: 'budgets'},
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @license Copyright 2018 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 Construct the legacy default config from the standard default config.
10
+ */
11
+
12
+ import defaultConfig from './default-config.js';
13
+
14
+ /** @type {LH.Config.Json} */
15
+ const legacyDefaultConfig = JSON.parse(JSON.stringify(defaultConfig));
16
+ if (!legacyDefaultConfig.categories) {
17
+ throw new Error('Default config should always have categories');
18
+ }
19
+
20
+ // These properties are ignored in Legacy navigations.
21
+ delete legacyDefaultConfig.artifacts;
22
+ delete legacyDefaultConfig.navigations;
23
+
24
+ // These audits don't work in Legacy navigation mode so we remove them.
25
+ const unsupportedAuditIds = [
26
+ 'experimental-interaction-to-next-paint',
27
+ 'uses-responsive-images-snapshot',
28
+ 'work-during-interaction',
29
+ ];
30
+
31
+ legacyDefaultConfig.audits = legacyDefaultConfig.audits?.filter(audit =>
32
+ !unsupportedAuditIds.find(auditId => audit.toString().endsWith(auditId)));
33
+
34
+ legacyDefaultConfig.categories['performance'].auditRefs =
35
+ legacyDefaultConfig.categories['performance'].auditRefs.filter(auditRef =>
36
+ !unsupportedAuditIds.includes(auditRef.id));
37
+
38
+ legacyDefaultConfig.passes = [{
39
+ passName: 'defaultPass',
40
+ recordTrace: true,
41
+ useThrottling: true,
42
+ pauseAfterFcpMs: 1000,
43
+ pauseAfterLoadMs: 1000,
44
+ networkQuietThresholdMs: 1000,
45
+ cpuQuietThresholdMs: 1000,
46
+ gatherers: [
47
+ 'css-usage',
48
+ 'js-usage',
49
+ 'viewport-dimensions',
50
+ 'console-messages',
51
+ 'anchor-elements',
52
+ 'image-elements',
53
+ 'link-elements',
54
+ 'meta-elements',
55
+ 'script-elements',
56
+ 'scripts',
57
+ 'iframe-elements',
58
+ 'inputs',
59
+ 'main-document-content',
60
+ 'global-listeners',
61
+ 'dobetterweb/doctype',
62
+ 'dobetterweb/domstats',
63
+ 'dobetterweb/optimized-images',
64
+ 'dobetterweb/password-inputs-with-prevented-paste',
65
+ 'dobetterweb/response-compression',
66
+ 'dobetterweb/tags-blocking-first-paint',
67
+ 'seo/font-size',
68
+ 'seo/embedded-content',
69
+ 'seo/robots-txt',
70
+ 'seo/tap-targets',
71
+ 'accessibility',
72
+ 'trace-elements',
73
+ 'inspector-issues',
74
+ 'source-maps',
75
+ 'full-page-screenshot',
76
+ ],
77
+ },
78
+ {
79
+ passName: 'offlinePass',
80
+ loadFailureMode: 'ignore',
81
+ gatherers: [
82
+ 'service-worker',
83
+ ],
84
+ }];
85
+
86
+ export default legacyDefaultConfig;
@@ -8,7 +8,7 @@
8
8
  import path from 'path';
9
9
  import log from 'lighthouse-logger';
10
10
  import {Runner} from '../../runner.js';
11
- import defaultConfig from './default-config.js';
11
+ import defaultConfig from '../../config/default-config.js';
12
12
  import {defaultNavigationConfig, nonSimulatedPassConfigOverrides} from '../../config/constants.js'; // eslint-disable-line max-len
13
13
 
14
14
  import {
@@ -35,7 +35,10 @@ import {
35
35
  import {getModuleDirectory} from '../../../esm-utils.js';
36
36
  import * as format from '../../../shared/localization/format.js';
37
37
 
38
- const defaultConfigPath = path.join(getModuleDirectory(import.meta), './default-config.js');
38
+ const defaultConfigPath = path.join(
39
+ getModuleDirectory(import.meta),
40
+ '../../config/default-config.js'
41
+ );
39
42
 
40
43
  /** @typedef {LH.Config.FRContext & {gatherMode: LH.Gatherer.GatherMode}} ConfigContext */
41
44
 
@@ -8,6 +8,7 @@
8
8
  /** @typedef {import('../../../shared/localization/locales').LhlMessages} LhlMessages */
9
9
 
10
10
  import path from 'path';
11
+ import url from 'url';
11
12
 
12
13
  import lookupClosestLocale from 'lookup-closest-locale';
13
14
  import {getAvailableLocales} from '../../../shared/localization/format.js';
@@ -170,7 +171,7 @@ function lookupLocale(locales, possibleLocales) {
170
171
  * @param {Record<string, string>} fileStrings
171
172
  */
172
173
  function createIcuMessageFn(filename, fileStrings) {
173
- filename = filename.replace('file://', '');
174
+ if (filename.startsWith('file://')) filename = url.fileURLToPath(filename);
174
175
 
175
176
  /**
176
177
  * Combined so fn can access both caller's strings and i18n.UIStrings shared across LH.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "9.5.0-dev.20220717",
4
+ "version": "9.5.0-dev.20220720",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./lighthouse-core/index.js",
7
7
  "bin": {
@@ -36,7 +36,7 @@
36
36
  "smoke": "node lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js",
37
37
  "debug": "node --inspect-brk ./lighthouse-cli/index.js",
38
38
  "start": "yarn build-report --standalone && node ./lighthouse-cli/index.js",
39
- "mocha": "node lighthouse-core/test/scripts/run-mocha-tests.js",
39
+ "mocha": "node --loader=testdouble lighthouse-core/test/scripts/run-mocha-tests.js",
40
40
  "test": "yarn diff:sample-json && yarn lint --quiet && yarn unit && yarn type-check",
41
41
  "test-bundle": "yarn smoke --runner bundle --retries=2",
42
42
  "test-clients": "yarn mocha --testMatch clients/**/*-test.js && yarn mocha --testMatch clients/**/*-test-pptr.js",
@@ -194,10 +194,10 @@
194
194
  "enquirer": "^2.3.6",
195
195
  "http-link-header": "^0.8.0",
196
196
  "intl-messageformat": "^4.4.0",
197
- "jpeg-js": "^0.4.3",
197
+ "jpeg-js": "^0.4.4",
198
198
  "js-library-detector": "^6.5.0",
199
199
  "lighthouse-logger": "^1.3.0",
200
- "lighthouse-stack-packs": "^1.8.1",
200
+ "lighthouse-stack-packs": "1.8.2",
201
201
  "lodash": "^4.17.21",
202
202
  "lookup-closest-locale": "6.2.0",
203
203
  "metaviewport-parser": "0.2.0",
@@ -2406,7 +2406,7 @@
2406
2406
  "message": "If you are using React Router, minimize usage of the `<Redirect>` component for [route navigations](https://reacttraining.com/react-router/web/api/Redirect)."
2407
2407
  },
2408
2408
  "node_modules/lighthouse-stack-packs/packs/react.js | server-response-time": {
2409
- "message": "If you are server-side rendering any React components, consider using `renderToNodeStream()` or `renderToStaticNodeStream()` to allow the client to receive and hydrate different parts of the markup instead of all at once. [Learn more](https://reactjs.org/docs/react-dom-server.html#rendertonodestream)."
2409
+ "message": "If you are server-side rendering any React components, consider using `renderToPipeableStream()` or `renderToStaticNodeStream()` to allow the client to receive and hydrate different parts of the markup instead of all at once. [Learn more](https://reactjs.org/docs/react-dom-server.html#renderToPipeableStream)."
2410
2410
  },
2411
2411
  "node_modules/lighthouse-stack-packs/packs/react.js | unminified-css": {
2412
2412
  "message": "If your build system minifies CSS files automatically, ensure that you are deploying the production build of your application. You can check this with the React Developer Tools extension. [Learn more](https://reactjs.org/docs/optimizing-performance.html#use-the-production-build)."
@@ -2424,7 +2424,7 @@
2424
2424
  "message": "Consider uploading your GIF to a service which will make it available to embed as an HTML5 video."
2425
2425
  },
2426
2426
  "node_modules/lighthouse-stack-packs/packs/wordpress.js | modern-image-formats": {
2427
- "message": "Consider using a [plugin](https://wordpress.org/plugins/search/convert+webp/) or service that will automatically convert your uploaded images to the optimal formats."
2427
+ "message": "Consider using the [Performance Lab](https://wordpress.org/plugins/performance-lab/) plugin to automatically convert your uploaded JPEG images into WebP, wherever supported."
2428
2428
  },
2429
2429
  "node_modules/lighthouse-stack-packs/packs/wordpress.js | offscreen-images": {
2430
2430
  "message": "Install a [lazy-load WordPress plugin](https://wordpress.org/plugins/search/lazy+load/) that provides the ability to defer any offscreen images, or switch to a theme that provides that functionality. Also consider using [the AMP plugin](https://wordpress.org/plugins/amp/)."
@@ -2406,7 +2406,7 @@
2406
2406
  "message": "Îf́ ŷóû ár̂é ûśîńĝ Ŕêáĉt́ R̂óût́êŕ, m̂ín̂ím̂íẑé ûśâǵê óf̂ t́ĥé `<Redirect>` ĉóm̂ṕôńêńt̂ f́ôŕ [r̂óût́ê ńâv́îǵât́îón̂ś](https://reacttraining.com/react-router/web/api/Redirect)."
2407
2407
  },
2408
2408
  "node_modules/lighthouse-stack-packs/packs/react.js | server-response-time": {
2409
- "message": "Îf́ ŷóû ár̂é ŝér̂v́êŕ-ŝíd̂é r̂én̂d́êŕîńĝ án̂ý R̂éâćt̂ ćôḿp̂ón̂én̂t́ŝ, ćôńŝíd̂ér̂ úŝín̂ǵ `renderToNodeStream()` ôŕ `renderToStaticNodeStream()` t̂ó âĺl̂óŵ t́ĥé ĉĺîén̂t́ t̂ó r̂éĉéîv́ê án̂d́ ĥýd̂ŕât́ê d́îf́f̂ér̂én̂t́ p̂ár̂t́ŝ óf̂ t́ĥé m̂ár̂ḱûṕ îńŝt́êád̂ óf̂ ál̂ĺ ât́ ôńĉé. [L̂éâŕn̂ ḿôŕê](https://reactjs.org/docs/react-dom-server.html#rendertonodestream)."
2409
+ "message": "Îf́ ŷóû ár̂é ŝér̂v́êŕ-ŝíd̂é r̂én̂d́êŕîńĝ án̂ý R̂éâćt̂ ćôḿp̂ón̂én̂t́ŝ, ćôńŝíd̂ér̂ úŝín̂ǵ `renderToPipeableStream()` ôŕ `renderToStaticNodeStream()` t̂ó âĺl̂óŵ t́ĥé ĉĺîén̂t́ t̂ó r̂éĉéîv́ê án̂d́ ĥýd̂ŕât́ê d́îf́f̂ér̂én̂t́ p̂ár̂t́ŝ óf̂ t́ĥé m̂ár̂ḱûṕ îńŝt́êád̂ óf̂ ál̂ĺ ât́ ôńĉé. [L̂éâŕn̂ ḿôŕê](https://reactjs.org/docs/react-dom-server.html#renderToPipeableStream)."
2410
2410
  },
2411
2411
  "node_modules/lighthouse-stack-packs/packs/react.js | unminified-css": {
2412
2412
  "message": "Îf́ ŷóûŕ b̂úîĺd̂ śŷśt̂ém̂ ḿîńîf́îéŝ ĆŜŚ f̂íl̂éŝ áût́ôḿât́îćâĺl̂ý, êńŝúr̂é t̂h́ât́ ŷóû ár̂é d̂ép̂ĺôýîńĝ t́ĥé p̂ŕôd́ûćt̂íôń b̂úîĺd̂ óf̂ ýôúr̂ áp̂ṕl̂íĉát̂íôń. Ŷóû ćâń ĉh́êćk̂ t́ĥíŝ ẃît́ĥ t́ĥé R̂éâćt̂ D́êv́êĺôṕêŕ T̂óôĺŝ éx̂t́êńŝíôń. [L̂éâŕn̂ ḿôŕê](https://reactjs.org/docs/optimizing-performance.html#use-the-production-build)."
@@ -2424,7 +2424,7 @@
2424
2424
  "message": "Ĉón̂śîd́êŕ ûṕl̂óâd́îńĝ ýôúr̂ ǴÎF́ t̂ó â śêŕv̂íĉé ŵh́îćĥ ẃîĺl̂ ḿâḱê ít̂ áv̂áîĺâb́l̂é t̂ó êḿb̂éd̂ áŝ án̂ H́T̂ḾL̂5 v́îd́êó."
2425
2425
  },
2426
2426
  "node_modules/lighthouse-stack-packs/packs/wordpress.js | modern-image-formats": {
2427
- "message": "Ĉón̂śîd́êŕ ûśîńĝ á [p̂ĺûǵîń](https://wordpress.org/plugins/search/convert+webp/) ôŕ ŝér̂v́îćê ĥát̂ ẃîĺl̂ áût́ôḿât́îćâĺl̂ý ĉón̂v́êŕt̂ ýôú úp̂ĺôád̂ém̂áĝéŝ t́ô t́é ôṕt̂ím̂ál̂ f́ôŕm̂át̂ś."
2427
+ "message": "Ĉón̂śîd́êŕ ûśîńĝ t́ĥé [P̂ér̂f́ôŕm̂án̂ćê Ĺâb́](https://wordpress.org/plugins/performance-lab/) p̂ĺûǵîń t̂ó ât̂óm̂át̂íĉál̂ĺŷ ćôńv̂ér̂t́ ŷóûŕ ûṕl̂âd́êd́ ĴṔÊǴ îḿâǵêś îńt̂ó Ŵéb̂Ṕ, ŵh́êŕêv́êŕ ŝúp̂ṕôŕt̂éd̂."
2428
2428
  },
2429
2429
  "node_modules/lighthouse-stack-packs/packs/wordpress.js | offscreen-images": {
2430
2430
  "message": "Îńŝt́âĺl̂ á [l̂áẑý-l̂óâd́ Ŵór̂d́P̂ŕêśŝ ṕl̂úĝín̂](https://wordpress.org/plugins/search/lazy+load/) t́ĥát̂ ṕr̂óv̂íd̂éŝ t́ĥé âb́îĺît́ŷ t́ô d́êf́êŕ âńŷ óf̂f́ŝćr̂éêń îḿâǵêś, ôŕ ŝẃît́ĉh́ t̂ó â t́ĥém̂é t̂h́ât́ p̂ŕôv́îd́êś t̂h́ât́ f̂ún̂ćt̂íôńâĺît́ŷ. Ál̂śô ćôńŝíd̂ér̂ úŝín̂ǵ [t̂h́ê ÁM̂Ṕ p̂ĺûǵîń](https://wordpress.org/plugins/amp/)."
@@ -1,208 +0,0 @@
1
- /**
2
- * @license Copyright 2020 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
- import legacyDefaultConfig from '../../config/default-config.js';
9
- import {deepClone} from '../../config/config-helpers.js';
10
- import {metricsToAudits} from '../../config/metrics-to-audits.js';
11
-
12
- /** @type {LH.Config.AuditJson[]} */
13
- const frAudits = [
14
- 'byte-efficiency/uses-responsive-images-snapshot',
15
- 'metrics/experimental-interaction-to-next-paint',
16
- 'work-during-interaction',
17
- ];
18
-
19
- /** @type {Record<string, LH.Config.AuditRefJson[]>} */
20
- const frCategoryAuditRefExtensions = {
21
- 'performance': [
22
- {id: 'uses-responsive-images-snapshot', weight: 0},
23
- {id: 'experimental-interaction-to-next-paint', weight: 0, group: 'metrics', acronym: 'INP',
24
- relevantAudits: metricsToAudits.inpRelevantAudits},
25
- {id: 'work-during-interaction', weight: 0},
26
- ],
27
- };
28
-
29
- /** @return {LH.Config.Json['categories']} */
30
- function mergeCategories() {
31
- if (!legacyDefaultConfig.categories) return {};
32
- // Don't modify original default config.
33
- const categories = deepClone(legacyDefaultConfig.categories);
34
- for (const key of Object.keys(frCategoryAuditRefExtensions)) {
35
- if (!categories[key]) continue;
36
- categories[key].auditRefs.push(...frCategoryAuditRefExtensions[key]);
37
- }
38
- return categories;
39
- }
40
-
41
- // Ensure all artifact IDs match the typedefs.
42
- /** @type {Record<keyof LH.FRArtifacts, string>} */
43
- const artifacts = {
44
- DevtoolsLog: '',
45
- Trace: '',
46
- Accessibility: '',
47
- AnchorElements: '',
48
- CacheContents: '',
49
- ConsoleMessages: '',
50
- CSSUsage: '',
51
- Doctype: '',
52
- DOMStats: '',
53
- EmbeddedContent: '',
54
- FontSize: '',
55
- Inputs: '',
56
- FullPageScreenshot: '',
57
- GlobalListeners: '',
58
- IFrameElements: '',
59
- ImageElements: '',
60
- InstallabilityErrors: '',
61
- InspectorIssues: '',
62
- JsUsage: '',
63
- LinkElements: '',
64
- MainDocumentContent: '',
65
- MetaElements: '',
66
- NetworkUserAgent: '',
67
- OptimizedImages: '',
68
- PasswordInputsWithPreventedPaste: '',
69
- ResponseCompression: '',
70
- RobotsTxt: '',
71
- ServiceWorker: '',
72
- ScriptElements: '',
73
- Scripts: '',
74
- SourceMaps: '',
75
- Stacks: '',
76
- TagsBlockingFirstPaint: '',
77
- TapTargets: '',
78
- TraceElements: '',
79
- ViewportDimensions: '',
80
- WebAppManifest: '',
81
- devtoolsLogs: '',
82
- traces: '',
83
- };
84
-
85
- for (const key of Object.keys(artifacts)) {
86
- artifacts[/** @type {keyof typeof artifacts} */ (key)] = key;
87
- }
88
-
89
- /** @type {LH.Config.Json} */
90
- const defaultConfig = {
91
- artifacts: [
92
- // Artifacts which can be depended on come first.
93
- {id: artifacts.DevtoolsLog, gatherer: 'devtools-log'},
94
- {id: artifacts.Trace, gatherer: 'trace'},
95
-
96
- /* eslint-disable max-len */
97
- {id: artifacts.Accessibility, gatherer: 'accessibility'},
98
- {id: artifacts.AnchorElements, gatherer: 'anchor-elements'},
99
- {id: artifacts.CacheContents, gatherer: 'cache-contents'},
100
- {id: artifacts.ConsoleMessages, gatherer: 'console-messages'},
101
- {id: artifacts.CSSUsage, gatherer: 'css-usage'},
102
- {id: artifacts.Doctype, gatherer: 'dobetterweb/doctype'},
103
- {id: artifacts.DOMStats, gatherer: 'dobetterweb/domstats'},
104
- {id: artifacts.EmbeddedContent, gatherer: 'seo/embedded-content'},
105
- {id: artifacts.FontSize, gatherer: 'seo/font-size'},
106
- {id: artifacts.Inputs, gatherer: 'inputs'},
107
- {id: artifacts.GlobalListeners, gatherer: 'global-listeners'},
108
- {id: artifacts.IFrameElements, gatherer: 'iframe-elements'},
109
- {id: artifacts.ImageElements, gatherer: 'image-elements'},
110
- {id: artifacts.InstallabilityErrors, gatherer: 'installability-errors'},
111
- {id: artifacts.InspectorIssues, gatherer: 'inspector-issues'},
112
- {id: artifacts.JsUsage, gatherer: 'js-usage'},
113
- {id: artifacts.LinkElements, gatherer: 'link-elements'},
114
- {id: artifacts.MainDocumentContent, gatherer: 'main-document-content'},
115
- {id: artifacts.MetaElements, gatherer: 'meta-elements'},
116
- {id: artifacts.NetworkUserAgent, gatherer: 'network-user-agent'},
117
- {id: artifacts.OptimizedImages, gatherer: 'dobetterweb/optimized-images'},
118
- {id: artifacts.PasswordInputsWithPreventedPaste, gatherer: 'dobetterweb/password-inputs-with-prevented-paste'},
119
- {id: artifacts.ResponseCompression, gatherer: 'dobetterweb/response-compression'},
120
- {id: artifacts.RobotsTxt, gatherer: 'seo/robots-txt'},
121
- {id: artifacts.ServiceWorker, gatherer: 'service-worker'},
122
- {id: artifacts.ScriptElements, gatherer: 'script-elements'},
123
- {id: artifacts.Scripts, gatherer: 'scripts'},
124
- {id: artifacts.SourceMaps, gatherer: 'source-maps'},
125
- {id: artifacts.Stacks, gatherer: 'stacks'},
126
- {id: artifacts.TagsBlockingFirstPaint, gatherer: 'dobetterweb/tags-blocking-first-paint'},
127
- {id: artifacts.TapTargets, gatherer: 'seo/tap-targets'},
128
- {id: artifacts.TraceElements, gatherer: 'trace-elements'},
129
- {id: artifacts.ViewportDimensions, gatherer: 'viewport-dimensions'},
130
- {id: artifacts.WebAppManifest, gatherer: 'web-app-manifest'},
131
- /* eslint-enable max-len */
132
-
133
- // Artifact copies are renamed for compatibility with legacy artifacts.
134
- {id: artifacts.devtoolsLogs, gatherer: 'devtools-log-compat'},
135
- {id: artifacts.traces, gatherer: 'trace-compat'},
136
-
137
- // FullPageScreenshot comes at the very end so all other node analysis is captured.
138
- {id: artifacts.FullPageScreenshot, gatherer: 'full-page-screenshot'},
139
- ],
140
- navigations: [
141
- {
142
- id: 'default',
143
- pauseAfterFcpMs: 1000,
144
- pauseAfterLoadMs: 1000,
145
- networkQuietThresholdMs: 1000,
146
- cpuQuietThresholdMs: 1000,
147
- artifacts: [
148
- // Artifacts which can be depended on come first.
149
- artifacts.DevtoolsLog,
150
- artifacts.Trace,
151
-
152
- artifacts.Accessibility,
153
- artifacts.AnchorElements,
154
- artifacts.CacheContents,
155
- artifacts.ConsoleMessages,
156
- artifacts.CSSUsage,
157
- artifacts.Doctype,
158
- artifacts.DOMStats,
159
- artifacts.EmbeddedContent,
160
- artifacts.FontSize,
161
- artifacts.Inputs,
162
- artifacts.GlobalListeners,
163
- artifacts.IFrameElements,
164
- artifacts.ImageElements,
165
- artifacts.InstallabilityErrors,
166
- artifacts.InspectorIssues,
167
- artifacts.JsUsage,
168
- artifacts.LinkElements,
169
- artifacts.MainDocumentContent,
170
- artifacts.MetaElements,
171
- artifacts.NetworkUserAgent,
172
- artifacts.OptimizedImages,
173
- artifacts.PasswordInputsWithPreventedPaste,
174
- artifacts.ResponseCompression,
175
- artifacts.RobotsTxt,
176
- artifacts.ServiceWorker,
177
- artifacts.ScriptElements,
178
- artifacts.Scripts,
179
- artifacts.SourceMaps,
180
- artifacts.Stacks,
181
- artifacts.TagsBlockingFirstPaint,
182
- artifacts.TapTargets,
183
- artifacts.TraceElements,
184
- artifacts.ViewportDimensions,
185
- artifacts.WebAppManifest,
186
-
187
- // Compat artifacts come last.
188
- artifacts.devtoolsLogs,
189
- artifacts.traces,
190
-
191
- // FullPageScreenshot comes at the very end so all other node analysis is captured.
192
- artifacts.FullPageScreenshot,
193
- ],
194
- },
195
- ],
196
- settings: legacyDefaultConfig.settings,
197
- audits: [
198
- ...(legacyDefaultConfig.audits || []).map(audit => {
199
- if (typeof audit === 'string') return {path: audit};
200
- return audit;
201
- }),
202
- ...frAudits,
203
- ],
204
- categories: mergeCategories(),
205
- groups: legacyDefaultConfig.groups,
206
- };
207
-
208
- export default defaultConfig;