lighthouse 13.5.0-dev.20261014 → 13.5.0-dev.20261016

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.
@@ -11,5 +11,5 @@ export function chromiumVersionCheck(opts: {
11
11
  * @param {number[]} versionA
12
12
  * @param {number[]} versionB
13
13
  */
14
- export function compareVersions(versionA: number[], versionB: number[]): 1 | -1 | 0;
14
+ export function compareVersions(versionA: number[], versionB: number[]): 0 | 1 | -1;
15
15
  //# sourceMappingURL=version-check.d.ts.map
@@ -796,8 +796,10 @@ declare class Baseline extends Audit {
796
796
  * - `high`: `baseline === 'high'` (Widely Available — 30 months past `baseline_low_date`; note that
797
797
  * `featureData.high[id]` still stores the feature's `baseline_low_date`).
798
798
  *
799
+ * Widely Available rows always show `baseline_low_date`, including rows promoted at runtime.
799
800
  * If a feature in `featureData.low` has reached `baseline_low_date + 30 months <= currentDate` at runtime,
800
- * we dynamically promote it from `low` (Newly Available) to `high` (Widely Available).
801
+ * we dynamically promote it from `low` (Newly Available) to `high` (Widely Available); promotion
802
+ * only changes the tier.
801
803
  *
802
804
  * @param {string} featureId
803
805
  * @param {{high: Record<string, string>, low: Record<string, string>, limited: string[]}} featureData
@@ -55,8 +55,10 @@ class Baseline extends Audit {
55
55
  * - `high`: `baseline === 'high'` (Widely Available — 30 months past `baseline_low_date`; note that
56
56
  * `featureData.high[id]` still stores the feature's `baseline_low_date`).
57
57
  *
58
+ * Widely Available rows always show `baseline_low_date`, including rows promoted at runtime.
58
59
  * If a feature in `featureData.low` has reached `baseline_low_date + 30 months <= currentDate` at runtime,
59
- * we dynamically promote it from `low` (Newly Available) to `high` (Widely Available).
60
+ * we dynamically promote it from `low` (Newly Available) to `high` (Widely Available); promotion
61
+ * only changes the tier.
60
62
  *
61
63
  * @param {string} featureId
62
64
  * @param {{high: Record<string, string>, low: Record<string, string>, limited: string[]}} featureData
@@ -76,11 +78,10 @@ class Baseline extends Audit {
76
78
  const lowData = /** @type {Record<string, string>} */ (featureData.low);
77
79
  const widelyAvailableDate = new Date(lowData[featureId]);
78
80
  widelyAvailableDate.setUTCMonth(widelyAvailableDate.getUTCMonth() + 30);
79
- const widelyAvailableDateStr = widelyAvailableDate.toISOString().slice(0, 10);
80
81
 
81
82
  if (widelyAvailableDate <= currentDate) {
82
83
  return {
83
- displayStatus: `Widely Available (${widelyAvailableDateStr})`,
84
+ displayStatus: `Widely Available (${lowData[featureId]})`,
84
85
  baselineTier: 'high',
85
86
  };
86
87
  } else {
@@ -136,36 +136,41 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
136
136
  // TODO: consider adding a `estimatedSavingsText` to InsightModel, which can capture
137
137
  // the exact i18n string used by RPP; and include the same est. timing savings.
138
138
  let displayValue;
139
+ let wastedMs;
140
+
141
+ switch (insight.insightKey) {
142
+ case 'DocumentLatency':
143
+ case 'FontDisplay':
144
+ case 'RenderBlocking': {
145
+ wastedMs = metricSavings?.FCP ?? 0;
146
+ break;
147
+ }
139
148
 
140
- if (insight.wastedBytes) {
141
- displayValue = str_(i18n.UIStrings.displayValueByteSavings, {wastedBytes: insight.wastedBytes});
142
- } else {
143
- let wastedMs;
144
-
145
- switch (insight.insightKey) {
146
- case 'DocumentLatency':
147
- case 'DuplicatedJavaScript':
148
- case 'FontDisplay':
149
- case 'LegacyJavaScript':
150
- case 'RenderBlocking': {
151
- wastedMs = metricSavings?.FCP;
152
- break;
153
- }
154
-
155
- case 'LCPDiscovery':
156
- case 'ModernHTTP': {
157
- wastedMs = metricSavings?.LCP;
158
- break;
159
- }
149
+ case 'LCPDiscovery':
150
+ case 'ModernHTTP': {
151
+ wastedMs = metricSavings?.LCP ?? 0;
152
+ break;
153
+ }
160
154
 
161
- case 'Viewport': {
162
- wastedMs = metricSavings?.INP;
163
- break;
164
- }
155
+ case 'Viewport': {
156
+ wastedMs = metricSavings?.INP ?? 0;
157
+ break;
165
158
  }
159
+ }
160
+
161
+ if (insight.wastedBytes) {
162
+ displayValue = str_(i18n.UIStrings.displayValueByteSavings, {wastedBytes: insight.wastedBytes});
163
+ } else if (wastedMs) {
164
+ displayValue = str_(i18n.UIStrings.displayValueMsSavings, {wastedMs});
165
+ }
166
166
 
167
- if (wastedMs) {
168
- displayValue = str_(i18n.UIStrings.displayValueMsSavings, {wastedMs});
167
+ if (numericValue === undefined) {
168
+ if (wastedMs !== undefined) {
169
+ numericValue = wastedMs;
170
+ numericUnit = 'millisecond';
171
+ } else if (insight.wastedBytes !== undefined) {
172
+ numericValue = insight.wastedBytes;
173
+ numericUnit = 'byte';
169
174
  }
170
175
  }
171
176
 
@@ -7,8 +7,6 @@ import path from 'path';
7
7
  import {createRequire} from 'module';
8
8
  import url from 'url';
9
9
 
10
- import {isEqual} from 'lodash-es';
11
-
12
10
  import * as LH from '../../types/lh.js';
13
11
  import * as constants from './constants.js';
14
12
  import ConfigPlugin from './config-plugin.js';
@@ -16,6 +14,7 @@ import {Runner} from '../runner.js';
16
14
  import * as i18n from '../lib/i18n/i18n.js';
17
15
  import * as validation from './validation.js';
18
16
  import {getModuleDirectory} from '../../shared/esm-utils.js';
17
+ import {isEqual} from '../lib/arbitrary-equality-map.js';
19
18
 
20
19
  const require = createRequire(import.meta.url);
21
20
 
@@ -5,12 +5,12 @@
5
5
  */
6
6
 
7
7
  import log from 'lighthouse-logger';
8
- import {isEqual} from 'lodash-es';
9
8
 
10
9
  import {
11
10
  getBrowserVersion, getBenchmarkIndex, getEnvironmentWarnings,
12
11
  getDevicePixelRatio,
13
12
  } from './driver/environment.js';
13
+ import {isEqual} from '../lib/arbitrary-equality-map.js';
14
14
 
15
15
  /**
16
16
  * @param {LH.Config.ResolvedConfig} resolvedConfig
@@ -41,13 +41,14 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
41
41
  * @return {Promise<LH.IcuMessage[]>}
42
42
  */
43
43
  async function clearDataForOrigin(session, url, clearStorageTypes) {
44
+ const origin = new URL(url).origin;
45
+ if (origin === 'null') return [];
46
+
44
47
  const status = {msg: 'Cleaning origin data', id: 'lh:storage:clearDataForOrigin'};
45
48
  log.time(status);
46
49
 
47
50
  const warnings = [];
48
51
 
49
- const origin = new URL(url).origin;
50
-
51
52
  const typesToClear = clearStorageTypes.join(',');
52
53
 
53
54
  // `Storage.clearDataForOrigin` is one of our PROTOCOL_TIMEOUT culprits and this command is also
@@ -5,9 +5,7 @@
5
5
  */
6
6
  export class ArbitraryEqualityMap {
7
7
  /**
8
- * Determines whether two objects are deeply equal. Defers to lodash isEqual, but is kept here for
9
- * easy usage by consumers.
10
- * See https://lodash.com/docs/4.17.5#isEqual.
8
+ * Determines whether two objects are deeply equal.
11
9
  * @param {*} objA
12
10
  * @param {*} objB
13
11
  * @return {boolean}
@@ -44,4 +42,17 @@ export class ArbitraryEqualityMap {
44
42
  */
45
43
  _findIndexOf(key: any): number;
46
44
  }
45
+ /**
46
+ * @license
47
+ * Copyright 2018 Google LLC
48
+ * SPDX-License-Identifier: Apache-2.0
49
+ */
50
+ /**
51
+ * @param {*} a
52
+ * @param {*} b
53
+ * @param {WeakMap<object, object>=} seen
54
+ * @return {boolean}
55
+ */
56
+ declare function deepEquals(a: any, b: any, seen?: WeakMap<object, object> | undefined): boolean;
57
+ export { deepEquals as isEqual };
47
58
  //# sourceMappingURL=arbitrary-equality-map.d.ts.map
@@ -4,7 +4,76 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import {isEqual} from 'lodash-es';
7
+ /**
8
+ * @param {*} a
9
+ * @param {*} b
10
+ * @param {WeakMap<object, object>=} seen
11
+ * @return {boolean}
12
+ */
13
+ function deepEquals(a, b, seen) {
14
+ if (a === b || (Number.isNaN(a) && Number.isNaN(b))) return true;
15
+ if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
16
+ return false;
17
+ }
18
+ if (a.constructor !== b.constructor) return false;
19
+
20
+ if (a instanceof Date && b instanceof Date) {
21
+ return a.getTime() === b.getTime() || (Number.isNaN(a.getTime()) && Number.isNaN(b.getTime()));
22
+ }
23
+ if (a instanceof RegExp && b instanceof RegExp) {
24
+ return a.source === b.source && a.flags === b.flags;
25
+ }
26
+ if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
27
+ if (a.byteLength !== b.byteLength) return false;
28
+ const viewA = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
29
+ const viewB = new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
30
+ for (let i = 0; i < viewA.length; i++) {
31
+ if (viewA[i] !== viewB[i]) return false;
32
+ }
33
+ return true;
34
+ }
35
+
36
+ if (seen) {
37
+ if (seen.get(a) === b) return true;
38
+ } else {
39
+ seen = new WeakMap();
40
+ }
41
+ seen.set(a, b);
42
+
43
+ if (Array.isArray(a)) {
44
+ const arrB = /** @type {unknown[]} */ (b);
45
+ if (a.length !== arrB.length) return false;
46
+ for (let i = 0; i < a.length; i++) {
47
+ if (!deepEquals(a[i], arrB[i], seen)) return false;
48
+ }
49
+ return true;
50
+ }
51
+
52
+ if (a instanceof Map && b instanceof Map) {
53
+ if (a.size !== b.size) return false;
54
+ for (const [k, v] of a) {
55
+ if (!b.has(k) || !deepEquals(v, b.get(k), seen)) return false;
56
+ }
57
+ return true;
58
+ }
59
+
60
+ if (a instanceof Set && b instanceof Set) {
61
+ if (a.size !== b.size) return false;
62
+ for (const v of a) {
63
+ if (!b.has(v)) return false;
64
+ }
65
+ return true;
66
+ }
67
+
68
+ const keysA = Object.keys(a);
69
+ const keysB = Object.keys(b);
70
+ if (keysA.length !== keysB.length) return false;
71
+ for (let i = 0; i < keysA.length; i++) {
72
+ const key = keysA[i];
73
+ if (!Object.hasOwn(b, key) || !deepEquals(a[key], b[key], seen)) return false;
74
+ }
75
+ return true;
76
+ }
8
77
 
9
78
  /**
10
79
  * @fileoverview This class is designed to allow maps with arbitrary equality functions.
@@ -65,16 +134,14 @@ class ArbitraryEqualityMap {
65
134
  }
66
135
 
67
136
  /**
68
- * Determines whether two objects are deeply equal. Defers to lodash isEqual, but is kept here for
69
- * easy usage by consumers.
70
- * See https://lodash.com/docs/4.17.5#isEqual.
137
+ * Determines whether two objects are deeply equal.
71
138
  * @param {*} objA
72
139
  * @param {*} objB
73
140
  * @return {boolean}
74
141
  */
75
142
  static deepEquals(objA, objB) {
76
- return isEqual(objA, objB);
143
+ return deepEquals(objA, objB);
77
144
  }
78
145
  }
79
146
 
80
- export {ArbitraryEqualityMap};
147
+ export {ArbitraryEqualityMap, deepEquals as isEqual};
package/core/runner.js CHANGED
@@ -9,7 +9,6 @@ import path from 'path';
9
9
 
10
10
 
11
11
  import log from 'lighthouse-logger';
12
- import {isEqual} from 'lodash-es';
13
12
 
14
13
  import {ReportScoring} from './scoring.js';
15
14
  import {Audit} from './audits/audit.js';
@@ -24,6 +23,7 @@ import {getModuleDirectory} from '../shared/esm-utils.js';
24
23
  import {EntityClassification} from './computed/entity-classification.js';
25
24
  import UrlUtils from './lib/url-utils.js';
26
25
  import {Util} from '../shared/util.js';
26
+ import {isEqual} from './lib/arbitrary-equality-map.js';
27
27
 
28
28
  const moduleDir = getModuleDirectory(import.meta);
29
29
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "13.5.0-dev.20261014",
4
+ "version": "13.5.0-dev.20261016",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -121,7 +121,6 @@
121
121
  "@types/google.analytics": "0.0.39",
122
122
  "@types/gtag.js": "0.0.20",
123
123
  "@types/jsdom": "^16.2.13",
124
- "@types/lodash-es": "^4.17.12",
125
124
  "@types/mocha": "^9.0.0",
126
125
  "@types/node": "*",
127
126
  "@types/pako": "^1.0.1",
@@ -129,9 +128,8 @@
129
128
  "@types/resize-observer-browser": "^0.1.1",
130
129
  "@types/resolve": "^1.20.2",
131
130
  "@types/semver": "^5.5.0",
132
- "@types/ws": "^7.0.0",
133
- "@types/yargs": "^17.0.8",
134
- "@types/yargs-parser": "^20.2.1",
131
+ "@types/yargs": "^17.0.35",
132
+ "@types/yargs-parser": "^21.0.3",
135
133
  "@typescript-eslint/eslint-plugin": "^8.60.1",
136
134
  "@typescript-eslint/parser": "^8.60.1",
137
135
  "acorn": "^8.5.0",
@@ -196,7 +194,6 @@
196
194
  "js-library-detector": "^6.7.0",
197
195
  "lighthouse-logger": "^2.0.2",
198
196
  "lighthouse-stack-packs": "1.12.3",
199
- "lodash-es": "^4.17.21",
200
197
  "lookup-closest-locale": "6.2.0",
201
198
  "open": "^8.4.0",
202
199
  "puppeteer-core": "^25.12.0",
@@ -204,9 +201,8 @@
204
201
  "speedline-core": "^1.4.3",
205
202
  "third-party-web": "^0.30.0",
206
203
  "tldts-icann": "^7.4.16",
207
- "ws": "^7.0.0",
208
- "yargs": "^17.3.1",
209
- "yargs-parser": "^21.0.0"
204
+ "yargs": "^18.2.0",
205
+ "yargs-parser": "^22.0.0"
210
206
  },
211
207
  "resolutions": {
212
208
  "puppeteer/**/devtools-protocol": "0.0.1704951",
@@ -4,9 +4,6 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import {set} from 'lodash-es';
8
- import {get} from 'lodash-es';
9
-
10
7
  import * as format from './format.js';
11
8
 
12
9
  /**
@@ -70,9 +67,17 @@ function swapLocale(lhr, requestedLocale) {
70
67
  values = instance.values;
71
68
  }
72
69
 
70
+ const keys = path.replace(/\[([^\]]+)\]/g, '.$1').replace(/^\./, '').split('.');
71
+ /** @type {any} */
72
+ let parent = lhr;
73
+ for (let i = 0; i < keys.length - 1; i++) {
74
+ parent = parent?.[keys[i]];
75
+ }
76
+ const lastKey = keys[keys.length - 1];
77
+
73
78
  // If the path isn't valid or the value isn't a string, there's no point in trying to replace it.
74
79
  /** @type {unknown} */
75
- const originalString = get(lhr, path);
80
+ const originalString = parent?.[lastKey];
76
81
  if (typeof originalString !== 'string') {
77
82
  continue;
78
83
  }
@@ -96,7 +101,7 @@ function swapLocale(lhr, requestedLocale) {
96
101
  }
97
102
 
98
103
  // Write string back into the LHR.
99
- set(lhr, path, relocalizedString);
104
+ parent[lastKey] = relocalizedString;
100
105
  }
101
106
  }
102
107