-
Notifications
You must be signed in to change notification settings - Fork 9.5k
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(tsc): refactor and add type checking to config.js #5486
Conversation
|
||
/** @type {LH.Config['settings']} */ | ||
this._settings = configJSON.settings || {}; | ||
this.settings = settings; |
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.
no reason to hide these on getters (and they don't work with JSON.stringify
since the getter is on the prototype)
@@ -623,90 +683,171 @@ describe('Config', () => { | |||
}); | |||
}); | |||
|
|||
describe('generateNewFilteredConfig', () => { | |||
describe('filterConfigIfNeeded', () => { |
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.
the only changes to existing tests were
- to use
onlyCategories
(etc) instead ofConfig.generateNewFilteredConfig
directly - to use
requireAudits
/requireGatherers
which now call the shorthand expansion methods instead ofexpandAuditShorthandAndMergeOptions
directly
caused a little code churn to maintain the same test, but the intention was for test path/result to be exactly the same
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 cleanup in config.js. so good
|
||
/** @type {LH.Config['settings']} */ | ||
this._settings = configJSON.settings || {}; | ||
this.settings = settings; | ||
/** @type {LH.Config['passes']} */ |
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 think these casts arent necessary any longer?
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.
Well, they're kind of casts, but they're not coercive, so if the lhs can't be treated as these things there will be an error. That's a nice thing to have because if future changes to config.js
accidentally change the lhs type, the error will be here. If not, the class's property type will just be whatever the accidental type is, and the error will instead be deep somewhere else where the property is actually used. It's also nice for file-level documentation since these are pretty much the Config instance API.
Some of this (and some of config.d.ts
) can be deleted when typescript supports @implements
, though (microsoft/TypeScript#17498)
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.
a bit heavier on impl changes for a type checking PR...
but it was all bad stuff, and I'm glad it's improving 😄
} | ||
|
||
return config; | ||
const {defaultPassConfig} = constants; | ||
return passes.map(pass => merge(deepClone(defaultPassConfig), pass)); |
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 think I may have missed this last time, but don't we need safe deepClone here for .gatherers
ala deepCloneConfigJson?
nevermind we only clone the defaults, right? 👍
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, the default is an empty gatherers
array so it's fine, though I guess it could be fragile if we ever change that in the future...
lighthouse-core/config/config.js
Outdated
if (!audits) { | ||
return null; | ||
} | ||
|
||
const newAudits = audits.map(audit => { | ||
if (typeof audit === 'string') { | ||
return {path: audit, options: {}}; | ||
} else if (audit && typeof audit.audit === 'function') { | ||
} else if ('implementation' in audit && typeof audit.implementation.audit === 'function') { | ||
return 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.
the example comments for gatherer are 💯 want to add them here too?
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.
good idea, done
lighthouse-core/config/config.js
Outdated
static filterConfigIfNeeded(config) { | ||
const settings = config.settings; | ||
if (!settings.onlyCategories && !settings.onlyAudits && !settings.skipAudits) { | ||
return config; |
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.
want to add a return typedef? seems like we don't actually want to return anything
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 we don't actually want to return anything
ah, yes, good call
lighthouse-core/config/config.js
Outdated
log.warn('config', `${auditId} in 'onlyAudits' is already included by ` + | ||
`${foundCategory} in 'onlyCategories'`); | ||
} else { | ||
if (auditIds.includes(auditId) && categoryIds.includes(foundCategory)) { |
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.
want to smoosh into else if?
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.
done
if (!auditDefn.implementation) { | ||
const path = auditDefn.path; | ||
const auditDefns = expandedAudits.map(audit => { | ||
let implementation; |
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.
does this become any
? or does TS
infer based on the later assignments?
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, it's any
here, but by the time it's out of the if/else blocks it's typeof Audit
from the assignments
lighthouse-core/config/config.js
Outdated
|
||
GathererClass = require(requirePath); | ||
const fullPasses = passes.map(pass => { | ||
const gathererDefns = Config.expandGathererShorthand(pass.gatherers).map(gathererDefn => { |
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 has become quite the inner loop, WDYT about extracting a method for the actual requireing
part as it was before?
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.
WDYT about extracting a method for the actual requireing part as it was before?
ok, attempted. LMKWYT
const extendedConfig = new Config(extended); | ||
|
||
assert.equal(config.passes.length, 1, 'did not filter config'); | ||
assert.deepStrictEqual(config, extendedConfig, 'no mutations'); |
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.
should the message be "had mutations"?
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.
yes, done
good point. Updated the PR name :) |
🔥🔥 ❓ 🔥🔥 |
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! 🎉
even with #5481 out of the way, this is still kind of a doozy to review, but
Main starting change is eliminating
generateNewFilteredConfig()
as kind of an externally-facing API. We have a way of expressing that in the config itself now (onlyCategories
, etc), so it makes sense to not expose a function for it anymore (and the extension, etc moved on to usingonlyCategories
, etc a while ago).With that gone, the constructor is really the only entry point, and control flow becomes just a simple tree (ignoring
deepClone
andmergeOptionsOfItems
):This means configs are always in one of two states, a
ConfigJson
-- possibly missing values and needing to extend something or be filtered -- and aConfig
-- all defaults set, filtered and extended. As mentioned in #5481, conveniently aConfig
is also aConfigJson
, enforced by the type system and some tests here.