-
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
add Closure type checking for report v2 #2043
Conversation
I have used it to set up the typing in this PR, but disabled it for now. Uncomment that line to see its output. Some examples:
Notably the old type inference also gets confused by the last two, it just doesn't complain, which is arguably worse. The Closure Compiler team is doing a large push on NTI right now, most especially finishing the ES6->ES6 compiler so type checking can be done on the actual code instead of transpiled ES5 code, so I'm hoping that our particular bugs will be squashed in the next month or two. Full NTI output:
|
@@ -73,7 +75,7 @@ class ReportRenderer { | |||
renderReport(report) { | |||
try { | |||
return this._renderReport(report); | |||
} catch (e) { | |||
} catch (/** @type {!Error} */ e) { |
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 is an example of stuff not needed with NTI
4826cf8
to
4e07204
Compare
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'm not liking the idea of already nuking recursive rendering as that was kinda the point of this structure in the first place. Table will definitely need this and list should already need it for text.
@@ -95,31 +81,6 @@ describe('DetailsRenderer', () => { | |||
assert.equal(items.children.length, 3, 'did not render children'); | |||
}); | |||
|
|||
it('renders nested structures', () => { |
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.
why? even if we don't care about blocks we certainly care about text within lists
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.
why? even if we don't care about blocks we certainly care about text within lists
oh, I didn't actually keep reading and see the list part :)
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.
actually, without the block part it's just a duplicate of the renders lists with headers
test above, so it's probably fine to remove and get more detailed list tests in #2019
This PR was aiming to satisfy two constraints: do actual type checking and don't change how things are currently being used. And just to be clear, a DAG of typedefs works fine (e.g. lists have items that are text or urls or images or etc...just not items that are lists or things that contain lists). Currently the only thing actually using the recursive structure was the unused Only some types had And if and when we need them, we can switch over to actual record types/interfaces instead of typedefs to support circular references. |
This is a bad thing, not something we should cement with type-checking that won't support the proper method.
If this is really going to be the case then I misunderstood what direction details was headed and definitely do not like it 😆 I'll dismiss my review because I think I'm out of sync with how the new world of LH front-end is taking shape... |
"I think I'm out of sync with how the new world of LH front-end is taking shape..."
yeah, there's probably a meta issue here: "wtf is The current queue of report PRs just sends us further down this path, so we should probably figure that out first if we don't want to go that way |
@@ -56,7 +55,7 @@ class DOM { | |||
* @throws {Error} | |||
*/ | |||
cloneTemplate(selector, context) { | |||
const template = context.querySelector(selector); | |||
const template = /** @type {HTMLTemplateElement} */ (context.querySelector(selector)); |
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.
compiling this PR in devtools i get this error:
.../devtools/front_end/audits2/lighthouse/renderer/dom.js:58
ERROR - Type "HTMLTemplateElement" nullability not marked explicitly with "?" (nullable) or "!" (non-nullable)
const template = /** @type {HTMLTemplateElement} */ (context.querySelector(selector));
that's the only error though!
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.
interesting! The devtools compiler must have an extra check for explicit nullability?
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.
fixed! made it explicitly nullable so the following if statement makes sense :)
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.
4e07204
to
dedc2e2
Compare
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.
nice pr. two proposals below.
yeah, there's probably a meta issue here: "wtf is
details
" :)
The current queue of report PRs just sends us further down this path, so we should probably figure that out first if we don't want to go that way
let's get this sorted out tomorrow.
@@ -88,13 +90,13 @@ class ReportRenderer { | |||
*/ | |||
_populateScore(element, score, scoringMode, title, description) { | |||
// Fill in the blanks. | |||
const valueEl = element.querySelector('.lh-score__value'); | |||
const valueEl = DOM.find(element, '.lh-score__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.
need to use the instance because we can't rely on this global being present.
so i guess let's drop the static and use it as this._dom.find(...
also, suggestion on alternative method signature above...
* @param {string} query | ||
* @return {!Element} | ||
*/ | ||
static find(context, query) { |
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.
to be more like https://www.w3.org/TR/selectors-api2/ it would make more sense to do find(query, context)
.
i'd prefer it, as well.
@@ -56,7 +55,7 @@ class DOM { | |||
* @throws {Error} | |||
*/ | |||
cloneTemplate(selector, context) { | |||
const template = context.querySelector(selector); | |||
const template = /** @type {HTMLTemplateElement} */ (context.querySelector(selector)); |
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.
dedc2e2
to
2285e5b
Compare
ok, Also added closure compiler check into travis and |
currently, at least, this is adding 5-6 seconds on travis, so currently tied with |
Yeah closure compilation is back! and on travis! ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ ⛵️ |
@paulirish already took this most of the way there in #2002, this is just cleaning up the rest and hitting some pretty strict rules, which is nice. It has to be manually run right now (
npm run closure
)...I wasn't sure if I should enable it or not yet in CI.Two things that might be controversial:
DetailsJSON
has been flattened into basically a base class (DetailsJSON
with atype
and optionaltext
field) and derived classesListDetailsJSON
, andCardDetailsJSON
(names are terrible :). Inheritance isn't actually used, however.Precipitating reason is that you can't do circular typedefs, so these weren't being checked at all. DevTools compilation didn't notice because it's not using the NewTypeInference engine (to be fair, we aren't either by default because of some lingering bugs). We could do an actual
@interface
, which handles circular references, but we aren't actually using the recursive nature ofDetailsJSON
anywhere and it really just makes it harder to figure out what type any particular place is dealing with. If every property onDetailsJSON
was going to be optional except fortype
, andtype
determines which properties are defined on a particular instance and what function deals with rendering it...that sounds a whole lot like a separate type, so lets just call it that.When dispatched from
_render
to their particular_render*
method, they're cast to the specific type needed. Whole thing works because these are structural types, so as long as they fit the structure they're good to go.For lists and cards I tried to narrow the property types to how they're actually used.
_renderBlock
is never actually used, so I have no information there, so I just deleted rather than deal with it. Next PR that needs blocks can add it back :)As mentioned offline to some, I've replaced instances of
context.querySelector(query)
(which can returnnull
if nothing is found) withDOM.find(context, query)
which never returns null so we don't have to constantly assure closure that it's not null. We use it for finding stuff in templates, so if the target isn't found we either screwed up the template or the query, andDOM.find
helpfully throws an exception in that case.