lighthouse 10.0.2 → 10.1.0-dev.20230323
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/cli/bin.js +1 -1
- package/cli/run.js +1 -1
- package/cli/test/smokehouse/config/exclusions.js +0 -2
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +1 -1
- package/core/audits/audit.d.ts +6 -0
- package/core/audits/audit.js +16 -4
- package/core/audits/bootup-time.js +2 -3
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +4 -0
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +8 -2
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +5 -0
- package/core/audits/byte-efficiency/legacy-javascript.js +42 -16
- package/core/audits/byte-efficiency/polyfill-graph-data.json +50 -50
- package/core/audits/byte-efficiency/total-byte-weight.js +1 -1
- package/core/audits/byte-efficiency/unminified-javascript.js +2 -1
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +2 -2
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +4 -0
- package/core/audits/dobetterweb/doctype.js +3 -2
- package/core/audits/long-tasks.js +2 -1
- package/core/audits/mainthread-work-breakdown.js +2 -1
- package/core/audits/network-rtt.js +1 -1
- package/core/audits/network-server-latency.js +2 -1
- package/core/audits/performance-budget.js +1 -1
- package/core/audits/prioritize-lcp-image.js +1 -1
- package/core/audits/seo/tap-targets.js +1 -1
- package/core/audits/third-party-summary.js +4 -1
- package/core/audits/timing-budget.js +2 -1
- package/core/audits/uses-rel-preconnect.js +1 -1
- package/core/audits/uses-rel-preload.js +1 -1
- package/core/audits/valid-source-maps.js +1 -1
- package/core/audits/work-during-interaction.js +1 -1
- package/core/computed/metrics/cumulative-layout-shift.d.ts +3 -3
- package/core/computed/metrics/cumulative-layout-shift.js +21 -9
- package/core/computed/unused-css.js +8 -7
- package/core/config/lr-mobile-config.js +0 -11
- package/core/gather/driver/prepare.js +15 -0
- package/core/gather/gatherers/image-elements.js +1 -0
- package/core/gather/gatherers/link-elements.js +2 -0
- package/core/lib/asset-saver.js +2 -12
- package/core/lib/i18n/i18n.d.ts +1 -1
- package/core/lib/i18n/i18n.js +1 -1
- package/core/lib/lighthouse-compatibility.js +17 -0
- package/core/lib/network-request.js +1 -1
- package/core/lib/page-functions.d.ts +17 -5
- package/core/lib/page-functions.js +41 -22
- package/core/lib/tracehouse/trace-processor.d.ts +8 -0
- package/core/lib/tracehouse/trace-processor.js +27 -6
- package/core/lib/url-utils.js +1 -1
- package/core/runner.js +1 -0
- package/dist/report/bundle.esm.js +258 -45
- package/dist/report/flow.js +18 -18
- package/dist/report/standalone.js +17 -17
- package/flow-report/src/i18n/i18n.d.ts +6 -0
- package/flow-report/src/summary/category.tsx +0 -1
- package/package.json +6 -7
- package/report/assets/styles.css +89 -5
- package/report/renderer/components.js +1 -1
- package/report/renderer/details-renderer.d.ts +26 -6
- package/report/renderer/details-renderer.js +144 -21
- package/report/renderer/performance-category-renderer.js +3 -3
- package/report/renderer/report-renderer.js +1 -0
- package/report/renderer/report-ui-features.js +18 -9
- package/report/renderer/report-utils.d.ts +13 -4
- package/report/renderer/report-utils.js +43 -11
- package/shared/localization/locales/ar-XB.json +1 -1
- package/shared/localization/locales/ar.json +1 -1
- package/shared/localization/locales/bg.json +1 -1
- package/shared/localization/locales/ca.json +1 -1
- package/shared/localization/locales/cs.json +1 -1
- package/shared/localization/locales/da.json +1 -1
- package/shared/localization/locales/de.json +1 -1
- package/shared/localization/locales/el.json +1 -1
- package/shared/localization/locales/en-GB.json +1 -1
- package/shared/localization/locales/en-US.json +11 -2
- package/shared/localization/locales/en-XA.json +1 -1
- package/shared/localization/locales/en-XL.json +11 -2
- package/shared/localization/locales/es-419.json +1 -1
- package/shared/localization/locales/es.json +1 -1
- package/shared/localization/locales/fi.json +1 -1
- package/shared/localization/locales/fil.json +1 -1
- package/shared/localization/locales/fr.json +1 -1
- package/shared/localization/locales/he.json +1 -1
- package/shared/localization/locales/hi.json +1 -1
- package/shared/localization/locales/hr.json +1 -1
- package/shared/localization/locales/hu.json +1 -1
- package/shared/localization/locales/id.json +1 -1
- package/shared/localization/locales/it.json +1 -1
- package/shared/localization/locales/ja.json +1 -1
- package/shared/localization/locales/ko.json +1 -1
- package/shared/localization/locales/lt.json +1 -1
- package/shared/localization/locales/lv.json +1 -1
- package/shared/localization/locales/nl.json +1 -1
- package/shared/localization/locales/no.json +1 -1
- package/shared/localization/locales/pl.json +1 -1
- package/shared/localization/locales/pt-PT.json +1 -1
- package/shared/localization/locales/pt.json +1 -1
- package/shared/localization/locales/ro.json +1 -1
- package/shared/localization/locales/ru.json +1 -1
- package/shared/localization/locales/sk.json +1 -1
- package/shared/localization/locales/sl.json +1 -1
- package/shared/localization/locales/sr-Latn.json +1 -1
- package/shared/localization/locales/sr.json +1 -1
- package/shared/localization/locales/sv.json +1 -1
- package/shared/localization/locales/ta.json +1 -1
- package/shared/localization/locales/te.json +1 -1
- package/shared/localization/locales/th.json +1 -1
- package/shared/localization/locales/tr.json +1 -1
- package/shared/localization/locales/uk.json +1 -1
- package/shared/localization/locales/vi.json +1 -1
- package/shared/localization/locales/zh-HK.json +1 -1
- package/shared/localization/locales/zh-TW.json +1 -1
- package/shared/localization/locales/zh.json +1 -1
- package/shared/tsconfig.json +1 -1
- package/shared/util.d.ts +6 -0
- package/shared/util.js +33 -0
- package/types/artifacts.d.ts +6 -1
- package/types/internal/node.d.ts +15 -2
- package/types/lhr/audit-details.d.ts +35 -11
|
@@ -24,22 +24,26 @@
|
|
|
24
24
|
/** @typedef {LH.FormattedIcu<LH.Audit.Details.TableItem>} TableItem */
|
|
25
25
|
/** @typedef {LH.FormattedIcu<LH.Audit.Details.ItemValue>} TableItemValue */
|
|
26
26
|
/** @typedef {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading>} TableColumnHeading */
|
|
27
|
+
/** @typedef {LH.FormattedIcu<LH.Audit.Details.Table | LH.Audit.Details.Opportunity>} TableLike */
|
|
27
28
|
|
|
28
29
|
import {Util} from '../../shared/util.js';
|
|
29
30
|
import {CriticalRequestChainRenderer} from './crc-details-renderer.js';
|
|
30
31
|
import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
|
|
31
32
|
import {Globals} from './report-globals.js';
|
|
33
|
+
import {ReportUtils} from './report-utils.js';
|
|
32
34
|
|
|
33
35
|
const URL_PREFIXES = ['http://', 'https://', 'data:'];
|
|
36
|
+
const SUMMABLE_VALUETYPES = ['bytes', 'numeric', 'ms', 'timespanMs'];
|
|
34
37
|
|
|
35
38
|
export class DetailsRenderer {
|
|
36
39
|
/**
|
|
37
40
|
* @param {DOM} dom
|
|
38
|
-
* @param {{fullPageScreenshot?: LH.Result.FullPageScreenshot}} [options]
|
|
41
|
+
* @param {{fullPageScreenshot?: LH.Result.FullPageScreenshot, entities?: LH.Result.Entities}} [options]
|
|
39
42
|
*/
|
|
40
43
|
constructor(dom, options = {}) {
|
|
41
44
|
this._dom = dom;
|
|
42
45
|
this._fullPageScreenshot = options.fullPageScreenshot;
|
|
46
|
+
this._entities = options.entities;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
/**
|
|
@@ -376,7 +380,110 @@ export class DetailsRenderer {
|
|
|
376
380
|
}
|
|
377
381
|
|
|
378
382
|
/**
|
|
379
|
-
*
|
|
383
|
+
* Adorn a table row element with entity chips based on [data-entity] attribute.
|
|
384
|
+
* @param {HTMLTableRowElement} rowEl
|
|
385
|
+
*/
|
|
386
|
+
_adornEntityGroupRow(rowEl) {
|
|
387
|
+
const entityName = rowEl.dataset.entity;
|
|
388
|
+
if (!entityName) return;
|
|
389
|
+
const matchedEntity = this._entities?.find(e => e.name === entityName);
|
|
390
|
+
if (!matchedEntity) return;
|
|
391
|
+
|
|
392
|
+
const firstTdEl = this._dom.find('td', rowEl);
|
|
393
|
+
|
|
394
|
+
if (matchedEntity.category) {
|
|
395
|
+
const categoryChipEl = this._dom.createElement('span');
|
|
396
|
+
categoryChipEl.classList.add('lh-audit__adorn');
|
|
397
|
+
categoryChipEl.textContent = matchedEntity.category;
|
|
398
|
+
firstTdEl.append(' ', categoryChipEl);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (matchedEntity.isFirstParty) {
|
|
402
|
+
const firstPartyChipEl = this._dom.createElement('span');
|
|
403
|
+
firstPartyChipEl.classList.add('lh-audit__adorn', 'lh-audit__adorn1p');
|
|
404
|
+
firstPartyChipEl.textContent = Globals.strings.firstPartyChipLabel;
|
|
405
|
+
firstTdEl.append(' ', firstPartyChipEl);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (matchedEntity.homepage) {
|
|
409
|
+
const entityLinkEl = this._dom.createElement('a');
|
|
410
|
+
entityLinkEl.href = matchedEntity.homepage;
|
|
411
|
+
entityLinkEl.target = '_blank';
|
|
412
|
+
entityLinkEl.title = Globals.strings.openInANewTabTooltip;
|
|
413
|
+
entityLinkEl.classList.add('lh-report-icon--external');
|
|
414
|
+
firstTdEl.append(' ', entityLinkEl);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Renders an entity-grouped row.
|
|
420
|
+
* @param {TableItem} item
|
|
421
|
+
* @param {LH.Audit.Details.TableColumnHeading[]} headings
|
|
422
|
+
*/
|
|
423
|
+
_renderEntityGroupRow(item, headings) {
|
|
424
|
+
const entityColumnHeading = {...headings[0]};
|
|
425
|
+
// In subitem-situations (unused-javascript), ensure Entity name is not rendered as code, etc.
|
|
426
|
+
entityColumnHeading.valueType = 'text';
|
|
427
|
+
const groupedRowHeadings = [entityColumnHeading, ...headings.slice(1)];
|
|
428
|
+
const fragment = this._dom.createFragment();
|
|
429
|
+
fragment.append(this._renderTableRow(item, groupedRowHeadings));
|
|
430
|
+
this._dom.find('tr', fragment).classList.add('lh-row--group');
|
|
431
|
+
return fragment;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Returns an array of entity-grouped TableItems to use as the top-level rows in
|
|
436
|
+
* an grouped table. Each table item returned represents a unique entity, with every
|
|
437
|
+
* applicable key that can be grouped as a property. Optionally, supported columns are
|
|
438
|
+
* summed by entity, and sorted by specified keys.
|
|
439
|
+
* @param {TableLike} details
|
|
440
|
+
* @return {TableItem[]}
|
|
441
|
+
*/
|
|
442
|
+
_getEntityGroupItems(details) {
|
|
443
|
+
const {items, headings, sortedBy} = details;
|
|
444
|
+
// Exclude entity-grouped audits and results without entity classification.
|
|
445
|
+
// Eg. Third-party Summary comes entity-grouped.
|
|
446
|
+
if (!items.length || details.isEntityGrouped || !items.some(item => item.entity)) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const skippedColumns = new Set(details.skipSumming || []);
|
|
451
|
+
/** @type {string[]} */
|
|
452
|
+
const summableColumns = [];
|
|
453
|
+
for (const heading of headings) {
|
|
454
|
+
if (!heading.key || skippedColumns.has(heading.key)) continue;
|
|
455
|
+
if (SUMMABLE_VALUETYPES.includes(heading.valueType)) {
|
|
456
|
+
summableColumns.push(heading.key);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Grab the first column's key to group by entity
|
|
461
|
+
const firstColumnKey = headings[0].key;
|
|
462
|
+
if (!firstColumnKey) return [];
|
|
463
|
+
|
|
464
|
+
/** @type {Map<string | undefined, TableItem>} */
|
|
465
|
+
const byEntity = new Map();
|
|
466
|
+
for (const item of items) {
|
|
467
|
+
const entityName = typeof item.entity === 'string' ? item.entity : undefined;
|
|
468
|
+
const groupedItem = byEntity.get(entityName) || {
|
|
469
|
+
[firstColumnKey]: entityName || Globals.strings.unattributable,
|
|
470
|
+
entity: entityName,
|
|
471
|
+
};
|
|
472
|
+
for (const key of summableColumns) {
|
|
473
|
+
groupedItem[key] = Number(groupedItem[key] || 0) + Number(item[key] || 0);
|
|
474
|
+
}
|
|
475
|
+
byEntity.set(entityName, groupedItem);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const result = [...byEntity.values()];
|
|
479
|
+
if (sortedBy) {
|
|
480
|
+
result.sort(ReportUtils.getTableItemSortComparator(sortedBy));
|
|
481
|
+
}
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* @param {TableLike} details
|
|
380
487
|
* @return {Element}
|
|
381
488
|
*/
|
|
382
489
|
_renderTable(details) {
|
|
@@ -394,29 +501,45 @@ export class DetailsRenderer {
|
|
|
394
501
|
this._dom.createChildOf(theadTrElem, 'th', classes).append(labelEl);
|
|
395
502
|
}
|
|
396
503
|
|
|
504
|
+
const entityItems = this._getEntityGroupItems(details);
|
|
397
505
|
const tbodyElem = this._dom.createChildOf(tableElem, 'tbody');
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
506
|
+
if (entityItems.length) {
|
|
507
|
+
for (const entityItem of entityItems) {
|
|
508
|
+
const entityName = typeof entityItem.entity === 'string' ? entityItem.entity : undefined;
|
|
509
|
+
const entityGroupFragment = this._renderEntityGroupRow(entityItem, details.headings);
|
|
510
|
+
// Render all the items that match the heading row
|
|
511
|
+
for (const item of details.items.filter((item) => item.entity === entityName)) {
|
|
512
|
+
entityGroupFragment.append(this._renderTableRowsFromItem(item, details.headings));
|
|
513
|
+
}
|
|
514
|
+
const rowEls = this._dom.findAll('tr', entityGroupFragment);
|
|
515
|
+
if (entityName && rowEls.length) {
|
|
516
|
+
rowEls.forEach(row => row.dataset.entity = entityName);
|
|
517
|
+
this._adornEntityGroupRow(rowEls[0]);
|
|
518
|
+
}
|
|
519
|
+
tbodyElem.append(entityGroupFragment);
|
|
409
520
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
521
|
+
} else {
|
|
522
|
+
let even = true;
|
|
523
|
+
for (const item of details.items) {
|
|
524
|
+
const rowsFragment = this._renderTableRowsFromItem(item, details.headings);
|
|
525
|
+
const rowEls = this._dom.findAll('tr', rowsFragment);
|
|
526
|
+
const firstRowEl = rowEls[0];
|
|
527
|
+
if (typeof item.entity === 'string') {
|
|
528
|
+
firstRowEl.dataset.entity = item.entity;
|
|
529
|
+
}
|
|
530
|
+
if (details.isEntityGrouped && item.entity) {
|
|
531
|
+
// If the audit is already grouped, consider first row as a heading row.
|
|
532
|
+
firstRowEl.classList.add('lh-row--group');
|
|
533
|
+
this._adornEntityGroupRow(firstRowEl);
|
|
534
|
+
} else {
|
|
535
|
+
for (const rowEl of rowEls) {
|
|
536
|
+
// For zebra styling (same shade for a row and its sub-rows).
|
|
537
|
+
rowEl.classList.add(even ? 'lh-row--even' : 'lh-row--odd');
|
|
538
|
+
}
|
|
416
539
|
}
|
|
540
|
+
even = !even;
|
|
541
|
+
tbodyElem.append(rowsFragment);
|
|
417
542
|
}
|
|
418
|
-
even = !even;
|
|
419
|
-
tbodyElem.append(rowsFragment);
|
|
420
543
|
}
|
|
421
544
|
|
|
422
545
|
return tableElem;
|
|
@@ -68,7 +68,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
68
68
|
return element;
|
|
69
69
|
}
|
|
70
70
|
const details = audit.result.details;
|
|
71
|
-
if (details.
|
|
71
|
+
if (details.overallSavingsMs === undefined) {
|
|
72
72
|
return element;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -98,7 +98,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
98
98
|
* @return {number}
|
|
99
99
|
*/
|
|
100
100
|
_getWastedMs(audit) {
|
|
101
|
-
if (audit.result.details
|
|
101
|
+
if (audit.result.details) {
|
|
102
102
|
const details = audit.result.details;
|
|
103
103
|
if (typeof details.overallSavingsMs !== 'number') {
|
|
104
104
|
throw new Error('non-opportunity details passed to _getWastedMs');
|
|
@@ -165,7 +165,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
165
165
|
*/
|
|
166
166
|
_classifyPerformanceAudit(audit) {
|
|
167
167
|
if (audit.group) return null;
|
|
168
|
-
if (audit.result.details
|
|
168
|
+
if (audit.result.details?.overallSavingsMs !== undefined) {
|
|
169
169
|
return 'load-opportunity';
|
|
170
170
|
}
|
|
171
171
|
return 'diagnostic';
|
|
@@ -268,6 +268,7 @@ export class ReportRenderer {
|
|
|
268
268
|
|
|
269
269
|
const detailsRenderer = new DetailsRenderer(this._dom, {
|
|
270
270
|
fullPageScreenshot: report.fullPageScreenshot ?? undefined,
|
|
271
|
+
entities: report.entities,
|
|
271
272
|
});
|
|
272
273
|
|
|
273
274
|
const categoryRenderer = new CategoryRenderer(this._dom, detailsRenderer);
|
|
@@ -244,9 +244,11 @@ export class ReportUIFeatures {
|
|
|
244
244
|
|
|
245
245
|
tablesWithUrls.forEach((tableEl) => {
|
|
246
246
|
const rowEls = getTableRows(tableEl);
|
|
247
|
-
const
|
|
248
|
-
const thirdPartyRowEls = this._getThirdPartyRows(
|
|
249
|
-
|
|
247
|
+
const nonSubItemRows = rowEls.filter(rowEl => !rowEl.classList.contains('lh-sub-item-row'));
|
|
248
|
+
const thirdPartyRowEls = this._getThirdPartyRows(nonSubItemRows,
|
|
249
|
+
Util.getFinalDisplayedUrl(this.json));
|
|
250
|
+
// Entity-grouped tables don't have zebra lines.
|
|
251
|
+
const hasZebraStyle = rowEls.some(rowEl => rowEl.classList.contains('lh-row--even'));
|
|
250
252
|
|
|
251
253
|
// create input box
|
|
252
254
|
const filterTemplate = this._dom.createComponent('3pFilter');
|
|
@@ -255,16 +257,19 @@ export class ReportUIFeatures {
|
|
|
255
257
|
filterInput.addEventListener('change', e => {
|
|
256
258
|
const shouldHideThirdParty = e.target instanceof HTMLInputElement && !e.target.checked;
|
|
257
259
|
let even = true;
|
|
258
|
-
let rowEl =
|
|
260
|
+
let rowEl = nonSubItemRows[0];
|
|
259
261
|
while (rowEl) {
|
|
260
262
|
const shouldHide = shouldHideThirdParty && thirdPartyRowEls.includes(rowEl);
|
|
261
263
|
|
|
262
264
|
// Iterate subsequent associated sub item rows.
|
|
263
265
|
do {
|
|
264
266
|
rowEl.classList.toggle('lh-row--hidden', shouldHide);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
|
|
268
|
+
if (hasZebraStyle) {
|
|
269
|
+
// Adjust for zebra styling.
|
|
270
|
+
rowEl.classList.toggle('lh-row--even', !shouldHide && even);
|
|
271
|
+
rowEl.classList.toggle('lh-row--odd', !shouldHide && !even);
|
|
272
|
+
}
|
|
268
273
|
|
|
269
274
|
rowEl = /** @type {HTMLElement} */ (rowEl.nextElementSibling);
|
|
270
275
|
} while (rowEl && rowEl.classList.contains('lh-sub-item-row'));
|
|
@@ -273,12 +278,16 @@ export class ReportUIFeatures {
|
|
|
273
278
|
}
|
|
274
279
|
});
|
|
275
280
|
|
|
281
|
+
// thirdPartyRowEls contains both heading and item rows.
|
|
282
|
+
// Filter out heading rows to get third party resource count.
|
|
283
|
+
const thirdPartyResourceCount = thirdPartyRowEls.filter(
|
|
284
|
+
rowEl => !rowEl.classList.contains('lh-row--group')).length;
|
|
276
285
|
this._dom.find('.lh-3p-filter-count', filterTemplate).textContent =
|
|
277
|
-
|
|
286
|
+
`${thirdPartyResourceCount}`;
|
|
278
287
|
this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =
|
|
279
288
|
Globals.strings.thirdPartyResourcesLabel;
|
|
280
289
|
|
|
281
|
-
const allThirdParty = thirdPartyRowEls.length ===
|
|
290
|
+
const allThirdParty = thirdPartyRowEls.length === nonSubItemRows.length;
|
|
282
291
|
const allFirstParty = !thirdPartyRowEls.length;
|
|
283
292
|
|
|
284
293
|
// If all or none of the rows are 3rd party, hide the control.
|
|
@@ -13,15 +13,21 @@ export class ReportUtils {
|
|
|
13
13
|
* Given an audit's details, identify and return a URL locator function that
|
|
14
14
|
* can be called later with an `item` to extract the URL of it.
|
|
15
15
|
* @param {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>} headings
|
|
16
|
-
* @return {
|
|
16
|
+
* @return {((item: LH.FormattedIcu<LH.Audit.Details.TableItem>) => string|undefined)=}
|
|
17
17
|
*/
|
|
18
18
|
static getUrlLocatorFn(headings: LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>): ((item: LH.FormattedIcu<LH.Audit.Details.TableItem>) => string | undefined) | undefined;
|
|
19
19
|
/**
|
|
20
20
|
* Mark TableItems/OpportunityItems with entity names.
|
|
21
|
-
* @param {LH.Result.Entities
|
|
22
|
-
* @param {
|
|
21
|
+
* @param {LH.Result.Entities} entities
|
|
22
|
+
* @param {LH.FormattedIcu<LH.Audit.Details.Opportunity|LH.Audit.Details.Table>} details
|
|
23
23
|
*/
|
|
24
|
-
static classifyEntities(entities: LH.Result.Entities
|
|
24
|
+
static classifyEntities(entities: LH.Result.Entities, details: LH.FormattedIcu<LH.Audit.Details.Opportunity | LH.Audit.Details.Table>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Returns a comparator created from the supplied list of keys
|
|
27
|
+
* @param {Array<string>} sortedBy
|
|
28
|
+
* @return {((a: LH.Audit.Details.TableItem, b: LH.Audit.Details.TableItem) => number)}
|
|
29
|
+
*/
|
|
30
|
+
static getTableItemSortComparator(sortedBy: Array<string>): (a: LH.Audit.Details.TableItem, b: LH.Audit.Details.TableItem) => number;
|
|
25
31
|
/**
|
|
26
32
|
* @param {LH.Result['configSettings']} settings
|
|
27
33
|
* @return {!{deviceEmulation: string, screenEmulation?: string, networkThrottling: string, cpuThrottling: string, summary: string}}
|
|
@@ -125,5 +131,8 @@ export namespace UIStrings {
|
|
|
125
131
|
const collapseView: string;
|
|
126
132
|
const runtimeSlow4g: string;
|
|
127
133
|
const runtimeCustom: string;
|
|
134
|
+
const firstPartyChipLabel: string;
|
|
135
|
+
const openInANewTabTooltip: string;
|
|
136
|
+
const unattributable: string;
|
|
128
137
|
}
|
|
129
138
|
//# sourceMappingURL=report-utils.d.ts.map
|
|
@@ -27,7 +27,13 @@ class ReportUtils {
|
|
|
27
27
|
|
|
28
28
|
for (const audit of Object.values(clone.audits)) {
|
|
29
29
|
// Attach table/opportunity items with entity information.
|
|
30
|
-
|
|
30
|
+
if (audit.details) {
|
|
31
|
+
if (audit.details.type === 'opportunity' || audit.details.type === 'table') {
|
|
32
|
+
if (!audit.details.isEntityGrouped && clone.entities) {
|
|
33
|
+
ReportUtils.classifyEntities(clone.entities, audit.details);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
// For convenience, smoosh all AuditResults into their auditRef (which has just weight & group)
|
|
@@ -79,7 +85,7 @@ class ReportUtils {
|
|
|
79
85
|
* Given an audit's details, identify and return a URL locator function that
|
|
80
86
|
* can be called later with an `item` to extract the URL of it.
|
|
81
87
|
* @param {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>} headings
|
|
82
|
-
* @return {
|
|
88
|
+
* @return {((item: LH.FormattedIcu<LH.Audit.Details.TableItem>) => string|undefined)=}
|
|
83
89
|
*/
|
|
84
90
|
static getUrlLocatorFn(headings) {
|
|
85
91
|
// The most common type, valueType=url.
|
|
@@ -109,17 +115,12 @@ class ReportUtils {
|
|
|
109
115
|
|
|
110
116
|
/**
|
|
111
117
|
* Mark TableItems/OpportunityItems with entity names.
|
|
112
|
-
* @param {LH.Result.Entities
|
|
113
|
-
* @param {
|
|
118
|
+
* @param {LH.Result.Entities} entities
|
|
119
|
+
* @param {LH.FormattedIcu<LH.Audit.Details.Opportunity|LH.Audit.Details.Table>} details
|
|
114
120
|
*/
|
|
115
|
-
static classifyEntities(entities,
|
|
116
|
-
if (!entities) return;
|
|
117
|
-
if (audit.details?.type !== 'opportunity' && audit.details?.type !== 'table') {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
+
static classifyEntities(entities, details) {
|
|
121
122
|
// If details.items are already marked with entity attribute during an audit, nothing to do here.
|
|
122
|
-
const {items, headings} =
|
|
123
|
+
const {items, headings} = details;
|
|
123
124
|
if (!items.length || items.some(item => item.entity)) return;
|
|
124
125
|
|
|
125
126
|
// Identify a URL-locator function that we could call against each item to get its URL.
|
|
@@ -142,6 +143,30 @@ class ReportUtils {
|
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Returns a comparator created from the supplied list of keys
|
|
148
|
+
* @param {Array<string>} sortedBy
|
|
149
|
+
* @return {((a: LH.Audit.Details.TableItem, b: LH.Audit.Details.TableItem) => number)}
|
|
150
|
+
*/
|
|
151
|
+
static getTableItemSortComparator(sortedBy) {
|
|
152
|
+
return (a, b) => {
|
|
153
|
+
for (const key of sortedBy) {
|
|
154
|
+
const aVal = a[key];
|
|
155
|
+
const bVal = b[key];
|
|
156
|
+
if (typeof aVal !== typeof bVal || !['number', 'string'].includes(typeof aVal)) {
|
|
157
|
+
console.warn(`Warning: Attempting to sort unsupported value type: ${key}.`);
|
|
158
|
+
}
|
|
159
|
+
if (typeof aVal === 'number' && typeof bVal === 'number' && aVal !== bVal) {
|
|
160
|
+
return bVal - aVal;
|
|
161
|
+
}
|
|
162
|
+
if (typeof aVal === 'string' && typeof bVal === 'string' && aVal !== bVal) {
|
|
163
|
+
return aVal.localeCompare(bVal);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return 0;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
145
170
|
/**
|
|
146
171
|
* @param {LH.Result['configSettings']} settings
|
|
147
172
|
* @return {!{deviceEmulation: string, screenEmulation?: string, networkThrottling: string, cpuThrottling: string, summary: string}}
|
|
@@ -440,6 +465,13 @@ const UIStrings = {
|
|
|
440
465
|
runtimeSlow4g: 'Slow 4G throttling',
|
|
441
466
|
/** Label indicating that Lighthouse throttled the page using custom throttling settings. */
|
|
442
467
|
runtimeCustom: 'Custom throttling',
|
|
468
|
+
|
|
469
|
+
/** This label is for a decorative chip that is included in a table row. The label indicates that the entity/company name in the row belongs to the first-party (or "1st-party"). First-party label is used to identify resources that are directly controlled by the owner of the web page. */
|
|
470
|
+
firstPartyChipLabel: '1st party',
|
|
471
|
+
/** Descriptive explanation in a tooltip form for a link to be opened in a new tab of the browser. */
|
|
472
|
+
openInANewTabTooltip: 'Open in a new tab',
|
|
473
|
+
/** Generic category name for all resources that could not be attributed to a 1st or 3rd party entity. */
|
|
474
|
+
unattributable: 'Unattributable',
|
|
443
475
|
};
|
|
444
476
|
|
|
445
477
|
export {
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "Map URL"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Source maps translate minified code to the original source code. This helps developers debug in production. In addition, Lighthouse is able to provide further insights. Consider deploying source maps to take advantage of these benefits. [Learn more about source maps](https://
|
|
1500
|
+
"message": "Source maps translate minified code to the original source code. This helps developers debug in production. In addition, Lighthouse is able to provide further insights. Consider deploying source maps to take advantage of these benefits. [Learn more about source maps](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Missing source maps for large first-party JavaScript"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "عنوان URL لخريطة المصدر"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "تحوِّل خرائط المصدر الرمز المصغَّر إلى رمز المصدر الأصلي. ويساعد هذا الإجراء المطوّرين على تصحيح الأخطاء في مرحلة الإنتاج. ويمكن لأداة Lighthouse أيضًا أن توفّر معلومات إضافية. ننصحك بنشر خرائط المصدر للاستفادة من هذه المزايا. [مزيد من المعلومات حول خرائط المصدر](https://
|
|
1500
|
+
"message": "تحوِّل خرائط المصدر الرمز المصغَّر إلى رمز المصدر الأصلي. ويساعد هذا الإجراء المطوّرين على تصحيح الأخطاء في مرحلة الإنتاج. ويمكن لأداة Lighthouse أيضًا أن توفّر معلومات إضافية. ننصحك بنشر خرائط المصدر للاستفادة من هذه المزايا. [مزيد من المعلومات حول خرائط المصدر](https://developer.chrome.com/docs/devtools/javascript/source-maps/)"
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "عدم توفّر خرائط مصدر للغة JavaScript من الطرف الأول الكبير"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "URL адрес на карта"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Картите на изходния код преобразуват минимизирания код в оригиналния му вид. Това помага на програмистите да отстраняват грешки в стандартния канал. Също така Lighthouse може да предоставя допълнителни статистически данни. Препоръчваме да внедрите карти на изходния код, за да се възползвате от тези предимства. [Научете повече за картите](https://
|
|
1500
|
+
"message": "Картите на изходния код преобразуват минимизирания код в оригиналния му вид. Това помага на програмистите да отстраняват грешки в стандартния канал. Също така Lighthouse може да предоставя допълнителни статистически данни. Препоръчваме да внедрите карти на изходния код, за да се възползвате от тези предимства. [Научете повече за картите](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Липсват карти на изходния код за големи файлове на JavaScript"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "URL del mapa"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Els mapes d'origen tradueixen el codi reduït al codi font original. Això ajuda els desenvolupadors a depurar durant la producció. A més, Lighthouse pot proporcionar estadístiques addicionals. Planteja't implementar mapes d'origen per treure partit d'aquests avantatges. [Obtén més informació sobre els mapes d'origen](https://
|
|
1500
|
+
"message": "Els mapes d'origen tradueixen el codi reduït al codi font original. Això ajuda els desenvolupadors a depurar durant la producció. A més, Lighthouse pot proporcionar estadístiques addicionals. Planteja't implementar mapes d'origen per treure partit d'aquests avantatges. [Obtén més informació sobre els mapes d'origen](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Falten mapes d'origen per a un fitxer JavaScript propi gran"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "Adresa URL souboru sourcemap"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Soubory sourcemap překládají minifikovaný kód na původní zdrojový kód. To vývojářům usnadňuje ladění produkčních verzí. Kromě toho to nástroji Lighthouse umožňuje poskytovat více informací. Zvažte implementaci souborů sourcemap, abyste tyto výhody získali. [Další informace o souborech sourcemap](https://
|
|
1500
|
+
"message": "Soubory sourcemap překládají minifikovaný kód na původní zdrojový kód. To vývojářům usnadňuje ladění produkčních verzí. Kromě toho to nástroji Lighthouse umožňuje poskytovat více informací. Zvažte implementaci souborů sourcemap, abyste tyto výhody získali. [Další informace o souborech sourcemap](https://developer.chrome.com/docs/devtools/javascript/source-maps/)"
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Chybí soubory sourcemap pro velké soubory JavaScript první strany"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "Kortets webadresse"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Kildekort oversætter formindsket kode til den originale kildekode. Dette hjælper udviklere med at fejlrette under produktion. Derudover kan Lighthouse levere yderligere indsigter. Overvej at implementere kildekort for at drage fordel af disse. [Få flere oplysninger om kildekort](https://
|
|
1500
|
+
"message": "Kildekort oversætter formindsket kode til den originale kildekode. Dette hjælper udviklere med at fejlrette under produktion. Derudover kan Lighthouse levere yderligere indsigter. Overvej at implementere kildekort for at drage fordel af disse. [Få flere oplysninger om kildekort](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Manglende kildekort til stor førsteparts-JavaScript"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "Zuordnungs-URL"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Quellzuordnungen übersetzen reduzierten Code in den ursprünglichen Quellcode. Dies hilft Entwicklern beim Debugging in der Produktionsphase. Zusätzlich kann Lighthouse weitere Informationen liefern. Wir empfehlen, Quellzuordnungen bereitzustellen, um diese Vorteile zu nutzen. [Weitere Informationen zu Quellzuordnungen.](https://
|
|
1500
|
+
"message": "Quellzuordnungen übersetzen reduzierten Code in den ursprünglichen Quellcode. Dies hilft Entwicklern beim Debugging in der Produktionsphase. Zusätzlich kann Lighthouse weitere Informationen liefern. Wir empfehlen, Quellzuordnungen bereitzustellen, um diese Vorteile zu nutzen. [Weitere Informationen zu Quellzuordnungen.](https://developer.chrome.com/docs/devtools/javascript/source-maps/)"
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Fehlende Quellzuordnungen für große eigene JavaScript-Dateien"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "URL αντιστοίχισης"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Οι αντιστοιχίσεις πηγής μεταφράζουν ελαχιστοποιημένο κώδικα στον αρχικό πηγαίο κώδικα. Αυτό βοηθά τους προγραμματιστές με τον εντοπισμό σφαλμάτων στην παραγωγή. Επίσης, το Lighthouse μπορεί να παράσχει περαιτέρω insight. Εξετάστε το ενδεχόμενο ανάπτυξης αντιστοιχίσεων πηγών, για να αξιοποιήσετε αυτά τα οφέλη. [Μάθετε περισσότερα σχετικά με τις αντιστοιχίσεις πηγών](https://
|
|
1500
|
+
"message": "Οι αντιστοιχίσεις πηγής μεταφράζουν ελαχιστοποιημένο κώδικα στον αρχικό πηγαίο κώδικα. Αυτό βοηθά τους προγραμματιστές με τον εντοπισμό σφαλμάτων στην παραγωγή. Επίσης, το Lighthouse μπορεί να παράσχει περαιτέρω insight. Εξετάστε το ενδεχόμενο ανάπτυξης αντιστοιχίσεων πηγών, για να αξιοποιήσετε αυτά τα οφέλη. [Μάθετε περισσότερα σχετικά με τις αντιστοιχίσεις πηγών](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Λείπουν οι αντιστοιχίσεις πηγής για μεγάλες βιβλιοθήκες JavaScript πρώτου μέρους"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "Map URL"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Source maps translate minified code to the original source code. This helps developers to debug in production. In addition, Lighthouse is able to provide further insights. Consider deploying source maps to take advantage of these benefits. [Learn more about source maps](https://
|
|
1500
|
+
"message": "Source maps translate minified code to the original source code. This helps developers to debug in production. In addition, Lighthouse is able to provide further insights. Consider deploying source maps to take advantage of these benefits. [Learn more about source maps](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Missing source maps for large first-party JavaScript"
|
|
@@ -1323,7 +1323,7 @@
|
|
|
1323
1323
|
"message": "robots.txt is valid"
|
|
1324
1324
|
},
|
|
1325
1325
|
"core/audits/seo/tap-targets.js | description": {
|
|
1326
|
-
"message": "Interactive elements like buttons and links should be large enough (48x48px),
|
|
1326
|
+
"message": "Interactive elements like buttons and links should be large enough (48x48px), or have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more about tap targets](https://developer.chrome.com/docs/lighthouse/seo/tap-targets/)."
|
|
1327
1327
|
},
|
|
1328
1328
|
"core/audits/seo/tap-targets.js | displayValue": {
|
|
1329
1329
|
"message": "{decimalProportion, number, percent} appropriately sized tap targets"
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "Map URL"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "Source maps translate minified code to the original source code. This helps developers debug in production. In addition, Lighthouse is able to provide further insights. Consider deploying source maps to take advantage of these benefits. [Learn more about source maps](https://
|
|
1500
|
+
"message": "Source maps translate minified code to the original source code. This helps developers debug in production. In addition, Lighthouse is able to provide further insights. Consider deploying source maps to take advantage of these benefits. [Learn more about source maps](https://developer.chrome.com/docs/devtools/javascript/source-maps/)."
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "Missing source maps for large first-party JavaScript"
|
|
@@ -3047,6 +3047,9 @@
|
|
|
3047
3047
|
"report/renderer/report-utils.js | expandView": {
|
|
3048
3048
|
"message": "Expand view"
|
|
3049
3049
|
},
|
|
3050
|
+
"report/renderer/report-utils.js | firstPartyChipLabel": {
|
|
3051
|
+
"message": "1st party"
|
|
3052
|
+
},
|
|
3050
3053
|
"report/renderer/report-utils.js | footerIssue": {
|
|
3051
3054
|
"message": "File an issue"
|
|
3052
3055
|
},
|
|
@@ -3065,6 +3068,9 @@
|
|
|
3065
3068
|
"report/renderer/report-utils.js | notApplicableAuditsGroupTitle": {
|
|
3066
3069
|
"message": "Not applicable"
|
|
3067
3070
|
},
|
|
3071
|
+
"report/renderer/report-utils.js | openInANewTabTooltip": {
|
|
3072
|
+
"message": "Open in a new tab"
|
|
3073
|
+
},
|
|
3068
3074
|
"report/renderer/report-utils.js | opportunityResourceColumnLabel": {
|
|
3069
3075
|
"message": "Opportunity"
|
|
3070
3076
|
},
|
|
@@ -3143,6 +3149,9 @@
|
|
|
3143
3149
|
"report/renderer/report-utils.js | toplevelWarningsMessage": {
|
|
3144
3150
|
"message": "There were issues affecting this run of Lighthouse:"
|
|
3145
3151
|
},
|
|
3152
|
+
"report/renderer/report-utils.js | unattributable": {
|
|
3153
|
+
"message": "Unattributable"
|
|
3154
|
+
},
|
|
3146
3155
|
"report/renderer/report-utils.js | varianceDisclaimer": {
|
|
3147
3156
|
"message": "Values are estimated and may vary. The [performance score is calculated](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) directly from these metrics."
|
|
3148
3157
|
},
|
|
@@ -1497,7 +1497,7 @@
|
|
|
1497
1497
|
"message": "[Måþ ÛŔĻ one]"
|
|
1498
1498
|
},
|
|
1499
1499
|
"core/audits/valid-source-maps.js | description": {
|
|
1500
|
-
"message": "[Šöûŕçé måþš ţŕåñšļåţé mîñîƒîéð çöðé ţö ţĥé öŕîĝîñåļ šöûŕçé çöðé. Ţĥîš ĥéļþš ðévéļöþéŕš ðéбûĝ îñ þŕöðûçţîöñ. Îñ åððîţîöñ, Ļîĝĥţĥöûšé îš åбļé ţö þŕövîðé ƒûŕţĥéŕ îñšîĝĥţš. Çöñšîðéŕ ðéþļöýîñĝ šöûŕçé måþš ţö ţåķé åðvåñţåĝé öƒ ţĥéšé бéñéƒîţš. ᐅ[ᐊĻéåŕñ möŕé åбöûţ šöûŕçé måþšᐅ](https://
|
|
1500
|
+
"message": "[Šöûŕçé måþš ţŕåñšļåţé mîñîƒîéð çöðé ţö ţĥé öŕîĝîñåļ šöûŕçé çöðé. Ţĥîš ĥéļþš ðévéļöþéŕš ðéбûĝ îñ þŕöðûçţîöñ. Îñ åððîţîöñ, Ļîĝĥţĥöûšé îš åбļé ţö þŕövîðé ƒûŕţĥéŕ îñšîĝĥţš. Çöñšîðéŕ ðéþļöýîñĝ šöûŕçé måþš ţö ţåķé åðvåñţåĝé öƒ ţĥéšé бéñéƒîţš. ᐅ[ᐊĻéåŕñ möŕé åбöûţ šöûŕçé måþšᐅ](https://developer.chrome.com/docs/devtools/javascript/source-maps/)ᐊ. one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty twentyone twentytwo twentythree twentyfour twentyfive twentysix twentyseven twentyeight twentynine thirty thirtyone thirtytwo thirtythree thirtyfour]"
|
|
1501
1501
|
},
|
|
1502
1502
|
"core/audits/valid-source-maps.js | failureTitle": {
|
|
1503
1503
|
"message": "[Mîššîñĝ šöûŕçé måþš ƒöŕ ļåŕĝé ƒîŕšţ-þåŕţý ĴåvåŠçŕîþţ one two three four five six seven eight nine ten eleven]"
|