lighthouse 9.5.0-dev.20220418 → 9.5.0-dev.20220419

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.
@@ -21,9 +21,7 @@ export class I18n {
21
21
  // When testing, use a locale with more exciting numeric formatting.
22
22
  if (locale === 'en-XA') locale = 'de';
23
23
 
24
- this._numberDateLocale = locale;
25
- this._numberFormatter = new Intl.NumberFormat(locale);
26
- this._percentFormatter = new Intl.NumberFormat(locale, {style: 'percent'});
24
+ this._locale = locale;
27
25
  this._strings = strings;
28
26
  }
29
27
 
@@ -31,6 +29,32 @@ export class I18n {
31
29
  return this._strings;
32
30
  }
33
31
 
32
+ /**
33
+ * @param {number} number
34
+ * @param {number} granularity
35
+ * @param {Intl.NumberFormatOptions} opts
36
+ * @return {string}
37
+ */
38
+ _formatNumberWithGranularity(number, granularity, opts = {}) {
39
+ opts = {...opts};
40
+ const log10 = -Math.log10(granularity);
41
+ if (!Number.isFinite(log10) || (granularity > 1 && !Number.isInteger(log10))) {
42
+ console.warn(`granularity of ${granularity} is invalid, defaulting to value of 1`);
43
+ granularity = 1;
44
+ }
45
+
46
+ if (granularity < 1) {
47
+ opts.minimumFractionDigits = opts.maximumFractionDigits = Math.ceil(log10);
48
+ }
49
+
50
+ number = Math.round(number / granularity) * granularity;
51
+
52
+ // Avoid displaying a negative value that rounds to zero as "0".
53
+ if (Object.is(number, -0)) number = 0;
54
+
55
+ return new Intl.NumberFormat(this._locale, opts).format(number).replace(' ', NBSP2);
56
+ }
57
+
34
58
  /**
35
59
  * Format number.
36
60
  * @param {number} number
@@ -38,8 +62,18 @@ export class I18n {
38
62
  * @return {string}
39
63
  */
40
64
  formatNumber(number, granularity = 0.1) {
41
- const coarseValue = Math.round(number / granularity) * granularity;
42
- return this._numberFormatter.format(coarseValue);
65
+ return this._formatNumberWithGranularity(number, granularity);
66
+ }
67
+
68
+ /**
69
+ * Format integer.
70
+ * Just like {@link formatNumber} but uses a granularity of 1, rounding to the nearest
71
+ * whole number.
72
+ * @param {number} number
73
+ * @return {string}
74
+ */
75
+ formatInteger(number) {
76
+ return this._formatNumberWithGranularity(number, 1);
43
77
  }
44
78
 
45
79
  /**
@@ -48,7 +82,7 @@ export class I18n {
48
82
  * @return {string}
49
83
  */
50
84
  formatPercent(number) {
51
- return this._percentFormatter.format(number);
85
+ return new Intl.NumberFormat(this._locale, {style: 'percent'}).format(number);
52
86
  }
53
87
 
54
88
  /**
@@ -57,9 +91,7 @@ export class I18n {
57
91
  * @return {string}
58
92
  */
59
93
  formatBytesToKiB(size, granularity = 0.1) {
60
- const formatter = this._byteFormatterForGranularity(granularity);
61
- const kbs = formatter.format(Math.round(size / 1024 / granularity) * granularity);
62
- return `${kbs}${NBSP2}KiB`;
94
+ return this._formatNumberWithGranularity(size / KiB, granularity) + `${NBSP2}KiB`;
63
95
  }
64
96
 
65
97
  /**
@@ -68,9 +100,7 @@ export class I18n {
68
100
  * @return {string}
69
101
  */
70
102
  formatBytesToMiB(size, granularity = 0.1) {
71
- const formatter = this._byteFormatterForGranularity(granularity);
72
- const kbs = formatter.format(Math.round(size / (1024 ** 2) / granularity) * granularity);
73
- return `${kbs}${NBSP2}MiB`;
103
+ return this._formatNumberWithGranularity(size / MiB, granularity) + `${NBSP2}MiB`;
74
104
  }
