-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
core(lhr): overhaul LHR details, introduce details.summary #4616
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 ❤️ love the overall direction this is headed, I'd really like to nail down what details can look like in the new world and start documenting it as we go
Is this what we currently have?
interface AuditDetails {
summary?: AuditDetailsSummary
items?: AuditDetailsItem[]
headings?: AuditTableHeading[]
// ???
}
interface AuditDetailsItem {
[key: string]: LegacyDetails | string | number
}
interface LegacyDetails {
type: string
value: string | number
granularity?: string
displayUnit?: string
}
interface AuditDetailsSummary {
displayValue?: LegacyDetails
wastedMs?: number // I might prefer sticking these two into a `performanceOpportunity` object or something, but we never really came up with other uses thus far either...
wastedKb?: number
// ???
}
interface AuditTableHeading extends LegacyDetails {
key: string
text: string
}
@@ -315,7 +337,7 @@ class DetailsRenderer { | |||
*/ | |||
_renderCode(details) { | |||
const pre = this._dom.createElement('pre', 'lh-code'); | |||
pre.textContent = details.text; | |||
pre.textContent = details.text || details.value; // TODO, pick a winner |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for value
return this._dom.createElement('span'); | ||
} | ||
_renderThumbnail(details) { | ||
// TODO: use some empty details test instead? to handle data uris? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah let's strip out all data URIs at the audit base-class, they can make the report super beefy anyway
const extendedInfo = /** @type {!PerformanceCategoryRenderer.PerfHintExtendedInfo} | ||
*/ (audit.result.extendedInfo); | ||
const summaryInfo = /** @type {!PerformanceCategoryRenderer.PerfHintSummaryInfo} | ||
*/ (audit.result.summary); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something, I thought we were nesting summary
inside of details
?
Wow, big amount of work! Thanks for noticing 👍 |
Yes, that looks mostly accurate. I'm almost done with typechecking the JSON against an iteration of these interfaces. ;) In the case of PSI audits we have an even simpler details story: interface AuditDetails {
summary?: AuditDetailsSummary // could be also be a sibling to details..
headings?: AuditTableHeading[]
items?: AuditDetailsItem[]
}
interface AuditDetailsSummary {
wastedMs?: number // maybe these two within a `performanceOpportunity` object?
wastedKb?: number
}
interface AuditTableHeading {
key: string
text: string
itemType: string
granularity?: string
displayUnit?: string
}
interface AuditDetailsItem {
[key: string]: string | number
} |
@patrickhulce this is ready for review. |
@@ -18,7 +18,7 @@ const IGNORE_THRESHOLD_IN_PERCENT = 0.925; | |||
const SCORING_POINT_OF_DIMINISHING_RETURNS = 4; // 4 KB | |||
const SCORING_MEDIAN = 768; // 768 KB | |||
|
|||
class CacheHeaders extends ByteEfficiencyAudit { | |||
class CacheHeaders { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paulirish let's make sure this extends audit
@@ -133,11 +137,16 @@ class DetailsRenderer { | |||
* @return {!Element} | |||
*/ | |||
_renderLink(details) { | |||
console.assert(('text' in details), 'link details must have .url'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like this should be 'url' in details
?
for (const heading of details.headings) { | ||
const value = /** @type {number|string|!DetailsRenderer.DetailsJSON} */ (row[heading.key]); | ||
if (typeof value === 'undefined') { | ||
continue; // e.g. no lineNumber in this row item |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't we still want to create a td element even if it's empty?
lighthouse-core/runner.js
Outdated
@@ -93,9 +93,10 @@ class Runner { | |||
const resultsById = {}; | |||
for (const audit of runResults.auditResults) resultsById[audit.name] = audit; | |||
|
|||
let report; | |||
let cats; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
27f425c
to
c0658ec
Compare
updated and squashed (to resolve silly conflicts) |
fa7407c
to
7486a4a
Compare
…w summary * completely new shape of `details`: * `result.details.headings` describes the expected report columns * `result.details.items` contains row information, and may include _more_ data than referenced by the headings. it's the new `extendedInfo`. :) * new `result.details.summary` obj that includes whatever toplevel numeric summary we have. So far just `wastedMs` and `wastedKb` * `reportCategories` not contain real auditResults (aka _de-dupe all audit results_ from #4333) * `reportCategories.performance` keeps it's `score` prop * each audit's score moves from `reportCategories.performance['audit-name']` (which now just holds `weight` and `group`) over to `audits['audit-name']` * removes `LHR.score`, the overallScore
@patrickhulce lgty? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I say merge away! Never a better time for the risky business than now 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to take out most of these runtime details checks and rely on the type checker(s)? e.g. at DetailsRenderer.render
, if type
is text
, we should be certain that it's a StringDetailsJSON
at that point. We can do some of this by checking the output of our audits, but some of it should fall out naturally…
also, it looks like we won’t be able to non-awkwardly be able to get read of text.text
since so many other details types still include a text
?
lighthouse-core/audits/audit.js
Outdated
@@ -141,20 +114,35 @@ class Audit { | |||
if (displayValue === score) { | |||
displayValue = ''; | |||
} | |||
|
|||
// TODO: restore after initial 3.0 branching |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete?
lighthouse-core/audits/audit.js
Outdated
|
||
// TODO, don't consider an auditResult's scoringMode (currently applied to all ByteEfficiency) | ||
const scoringMode = result.scoringMode || audit.meta.scoringMode || Audit.SCORING_MODES.BINARY; | ||
delete result.scoringMode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add note that will be removed in next PR
@@ -7,6 +7,7 @@ | |||
|
|||
const assert = require('assert'); | |||
const parseCacheControl = require('parse-cache-control'); | |||
const Audit = require('../audit'); | |||
const ByteEfficiencyAudit = require('./byte-efficiency-audit'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still need ByteEfficiencyAudit
? Looks like most things might just be through Audit
?
@@ -203,9 +203,10 @@ class CategoryRenderer { | |||
const auditsGroupedByGroup = /** @type {!Object<string, | |||
!Array<!ReportRenderer.AuditJSON>>} */ ({}); | |||
manualAudits.forEach(audit => { | |||
const group = auditsGroupedByGroup[audit.group] || []; | |||
const groupId = audit.group || 'ungrouped'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this doing?
* @return {!Element} | ||
*/ | ||
_renderTextURL(text) { | ||
const url = text.text || ''; | ||
console.assert(!('text' in text), 'url details should not have .text'); | ||
console.assert('value' in text, 'url details must have .value'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should be checked by the type system?
@@ -133,11 +137,16 @@ class DetailsRenderer { | |||
* @return {!Element} | |||
*/ | |||
_renderLink(details) { | |||
console.assert(('url' in details), 'link details must have .url'); | |||
console.assert(('text' in details), 'link details must have .text'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
* wastedBytes: (number|undefined), | ||
* }} | ||
*/ | ||
DetailsRenderer.DetailsSummary; // eslint-disable-line no-unused-expressions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems more specific than a just a "summary"?
if we want to get really fancy (and if we do TS checking of the report renderer), we can use discriminated unions which will type check based on the value of |
fwiw the current shape of details objects looks like this: detail types with 1 required fieldtype type detail types with >1 required fieldtype Additionally, the second block of 1-field types (ms/bytes/url) are currently never expressed in the LHR explicitly; they are all created via |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
- completely new shape of `details`: - `result.details.headings` describes the expected report columns - `result.details.items` contains row information, and may include _more_ data than referenced by the headings. it's the new `extendedInfo`. :) - new `result.details.summary` obj that includes whatever toplevel numeric summary we have. So far just `wastedMs` and `wastedKb`
Alright, #4384 laid the groundwork here and I've built off that for round two in the goal of "making the juicy details in LHR easy to consume."
(This is a super-breaking change. Yes.)
Changes
details
:result.details.headings
describes the expected report columnsresult.details.items
contains row information, and may include more data than referenced by the headings. it's the newextendedInfo
. :)result.details.summary
obj that includes whatever toplevel numeric summary we have. So far justwastedMs
andwastedKb
, but I could seedisplayValue
migrating into it.I think this is easiest to evaluate by looking at the LHR json. Here's one audit result, before and after:
🔎 Also here's a complete LHR: tinyhouse.json.txt
@patrickhulce @brendankenny Thoughts on the new LHR shape?
cc @benschwarz @denar90
This is a chain of PRs. Landing order is... 1st:
newdetails
(#4616). 2nd:shallowCategories
(#4711). 3rd:scoring2.0
(#4690)ref #4614