lighthouse 11.7.0-dev.20240408 → 11.7.0-dev.20240409

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.
@@ -20,6 +20,7 @@ const exclusions = {
20
20
  'redirects-client-paint-server', 'redirects-multiple-server',
21
21
  'redirects-single-server', 'redirects-single-client',
22
22
  'redirects-history-push-state', 'redirects-scripts',
23
+ 'redirects-http',
23
24
  // Disabled because these tests use settings that cannot be fully configured in
24
25
  // DevTools (e.g. throttling method "provided").
25
26
  'metrics-tricky-tti', 'metrics-tricky-tti-late-fcp', 'screenshot',
@@ -13,6 +13,7 @@ import dbw from './test-definitions/dobetterweb.js';
13
13
  import errorsExpiredSsl from './test-definitions/errors-expired-ssl.js';
14
14
  import errorsIframeExpiredSsl from './test-definitions/errors-iframe-expired-ssl.js';
15
15
  import errorsInfiniteLoop from './test-definitions/errors-infinite-loop.js';
16
+ import fontSize from './test-definitions/font-size.js';
16
17
  import formsAutoComplete from './test-definitions/forms-autocomplete.js';
17
18
  import fpsMax from './test-definitions/fps-max.js';
18
19
  import fpsMaxPassive from './test-definitions/fps-max-passive.js';
@@ -52,6 +53,7 @@ import pwaRocks from './test-definitions/pwa-rocks.js';
52
53
  import pwaSvgomg from './test-definitions/pwa-svgomg.js';
53
54
  import redirectsClientPaintServer from './test-definitions/redirects-client-paint-server.js';
54
55
  import redirectsHistoryPushState from './test-definitions/redirects-history-push-state.js';
56
+ import redirectsHttp from './test-definitions/redirects-http.js';
55
57
  import redirectsMultipleServer from './test-definitions/redirects-multiple-server.js';
56
58
  import redirectsScripts from './test-definitions/redirects-scripts.js';
57
59
  import redirectsSelf from './test-definitions/redirects-self.js';
@@ -77,6 +79,7 @@ const smokeTests = [
77
79
  errorsExpiredSsl,
78
80
  errorsIframeExpiredSsl,
79
81
  errorsInfiniteLoop,
82
+ fontSize,
80
83
  formsAutoComplete,
81
84
  fpsMax,
82
85
  fpsMaxPassive,
@@ -116,6 +119,7 @@ const smokeTests = [
116
119
  pwaSvgomg,
117
120
  redirectsClientPaintServer,
118
121
  redirectsHistoryPushState,
122
+ redirectsHttp,
119
123
  redirectsMultipleServer,
120
124
  redirectsScripts,
121
125
  redirectsSelf,
@@ -0,0 +1,19 @@
1
+ export default RedirectsHTTP;
2
+ /**
3
+ * An audit for checking if a site starting on http redirects to https. The audit
4
+ * is marked not applicable if the requestedUrl is already https.
5
+ */
6
+ declare class RedirectsHTTP extends Audit {
7
+ /**
8
+ * @param {LH.Artifacts} artifacts
9
+ * @return {LH.Audit.Product}
10
+ */
11
+ static audit(artifacts: LH.Artifacts): LH.Audit.Product;
12
+ }
13
+ export namespace UIStrings {
14
+ const title: string;
15
+ const failureTitle: string;
16
+ const description: string;
17
+ }
18
+ import { Audit } from './audit.js';
19
+ //# sourceMappingURL=redirects-http.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @license Copyright 2024 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
+
7
+ import {Audit} from './audit.js';
8
+ import * as i18n from '../lib/i18n/i18n.js';
9
+ import UrlUtils from '../lib/url-utils.js';
10
+
11
+ const UIStrings = {
12
+ /** Title of a Lighthouse audit that provides detail on HTTP to HTTPS redirects. This descriptive title is shown to users when HTTP traffic is redirected to HTTPS. */
13
+ title: 'Redirects HTTP traffic to HTTPS',
14
+ /** Title of a Lighthouse audit that provides detail on HTTP to HTTPS redirects. This descriptive title is shown to users when HTTP traffic is not redirected to HTTPS. */
15
+ failureTitle: 'Does not redirect HTTP traffic to HTTPS',
16
+ /** Description of a Lighthouse audit that tells the user why they should direct HTTP traffic to HTTPS. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
17
+ description: 'Make sure that you redirect all HTTP ' +
18
+ 'traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developer.chrome.com/docs/lighthouse/pwa/redirects-http/).',
19
+ };
20
+
21
+ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
22
+
23
+ /**
24
+ * An audit for checking if a site starting on http redirects to https. The audit
25
+ * is marked not applicable if the requestedUrl is already https.
26
+ */
27
+ class RedirectsHTTP extends Audit {
28
+ /**
29
+ * @return {LH.Audit.Meta}
30
+ */
31
+ static get meta() {
32
+ return {
33
+ id: 'redirects-http',
34
+ title: str_(UIStrings.title),
35
+ failureTitle: str_(UIStrings.failureTitle),
36
+ description: str_(UIStrings.description),
37
+ requiredArtifacts: ['URL'],
38
+ supportedModes: ['navigation'],
39
+ };
40
+ }
41
+
42
+ /**
43
+ * @param {LH.Artifacts} artifacts
44
+ * @return {LH.Audit.Product}
45
+ */
46
+ static audit(artifacts) {
47
+ if (!artifacts.URL.requestedUrl) {
48
+ throw new Error('Missing requestedUrl');
49
+ }
50
+
51
+ const requestedUrl = new URL(artifacts.URL.requestedUrl);
52
+ const finalDisplayedUrl = new URL(artifacts.URL.finalDisplayedUrl);
53
+
54
+ // Not applicable unless starting on http.
55
+ const startedInsecure = requestedUrl.protocol === 'http:';
56
+
57
+ // Relax requirements on localhost.
58
+ const isLocalhost = UrlUtils.isLikeLocalhost(finalDisplayedUrl.hostname);
59
+
60
+ if (!startedInsecure || isLocalhost) {
61
+ return {
62
+ score: null,
63
+ notApplicable: true,
64
+ };
65
+ }
66
+
67
+ const endedSecure = finalDisplayedUrl.protocol === 'https:';
68
+ return {
69
+ score: Number(endedSecure),
70
+ };
71
+ }
72
+ }
73
+
74
+ export default RedirectsHTTP;
75
+ export {UIStrings};
@@ -168,6 +168,7 @@ const defaultConfig = {
168
168
  ],
169
169
  audits: [
170
170
  'is-on-https',
171
+ 'redirects-http',
171
172
  'viewport',
172
173
  'metrics/first-contentful-paint',
173
174
  'metrics/largest-contentful-paint',
@@ -574,6 +575,7 @@ const defaultConfig = {
574
575
  auditRefs: [
575
576
  // Trust & Safety
576
577
  {id: 'is-on-https', weight: 5, group: 'best-practices-trust-safety'},
578
+ {id: 'redirects-http', weight: 1, group: 'best-practices-trust-safety'},
577
579
  {id: 'geolocation-on-start', weight: 1, group: 'best-practices-trust-safety'},
578
580
  {id: 'notification-on-start', weight: 1, group: 'best-practices-trust-safety'},
579
581
  {id: 'csp-xss', weight: 0, group: 'best-practices-trust-safety'},
@@ -581,6 +583,8 @@ const defaultConfig = {
581
583
  {id: 'paste-preventing-inputs', weight: 3, group: 'best-practices-ux'},
582
584
  {id: 'image-aspect-ratio', weight: 1, group: 'best-practices-ux'},
583
585
  {id: 'image-size-responsive', weight: 1, group: 'best-practices-ux'},
586
+ {id: 'viewport', weight: 1, group: 'best-practices-ux'},
587
+ {id: 'font-size', weight: 1, group: 'best-practices-ux'},
584
588
  // Browser Compatibility
585
589
  {id: 'doctype', weight: 1, group: 'best-practices-browser-compat'},
586
590
  {id: 'charset', weight: 1, group: 'best-practices-browser-compat'},
@@ -599,7 +603,6 @@ const defaultConfig = {
599
603
  manualDescription: str_(UIStrings.seoCategoryManualDescription),
600
604
  supportedModes: ['navigation', 'snapshot'],
601
605
  auditRefs: [
602
- {id: 'viewport', weight: 1, group: 'seo-mobile'},
603
606
  {id: 'document-title', weight: 1, group: 'seo-content'},
604
607
  {id: 'meta-description', weight: 1, group: 'seo-content'},
605
608
  {id: 'http-status-code', weight: 1, group: 'seo-crawl'},
@@ -610,7 +613,6 @@ const defaultConfig = {
610
613
  {id: 'image-alt', weight: 1, group: 'seo-content'},
611
614
  {id: 'hreflang', weight: 1, group: 'seo-content'},
612
615
  {id: 'canonical', weight: 1, group: 'seo-content'},
613
- {id: 'font-size', weight: 1, group: 'seo-mobile'},
614
616
  // Manual audits
615
617
  {id: 'structured-data', weight: 0},
616
618
  ],
@@ -180,7 +180,7 @@ class TargetManager extends ProtocolEventEmitter {
180
180
  throw err;
181
181
  } finally {
182
182
  // Resume the target if it was paused, but if it's unnecessary, we don't care about the error.
183
- await newSession.sendCommand('Runtime.runIfWaitingForDebugger').catch(() => {});
183
+ await newSession.sendCommandAndIgnore('Runtime.runIfWaitingForDebugger');
184
184
  }
185
185
  }
186
186
 
@@ -498,9 +498,8 @@ async function waitForFullyLoaded(session, networkMonitor, options) {
498
498
  if (await isPageHung(session)) {
499
499
  log.warn('waitFor', 'Page appears to be hung, killing JavaScript...');
500
500
  // We don't await these, as we want to exit with PAGE_HUNG
501
- void session.sendCommand('Emulation.setScriptExecutionDisabled', {value: true})
502
- .catch(_ => {});
503
- void session.sendCommand('Runtime.terminateExecution').catch(_ => {});
501
+ void session.sendCommandAndIgnore('Emulation.setScriptExecutionDisabled', {value: true});
502
+ void session.sendCommandAndIgnore('Runtime.terminateExecution');
504
503
  throw new LighthouseError(LighthouseError.errors.PAGE_HUNG);
505
504
  }
506
505
 
@@ -26,6 +26,7 @@ const throwingSession = {
26
26
  once: throwNotConnectedFn,
27
27
  off: throwNotConnectedFn,
28
28
  sendCommand: throwNotConnectedFn,
29
+ sendCommandAndIgnore: throwNotConnectedFn,
29
30
  dispose: throwNotConnectedFn,
30
31
  };
31
32
 
@@ -40,6 +40,14 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
40
40
  * @return {Promise<LH.CrdpCommands[C]['returnType']>}
41
41
  */
42
42
  sendCommand<C extends keyof import("puppeteer-core").ProtocolMapping.Commands>(method: C, ...params: import("puppeteer-core").ProtocolMapping.Commands[C]["paramsType"]): Promise<import("puppeteer-core").ProtocolMapping.Commands[C]["returnType"]>;
43
+ /**
44
+ * Send and if there's an error response, do not reject.
45
+ * @template {keyof LH.CrdpCommands} C
46
+ * @param {C} method
47
+ * @param {LH.CrdpCommands[C]['paramsType']} params
48
+ * @return {Promise<void>}
49
+ */
50
+ sendCommandAndIgnore<C_1 extends keyof import("puppeteer-core").ProtocolMapping.Commands>(method: C_1, ...params: import("puppeteer-core").ProtocolMapping.Commands[C_1]["paramsType"]): Promise<void>;
43
51
  /**
44
52
  * Disposes of a session so that it can no longer talk to Chrome.
45
53
  * @return {Promise<void>}
@@ -120,6 +120,18 @@ class ProtocolSession extends CrdpEventEmitter {
120
120
  });
121
121
  }
122
122
 
123
+ /**
124
+ * Send and if there's an error response, do not reject.
125
+ * @template {keyof LH.CrdpCommands} C
126
+ * @param {C} method
127
+ * @param {LH.CrdpCommands[C]['paramsType']} params
128
+ * @return {Promise<void>}
129
+ */
130
+ sendCommandAndIgnore(method, ...params) {
131
+ return this.sendCommand(method, ...params)
132
+ .catch(e => log.verbose('session', method, e.message)).then(_ => void 0);
133
+ }
134
+
123
135
  /**
124
136
  * Disposes of a session so that it can no longer talk to Chrome.
125
137
  * @return {Promise<void>}
package/core/runner.js CHANGED
@@ -199,32 +199,27 @@ class Runner {
199
199
  data: sentryContext,
200
200
  });
201
201
 
202
- /** @type {LH.Artifacts} */
203
- let artifacts;
204
202
  if (settings.auditMode && !settings.gatherMode) {
205
203
  // No browser required, just load the artifacts from disk.
206
204
  const path = this._getDataSavePath(settings);
207
- artifacts = assetSaver.loadArtifacts(path);
208
- } else {
209
- const runnerStatus = {msg: 'Gather phase', id: 'lh:runner:gather'};
210
- log.time(runnerStatus, 'verbose');
211
-
212
- artifacts = await gatherFn({
213
- resolvedConfig: options.resolvedConfig,
214
- });
215
-
216
- log.timeEnd(runnerStatus);
217
-
218
- // If `gather` is run multiple times before `audit`, the timing entries for each `gather` can pollute one another.
219
- // We need to clear the timing entries at the end of gathering.
220
- // Set artifacts.Timing again to ensure lh:runner:gather is included.
221
- artifacts.Timing = log.takeTimeEntries();
222
-
223
- // -G means save these to disk (e.g. ./latest-run).
224
- if (settings.gatherMode) {
225
- const path = this._getDataSavePath(settings);
226
- await assetSaver.saveArtifacts(artifacts, path);
227
- }
205
+ return assetSaver.loadArtifacts(path);
206
+ }
207
+
208
+ const runnerStatus = {msg: 'Gather phase', id: 'lh:runner:gather'};
209
+ log.time(runnerStatus, 'verbose');
210
+
211
+ const artifacts = await gatherFn({resolvedConfig: options.resolvedConfig});
212
+ log.timeEnd(runnerStatus);
213
+
214
+ // If `gather` is run multiple times before `audit`, the timing entries for each `gather` can pollute one another.
215
+ // We need to clear the timing entries at the end of gathering.
216
+ // Set artifacts.Timing again to ensure lh:runner:gather is included.
217
+ artifacts.Timing = log.takeTimeEntries();
218
+
219
+ // -G means save these to disk (e.g. ./latest-run).
220
+ if (settings.gatherMode) {
221
+ const path = this._getDataSavePath(settings);
222
+ await assetSaver.saveArtifacts(artifacts, path);
228
223
  }
229
224
 
230
225
  return artifacts;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "11.7.0-dev.20240408",
4
+ "version": "11.7.0-dev.20240409",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -1322,6 +1322,15 @@
1322
1322
  "core/audits/prioritize-lcp-image.js | title": {
1323
1323
  "message": "Preload Largest Contentful Paint image"
1324
1324
  },
1325
+ "core/audits/redirects-http.js | description": {
1326
+ "message": "Make sure that you redirect all HTTP traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developer.chrome.com/docs/lighthouse/pwa/redirects-http/)."
1327
+ },
1328
+ "core/audits/redirects-http.js | failureTitle": {
1329
+ "message": "Does not redirect HTTP traffic to HTTPS"
1330
+ },
1331
+ "core/audits/redirects-http.js | title": {
1332
+ "message": "Redirects HTTP traffic to HTTPS"
1333
+ },
1325
1334
  "core/audits/redirects.js | description": {
1326
1335
  "message": "Redirects introduce additional delays before the page can be loaded. [Learn how to avoid page redirects](https://developer.chrome.com/docs/lighthouse/performance/redirects/)."
1327
1336
  },
@@ -1322,6 +1322,15 @@
1322
1322
  "core/audits/prioritize-lcp-image.js | title": {
1323
1323
  "message": "P̂ŕêĺôád̂ Ĺâŕĝéŝt́ Ĉón̂t́êńt̂f́ûĺ P̂áîńt̂ ím̂áĝé"
1324
1324
  },
1325
+ "core/audits/redirects-http.js | description": {
1326
+ "message": "M̂ák̂é ŝúr̂é t̂h́ât́ ŷóû ŕêd́îŕêćt̂ ál̂ĺ ĤT́T̂Ṕ t̂ŕâf́f̂íĉ t́ô H́T̂T́P̂Ś îń ôŕd̂ér̂ t́ô én̂áb̂ĺê śêćûŕê ẃêb́ f̂éât́ûŕêś f̂ór̂ ál̂ĺ ŷóûŕ ûśêŕŝ. [Ĺêár̂ń m̂ór̂é](https://developer.chrome.com/docs/lighthouse/pwa/redirects-http/)."
1327
+ },
1328
+ "core/audits/redirects-http.js | failureTitle": {
1329
+ "message": "D̂óêś n̂ót̂ ŕêd́îŕêćt̂ H́T̂T́P̂ t́r̂áf̂f́îć t̂ó ĤT́T̂ṔŜ"
1330
+ },
1331
+ "core/audits/redirects-http.js | title": {
1332
+ "message": "R̂éd̂ír̂éĉt́ŝ H́T̂T́P̂ t́r̂áf̂f́îć t̂ó ĤT́T̂ṔŜ"
1333
+ },
1325
1334
  "core/audits/redirects.js | description": {
1326
1335
  "message": "R̂éd̂ír̂éĉt́ŝ ín̂t́r̂ód̂úĉé âd́d̂ít̂íôńâĺ d̂él̂áŷś b̂éf̂ór̂é t̂h́ê ṕâǵê ćâń b̂é l̂óâd́êd́. [L̂éâŕn̂ h́ôẃ t̂ó âv́ôíd̂ ṕâǵê ŕêd́îŕêćt̂ś](https://developer.chrome.com/docs/lighthouse/performance/redirects/)."
1327
1336
  },
@@ -33,6 +33,7 @@ declare module Gatherer {
33
33
  once<TEvent extends keyof CrdpEvents>(event: TEvent, callback: (...args: CrdpEvents[TEvent]) => void): void;
34
34
  off<TEvent extends keyof CrdpEvents>(event: TEvent, callback: (...args: CrdpEvents[TEvent]) => void): void;
35
35
  sendCommand<TMethod extends keyof CrdpCommands>(method: TMethod, ...params: CrdpCommands[TMethod]['paramsType']): Promise<CrdpCommands[TMethod]['returnType']>;
36
+ sendCommandAndIgnore<TMethod extends keyof CrdpCommands>(method: TMethod, ...params: CrdpCommands[TMethod]['paramsType']): Promise<void>;
36
37
  dispose(): Promise<void>;
37
38
  }
38
39