75
105
 
76
106
  /**
@@ -79,9 +109,11 @@ export class I18n {
79
109
  * @return {string}
80
110
  */
81
111
  formatBytes(size, granularity = 1) {
82
- const formatter = this._byteFormatterForGranularity(granularity);
83
- const kbs = formatter.format(Math.round(size / granularity) * granularity);
84
- return `${kbs}${NBSP2}bytes`;
112
+ return this._formatNumberWithGranularity(size, granularity, {
113
+ style: 'unit',
114
+ unit: 'byte',
115
+ unitDisplay: 'long',
116
+ });
85
117
  }
86
118
 
87
119
  /**
@@ -92,25 +124,23 @@ export class I18n {
92
124
  formatBytesWithBestUnit(size, granularity = 0.1) {
93
125
  if (size >= MiB) return this.formatBytesToMiB(size, granularity);
94
126
  if (size >= KiB) return this.formatBytesToKiB(size, granularity);
95
- return this.formatNumber(size, granularity) + '\xa0B';
127
+ return this._formatNumberWithGranularity(size, granularity, {
128
+ style: 'unit',
129
+ unit: 'byte',
130
+ unitDisplay: 'narrow',
131
+ });
96
132
  }
97
133
 
98
134
  /**
99
- * Format bytes with a constant number of fractional digits, i.e. for a granularity of 0.1, 10 becomes '10.0'
100
- * @param {number} granularity Controls how coarse the displayed value is
101
- * @return {Intl.NumberFormat}
135
+ * @param {number} size
136
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 1
137
+ * @return {string}
102
138
  */
