-
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: initial refactor of computedArtifact import/caching #5907
Conversation
whoops, wanted it to be the cache passed in, not the whole context. Just sec :) |
Done. Only ergonomic downside is that, since these are essentially single function calls with no state (that isn't cacheable and so needs to be passed in), they really cry out to be static. However, typescript doesn't support polymorphic We really want type checking on those, but I think we also want automatic caching. Repeating the boilerplate everywhere would be dumb, and it's really easy to get slightly wrong and not notice it's recomputing some expensive calculation (a bug that did this was the original impetus for automatic caching - #675). So, as a result, you have to do |
@brendankenny WDYT about something like this instead? |
well I spent the last day learning everything about the limits of polymorphic I'll try it out. |
d8e6372
to
6dc2457
Compare
@paulirish how do you feel about this format |
e49cb12
to
f2817dd
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 like this a lot. My only gripe right now is the churn to all of our context passing.
Just thinking out loud here, what are the primary benefits of attaching the cache to context? If each computed artifact just kept its cache in its request method closure, would that be enough?
Might clean up a lot of the changes we'd have to do to migrate. If we're worried about having to introduce it later, maybe we could ensure all computed artifacts take an inputs object as first argument (like it works now) and an optional context second for future proofing?
return 'ManifestValues'; | ||
} | ||
|
||
class ManifestValues { |
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 believe someone once told me that he thought it was silly we made classes with all static members instead of plain objects ;)
FWIW I kinda like them as classes for the flashy require
name consistency, but I was mostly persuaded back then for plain objects, thoughts?
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 believe someone once told me that he thought it was silly we made classes with all static members instead of plain objects ;)
haha, I didn't even think about it as I changed things. There used to be some tsc benefit due to polymorphic this
(but not an issue with this implementation) and then from augmenting things in javascript passed out on module.exports
(but that's all fixed in 3.1).
I'll admit to stockholm syndrome in that I don't see it as ugly and unnecessary any more, but I also don't have any problem with a plain object. The main thing might be the name
property, which I think we still want, whether explicit or from just the StaticClass.name
default from declaring a class.
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.
true true, this WFM :)
I don't think we can do that because of GC. It would be node-module-only (I believe all the other clients destroy the world after every run), but since we use artifacts as the keys, artifacts will keep accumulating from each run. Good use for WeakMap, but we don't always have unique parent objects for sets of artifacts, so, I don't think that will work. I can't think of another approach around that. FWIW, I don't think the churn will end up being too bad outside of tests. Basically add a Tests need more changes since we make so many calls, but I could reduce the noise there (e.g. just pass in |
Yeah the clearing mechanism would be ugly at best, if you foresee little pain outside tests, then this makes sense to me 👍 |
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.
exciting 🚗ahead! woohoo!
lighthouse-core/runner.js
Outdated
@@ -269,8 +275,7 @@ class Runner { | |||
const auditOptions = Object.assign({}, audit.defaultOptions, auditDefn.options); | |||
const auditContext = { | |||
options: auditOptions, | |||
settings, | |||
LighthouseRunWarnings: runWarnings, | |||
...auditsContext, |
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.
dont love these two diff objs with such similar names
how about sharedAuditsContext
for this one?
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.
sure
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
|
||
// Computed artifacts switching to the new system. | ||
'new-computed-artifact.js', | ||
'manifest-values.js', |
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 fn is for the gulpfile. wont browserify already discover this manifest-values module without being added manually?
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 diff obscures it, but this is the list of filenamesToSkip :)
f2817dd
to
6b10f70
Compare
/** @type {Array<string>} */ | ||
const failures = []; | ||
|
||
return artifacts.requestManifestValues(artifacts.Manifest).then(manifestValues => { | ||
SplashScreen.assessManifest(manifestValues, failures); | ||
const manifestValues = await ManifestValues.request(context, artifacts.Manifest); |
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'll refrain from going promises->async/await in future changes to cut down the diffs as well :)
Proof of concept of moving computed artifacts off of the
artifacts
object passed into audits and just use regularrequire('./path/to/computed-artifact.js')
to use them.Starts with a really simple computed artifact,
manifest-values.js
, which doesn't need to request any computed artifacts itself, nor does any computed artifact request it, but it is non-trivial and required in four audits. The computed artifact cache is now part ofLH.Audit.Context
, and audits need to pass in the cache rather than it being created and bound behind the scenes.Nice thing about this approach is non-converted computed artifacts can continue to function as-is and we can convert them one by one (or in bunches) rather than in one massive, unreadable PR.
There are a few things we lose without the automatically created methods, but I think we'll have a huge step up in being able to analyze dependencies, trace logic through, etc. The fact that this was so difficult to untangle just for a super-simple computedArtifact shows the downsides of the earlier magic experience. Using computed artifacts (or moving off them) in an audit is now as easy as pulling in any other library module.
This was part of #5008 under "Nice to have". I swear we had a discussion about this in a set of comments somewhere, but the earliest reference I could find was #4961 (review), so maybe it was in person.