103
- _byteFormatterForGranularity(granularity) {
104
- // assume any granularity above 1 will not contain fractional parts, i.e. will never be 1.5
105
- let numberOfFractionDigits = 0;
106
- if (granularity < 1) {
107
- numberOfFractionDigits = -Math.floor(Math.log10(granularity));
108
- }
109
-
110
- return new Intl.NumberFormat(this._numberDateLocale, {
111
- ...this._numberFormatter.resolvedOptions(),
112
- maximumFractionDigits: numberOfFractionDigits,
113
- minimumFractionDigits: numberOfFractionDigits,
139
+ formatKbps(size, granularity = 1) {
140
+ return this._formatNumberWithGranularity(size, granularity, {
141
+ style: 'unit',
142
+ unit: 'kilobit-per-second',
143
+ unitDisplay: 'short',
114
144
  });
115
145
  }
116
146
 
@@ -120,10 +150,11 @@ export class I18n {
120
150
  * @return {string}
121
151
  */
122
152
  formatMilliseconds(ms, granularity = 10) {
123
- const coarseTime = Math.round(ms / granularity) * granularity;
124
- return coarseTime === 0
125
- ? `${this._numberFormatter.format(0)}${NBSP2}ms`
126
- : `${this._numberFormatter.format(coarseTime)}${NBSP2}ms`;
153
+ return this._formatNumberWithGranularity(ms, granularity, {
154
+ style: 'unit',
155
+ unit: 'millisecond',
156
+ unitDisplay: 'short',
157
+ });
127
158
  }
128
159
 
129
160
  /**
@@ -132,8 +163,11 @@ export class I18n {
132
163
  * @return {string}
133
164
  */
134
165
  formatSeconds(ms, granularity = 0.1) {
135
- const coarseTime = Math.round(ms / 1000 / granularity) * granularity;
136
- return `${this._numberFormatter.format(coarseTime)}${NBSP2}s`;
166
+ return this._formatNumberWithGranularity(ms / 1000, granularity, {
167
+ style: 'unit',
168
+ unit: 'second',
169
+ unitDisplay: 'short',
170
+ });
137
171
  }
138
172
 
139
173
  /**
@@ -153,10 +187,10 @@ export class I18n {
153
187
  // and https://github.com/GoogleChrome/lighthouse/pull/9822
154
188
  let formatter;
155
189
  try {
156
- formatter = new Intl.DateTimeFormat(this._numberDateLocale, options);
190
+ formatter = new Intl.DateTimeFormat(this._locale, options);
157
191
  } catch (err) {
158
192
  options.timeZone = 'UTC';
159
- formatter = new Intl.DateTimeFormat(this._numberDateLocale, options);
193
+ formatter = new Intl.DateTimeFormat(this._locale, options);
160
194
  }
161
195
 
162
196
  return formatter.format(new Date(date));
@@ -168,6 +202,10 @@ export class I18n {
168
202
  * @return {string}
169
203
  */
170
204
  formatDuration(timeInMilliseconds) {
205
+ // There is a proposal for a Intl.DurationFormat.
206
+ // https://github.com/tc39/proposal-intl-duration-format
207
+ // Until then, we do things a bit more manually.
208
+
171
209
  let timeInSeconds = timeInMilliseconds / 1000;
172
210
  if (Math.round(timeInSeconds) === 0) {
173
211
  return 'None';
@@ -176,19 +214,24 @@ export class I18n {
176
214
  /** @type {Array<string>} */
177
215
  const parts = [];
178
216
  /** @type {Record<string, number>} */
179
- const unitLabels = {
180
- d: 60 * 60 * 24,
181
- h: 60 * 60,
182
- m: 60,
183
- s: 1,
217
+ const unitToSecondsPer = {
218
+ day: 60 * 60 * 24,
219
+ hour: 60 * 60,
220
+ minute: 60,
221
+ second: 1,
184
222
  };
185
223
 
186
- Object.keys(unitLabels).forEach(label => {
187
- const unit = unitLabels[label];
188
- const numberOfUnits = Math.floor(timeInSeconds / unit);
224
+ Object.keys(unitToSecondsPer).forEach(unit => {
225
+ const secondsPerUnit = unitToSecondsPer[unit];
226
+ const numberOfUnits = Math.floor(timeInSeconds / secondsPerUnit);
189
227
  if (numberOfUnits > 0) {
190
- timeInSeconds -= numberOfUnits * unit;
191
- parts.push(`${numberOfUnits}\xa0${label}`);
228
+ timeInSeconds -= numberOfUnits * secondsPerUnit;
229
+ const part = this._formatNumberWithGranularity(numberOfUnits, 1, {
230
+ style: 'unit',
231
+ unit,
232
+ unitDisplay: 'narrow',
233
+ });
234
+ parts.push(part);
192
235
  }
193
236
  });
194
237
 
@@ -429,10 +429,13 @@ class Util {
429
429
  break;
430
430
  case 'devtools': {
431
431
  const {cpuSlowdownMultiplier, requestLatencyMs} = throttling;
432
- cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`;
433
- networkThrottling = `${Util.i18n.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` +
434
- `${Util.i18n.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` +
435
- `${Util.i18n.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`;
432
+ // TODO: better api in i18n formatter such that this isn't needed.
433
+ const cpuGranularity = Number.isInteger(cpuSlowdownMultiplier) ? 1 : 0.1;
434
+ // eslint-disable-next-line max-len
435
+ cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier, cpuGranularity)}x slowdown (DevTools)`;
436
+ networkThrottling = `${Util.i18n.formatMilliseconds(requestLatencyMs, 1)} HTTP RTT, ` +
437
+ `${Util.i18n.formatKbps(throttling.downloadThroughputKbps)} down, ` +
438
+ `${Util.i18n.formatKbps(throttling.uploadThroughputKbps)} up (DevTools)`;
436
439
 
437
440
  const isSlow4G = () => {
438
441
  return requestLatencyMs === 150 * 3.75 &&
@@ -444,9 +447,12 @@ class Util {
444
447
  }
445
448
  case 'simulate': {
446
449
  const {cpuSlowdownMultiplier, rttMs, throughputKbps} = throttling;
447
- cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
448
- networkThrottling = `${Util.i18n.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` +
449
- `${Util.i18n.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`;
450
+ // TODO: better api in i18n formatter such that this isn't needed.
451
+ const cpuGranularity = Number.isInteger(cpuSlowdownMultiplier) ? 1 : 0.1;
452
+ // eslint-disable-next-line max-len
453
+ cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier, cpuGranularity)}x slowdown (Simulated)`;
454
+ networkThrottling = `${Util.i18n.formatMilliseconds(rttMs)} TCP RTT, ` +
455
+ `${Util.i18n.formatKbps(throughputKbps)} throughput (Simulated)`;
450
456
 
451
457
  const isSlow4G = () => {
452
458
  return rttMs === 150 && throughputKbps === 1.6 * 1024;
@@ -65,7 +65,8 @@ describe('ElementScreenshotRenderer', () => {
65
65
  /* eslint-disable max-len */
66
66
  expect(htmlFormatted).toMatchInlineSnapshot(`
67
67
  "
68
- <div class=\\"lh-element-screenshot__content\\" style=\\"top: -500px;\\">
68
+ <div class=\\"lh-element-screenshot__content\\">
69
+ <div class=\\"lh-element-screenshot__image\\" style=\\"width: 500px; height: 500px; background-position-y: 0px; background-position-x: 0px; background-size: 1000px 1000px;\\">
69
70
  <div class=\\"lh-element-screenshot__mask\\" style=\\"width: 500px; height: 500px; clip-path: url(#clip-0);\\">
70
71
  <svg height=\\"0\\" width=\\"0\\"> <defs>
71
72
  <clipPath clipPathUnits=\\"objectBoundingBox\\" id=\\"clip-0\\">
@@ -73,8 +74,7 @@ describe('ElementScreenshotRenderer', () => {
73
74
  <polygon points=\\"0,0.7 1,0.7 1,1 0,1\\"></polygon>
74
75
  <polygon points=\\"0,0.1 0.1,0.1 0.1,0.7 0,0.7\\"></polygon>
75
76
  <polygon points=\\"0.5,0.1 1,0.1 1,0.7 0.5,0.7\\"></polygon></clipPath> </defs> </svg> </div>
76
- <div class=\\"lh-element-screenshot__image\\" style=\\"width: 500px; height: 500px; background-position-y: 0px; background-position-x: 0px; background-size: 1000px 1000px;\\"></div>
77
- <div class=\\"lh-element-screenshot__element-marker\\" style=\\"width: 200px; height: 300px; left: 50px; top: 50px;\\"></div> </div> "
77
+ <div class=\\"lh-element-screenshot__element-marker\\" style=\\"width: 200px; height: 300px; left: 50px; top: 50px;\\"></div> </div> </div> "
78
78
  `);
79
79
  /* eslint-enable max-len */
80
80
  });
@@ -21,9 +21,12 @@ const NBSP = '\xa0';
21
21
  describe('util helpers', () => {
22
22
  it('formats a number', () => {
23
23
  const i18n = new I18n('en', {...Util.UIStrings});
24
- assert.strictEqual(i18n.formatNumber(10), '10');
25
- assert.strictEqual(i18n.formatNumber(100.01), '100');
24
+ assert.strictEqual(i18n.formatNumber(10), '10.0');
25
+ assert.strictEqual(i18n.formatNumber(100.01), '100.0');
26
26
  assert.strictEqual(i18n.formatNumber(13000.456), '13,000.5');
27
+ assert.strictEqual(i18n.formatInteger(10), '10');
28
+ assert.strictEqual(i18n.formatInteger(100.01), '100');
29
+ assert.strictEqual(i18n.formatInteger(13000.6), '13,001');
27
30
  });
28
31
 
29
32
  it('formats a date', () => {
@@ -100,9 +103,25 @@ describe('util helpers', () => {
100
103
 
101
104
  it('formats a duration', () => {
102
105
  const i18n = new I18n('en', {...Util.UIStrings});
103
- assert.equal(i18n.formatDuration(60 * 1000), `1${NBSP}m`);
104
- assert.equal(i18n.formatDuration(60 * 60 * 1000 + 5000), `1${NBSP}h 5${NBSP}s`);
105
- assert.equal(i18n.formatDuration(28 * 60 * 60 * 1000 + 5000), `1${NBSP}d 4${NBSP}h 5${NBSP}s`);
106
+ assert.equal(i18n.formatDuration(60 * 1000), '1m');
107
+ assert.equal(i18n.formatDuration(60 * 60 * 1000 + 5000), '1h 5s');
108
+ assert.equal(i18n.formatDuration(28 * 60 * 60 * 1000 + 5000), '1d 4h 5s');
109
+ });
110
+
111
+ it('formats a duration based on locale', () => {
112
+ let i18n = new I18n('de', {...Util.UIStrings});
113
+ assert.equal(i18n.formatDuration(60 * 1000), `1${NBSP}Min.`);
114
+ assert.equal(i18n.formatDuration(60 * 60 * 1000 + 5000), `1${NBSP}Std. 5${NBSP}Sek.`);
115
+ assert.equal(
116
+ i18n.formatDuration(28 * 60 * 60 * 1000 + 5000), `1${NBSP}T 4${NBSP}Std. 5${NBSP}Sek.`);
117
+
118
+ // Yes, this is actually backwards (s h d).
119
+ i18n = new I18n('ar', {...Util.UIStrings});
120
+ /* eslint-disable no-irregular-whitespace */
121
+ assert.equal(i18n.formatDuration(60 * 1000), `١${NBSP}د`);
122
+ assert.equal(i18n.formatDuration(60 * 60 * 1000 + 5000), `١${NBSP}س ٥${NBSP}ث`);
123
+ assert.equal(i18n.formatDuration(28 * 60 * 60 * 1000 + 5000), `١ ي ٤ س ٥ ث`);
124
+ /* eslint-enable no-irregular-whitespace */
106
125
  });
107
126
 
108
127
  it('formats numbers based on locale', () => {
@@ -113,7 +132,7 @@ describe('util helpers', () => {
113
132
  assert.strictEqual(i18n.formatNumber(number), '12.346,9');
114
133
  assert.strictEqual(i18n.formatBytesToKiB(number), `12,1${NBSP}KiB`);
115
134
  assert.strictEqual(i18n.formatMilliseconds(number), `12.350${NBSP}ms`);
116
- assert.strictEqual(i18n.formatSeconds(number), `12,3${NBSP}s`);
135
+ assert.strictEqual(i18n.formatSeconds(number), `12,3${NBSP}Sek.`);
117
136
  });
118
137
 
119
138
  it('uses decimal comma with en-XA test locale', () => {
@@ -124,7 +143,7 @@ describe('util helpers', () => {
124
143
  assert.strictEqual(i18n.formatNumber(number), '12.346,9');
125
144
  assert.strictEqual(i18n.formatBytesToKiB(number), `12,1${NBSP}KiB`);
126
145
  assert.strictEqual(i18n.formatMilliseconds(number), `12.350${NBSP}ms`);
127
- assert.strictEqual(i18n.formatSeconds(number), `12,3${NBSP}s`);
146
+ assert.strictEqual(i18n.formatSeconds(number), `12,3${NBSP}Sek.`);
128
147
  });
129
148
 
130
149
  it('should not crash on unknown locales', () => {
@@ -56,7 +56,7 @@ describe('util helpers', () => {
56
56
  });
57
57
 
58
58
  // eslint-disable-next-line max-len
59
- assert.equal(descriptions.networkThrottling, '565\xa0ms HTTP RTT, 1,400\xa0Kbps down, 600\xa0Kbps up (DevTools)');
59
+ assert.equal(descriptions.networkThrottling, '565\xa0ms HTTP RTT, 1,400\xa0kb/s down, 600\xa0kb/s up (DevTools)');
60
60
  assert.equal(descriptions.cpuThrottling, '4.5x slowdown (DevTools)');
61
61
  });
62
62
 
@@ -71,7 +71,7 @@ describe('util helpers', () => {
71
71
  });
72
72
 
73
73
  // eslint-disable-next-line max-len
74
- assert.equal(descriptions.networkThrottling, '150\xa0ms TCP RTT, 1,600\xa0Kbps throughput (Simulated)');
74
+ assert.equal(descriptions.networkThrottling, '150\xa0ms TCP RTT, 1,600\xa0kb/s throughput (Simulated)');
75
75
  assert.equal(descriptions.cpuThrottling, '2x slowdown (Simulated)');
76
76
  });
77
77
 
@@ -603,7 +603,7 @@
603
603
  "message": "Use video formats for animated content"
604
604
  },
605
605
  "lighthouse-core/audits/byte-efficiency/legacy-javascript.js | description": {
606
- "message": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn More](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)"
606
+ "message": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn More](https://web.dev/publish-modern-javascript/)"
607
607
  },
608
608
  "lighthouse-core/audits/byte-efficiency/legacy-javascript.js | title": {
609
609
  "message": "Avoid serving legacy JavaScript to modern browsers"
@@ -603,7 +603,7 @@
603
603
  "message": "Ûśê v́îd́êó f̂ór̂ḿât́ŝ f́ôŕ âńîḿât́êd́ ĉón̂t́êńt̂"
604
604
  },
605
605
  "lighthouse-core/audits/byte-efficiency/legacy-javascript.js | description": {
606
- "message": "P̂ól̂ýf̂íl̂ĺŝ án̂d́ t̂ŕâńŝf́ôŕm̂ś êńâb́l̂é l̂éĝáĉý b̂ŕôẃŝér̂ś t̂ó ûśê ńêẃ Ĵáv̂áŜćr̂íp̂t́ f̂éât́ûŕêś. Ĥóŵév̂ér̂, ḿâńŷ ár̂én̂'t́ n̂éĉéŝśâŕŷ f́ôŕ m̂ód̂ér̂ń b̂ŕôẃŝér̂ś. F̂ór̂ ýôúr̂ b́ûńd̂ĺêd́ Ĵáv̂áŜćr̂íp̂t́, âd́ôṕt̂ á m̂ód̂ér̂ń ŝćr̂íp̂t́ d̂ép̂ĺôým̂én̂t́ ŝt́r̂át̂éĝý ûśîńĝ ḿôd́ûĺê/ńôḿôd́ûĺê f́êát̂úr̂é d̂ét̂éĉt́îón̂ t́ô ŕêd́ûćê t́ĥé âḿôún̂t́ ôf́ ĉód̂é ŝh́îṕp̂éd̂ t́ô ḿôd́êŕn̂ b́r̂óŵśêŕŝ, ẃĥíl̂é r̂ét̂áîńîńĝ śûṕp̂ór̂t́ f̂ór̂ ĺêǵâćŷ b́r̂óŵśêŕŝ. [Ĺêár̂ń M̂ór̂é](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)"
606
+ "message": "P̂ól̂ýf̂íl̂ĺŝ án̂d́ t̂ŕâńŝf́ôŕm̂ś êńâb́l̂é l̂éĝáĉý b̂ŕôẃŝér̂ś t̂ó ûśê ńêẃ Ĵáv̂áŜćr̂íp̂t́ f̂éât́ûŕêś. Ĥóŵév̂ér̂, ḿâńŷ ár̂én̂'t́ n̂éĉéŝśâŕŷ f́ôŕ m̂ód̂ér̂ń b̂ŕôẃŝér̂ś. F̂ór̂ ýôúr̂ b́ûńd̂ĺêd́ Ĵáv̂áŜćr̂íp̂t́, âd́ôṕt̂ á m̂ód̂ér̂ń ŝćr̂íp̂t́ d̂ép̂ĺôým̂én̂t́ ŝt́r̂át̂éĝý ûśîńĝ ḿôd́ûĺê/ńôḿôd́ûĺê f́êát̂úr̂é d̂ét̂éĉt́îón̂ t́ô ŕêd́ûćê t́ĥé âḿôún̂t́ ôf́ ĉód̂é ŝh́îṕp̂éd̂ t́ô ḿôd́êŕn̂ b́r̂óŵśêŕŝ, ẃĥíl̂é r̂ét̂áîńîńĝ śûṕp̂ór̂t́ f̂ór̂ ĺêǵâćŷ b́r̂óŵśêŕŝ. [Ĺêár̂ń M̂ór̂é](https://web.dev/publish-modern-javascript/)"
607
607
  },
608
608
  "lighthouse-core/audits/byte-efficiency/legacy-javascript.js | title": {
609
609
  "message": "Âv́ôíd̂ śêŕv̂ín̂ǵ l̂éĝáĉý Ĵáv̂áŜćr̂íp̂t́ t̂ó m̂ód̂ér̂ń b̂ŕôẃŝér̂ś"
@@ -116,14 +116,14 @@
116
116
  {"id":"SNYK-JS-LODASH-1040724","severity":"high","semver":{"vulnerable":["<4.17.21"]}},
117
117
  {"id":"SNYK-JS-LODASH-1018905","severity":"medium","semver":{"vulnerable":["<4.17.21"]}},
118
118
  {"id":"SNYK-JS-LODASH-608086","severity":"high","semver":{"vulnerable":["<4.17.17"]}},
119
- {"id":"SNYK-JS-LODASH-590103","severity":"critical","semver":{"vulnerable":["<4.17.20"]}},
120
- {"id":"SNYK-JS-LODASH-567746","severity":"medium","semver":{"vulnerable":["<4.17.16"]}},
119
+ {"id":"SNYK-JS-LODASH-567746","severity":"high","semver":{"vulnerable":["<4.17.20"]}},
121
120
  {"id":"SNYK-JS-LODASH-450202","severity":"high","semver":{"vulnerable":["<4.17.12"]}},
122
121
  {"id":"SNYK-JS-LODASH-73639","severity":"medium","semver":{"vulnerable":["<4.17.11"]}},
123
122
  {"id":"SNYK-JS-LODASH-73638","severity":"high","semver":{"vulnerable":["<4.17.11"]}},
124
123
  {"id":"npm:lodash:20180130","severity":"medium","semver":{"vulnerable":["<4.17.5"]}}
125
124
  ],
126
125
  "moment":[
126
+ {"id":"SNYK-JS-MOMENT-2440688","severity":"high","semver":{"vulnerable":["<2.29.2"]}},
127
127
  {"id":"npm:moment:20170905","severity":"low","semver":{"vulnerable":["<2.19.3"]}},
128
128
  {"id":"npm:moment:20161019","severity":"medium","semver":{"vulnerable":["<2.15.2"]}},
129
129
  {"id":"npm:moment:20160126","severity":"medium","semver":{"vulnerable":["<2.11.2"]}}
package/types/config.d.ts CHANGED
@@ -66,6 +66,8 @@ declare module Config {
66
66
  settingsOverrides?: SharedFlagsSettings & Pick<LH.Flags, 'plugins'>;
67
67
  skipAboutBlank?: boolean;
68
68
  logLevel?: string;
69
+ hostname?: string;
70
+ port?: number;
69
71
  }
70
72
 
71
73
  interface SharedPassNavigationJson {