-
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: support saving and loading error artifacts #9397
Conversation
}; | ||
} | ||
|
||
throw new Error('Invalid value for LHError stringification'); |
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 could pass through like parseReviver
, but then we wouldn't have a foolproof way to have tsc type check the constructed error substitutes, so this seemed ok
@@ -227,7 +301,7 @@ const ERRORS = { | |||
}, | |||
|
|||
/* Protocol timeout failures | |||
* Requires an additional `icuProtocolMethod` field for translation. | |||
* Requires an additional `protocolMethod` field for translation. |
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.
driveby, I assume changed at some point and missed
class LighthouseError extends Error { | ||
/** | ||
* @param {LighthouseErrorDefinition} errorDefinition | ||
* @param {Record<string, string|boolean|undefined>=} properties | ||
* @param {Record<string, string|undefined>=} properties |
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 added boolean
here in #6014 but have no memory of why, and it doesn't appear to do anything in that PR or in the current codebase, so I'll assume it was from an early edit of that PR and it slipped through at the end unnoticed :)
would be nice to have this for LR too. |
@@ -56,10 +55,17 @@ const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); | |||
* @property {boolean} [lhrRuntimeError] True if it should appear in the top-level LHR.runtimeError property. | |||
*/ | |||
|
|||
const LHERROR_SENTINEL = '__LighthouseErrorSentinel'; |
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 this an occasion to use Symbol()
??
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.
ah damn it. nope. won't be the same across separate executions.
lighthouse-core/lib/lh-error.js
Outdated
* A JSON.stringify replacer to serialize LHErrors and (as a fallback) Errors. | ||
* Returns a simplified version of the error object that can be reconstituted | ||
* as a copy of the original error at parse time. | ||
* @param {unknown} err |
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 not a Error|LighthouseError ? curious.
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 not a Error|LighthouseError ? curious.
that's probably better. I was originally writing it more generalized but it wasn't helpful at all
lighthouse-core/lib/lh-error.js
Outdated
}; | ||
} | ||
|
||
// We still have some errors that haven't moved to be LHErrors, so serialize them as well. |
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.
any error that happens unexpectedly will always be a non-LHError, so it's a condition that's likely to exist forever :)
// We still have some errors that haven't moved to be LHErrors, so serialize them as well. | |
// Unexpected errors won't be `LHError`, but we still want to serialize them as well. |
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.
we could leave the line in if we want to also nudge folks to look into the still expected ones that aren't LHError yet?
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.
we could leave the line in if we want to also nudge folks to look into the still expected ones that aren't LHError yet?
no, I think you're right. We should have some nudges but probably not here :)
static parseReviver(key, possibleError) { | ||
if (typeof possibleError === 'object' && possibleError !== null) { | ||
if (possibleError.sentinel === LHERROR_SENTINEL) { | ||
// eslint-disable-next-line no-unused-vars |
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.
// eslint-disable-next-line no-unused-vars | |
// eslint-disable-next-line no-unused-vars - unpack sentinel just to avoid including it in `properties` |
// save everything else | ||
const restArtifactsString = JSON.stringify(restArtifacts, null, 2); | ||
// save everything else, using a replacer to serialize LHErrors in the artifacts. | ||
const restArtifactsString = JSON.stringify(restArtifacts, stringifyReplacer, 2); |
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 wild non-null usage in stringify appears! 😮
} | ||
|
||
/** | ||
* A JSON.parse reviver. If any value passed in is a serialized Error or |
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.
worth linking to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Using_the_reviver_parameter
?
TIL that it visits all the leaf nodes first before visiting parent objects :)
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.
TIL that it visits all the leaf nodes first before visiting parent objects :)
yeah, I was a little worried about performance, but it doesn't seem like a big hit, and all the "optimizations" I tried to add fast paths seemed to have no net effect (so maybe the biggest hit is just the overhead from having any replacer/reviver at all)
assert.strictEqual(auditResult.scoreDisplayMode, 'error'); | ||
assert.ok(auditResult.errorMessage.includes(errorMessage)); | ||
|
||
rimraf.sync(resolvedPath); |
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 this be done in some sort of after
?
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 this be done in some sort of
after
?
everything is sync before it, so it seemed ok. Are there tricky jest things to worry about or 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.
no I was just thinking if the assertions fail it won't be cleaned up, but this certainly won't be the only rough edge we have in that situation across the codebase
fixes #4984
This is done through using a
JSON.stringify()
replacer and aJSON.parse()
reviver.It would be possible to do the stringify part by adding a
toJSON
toLHError
, but we still use regularError
in a bunch of places (and allow regular exceptions to bubble up as gatherer errors), so we want to serialize those as well. I don't feel good about changing the globalError
prototype, though, so here we are :) The implementations are basically the same, though.Serialization is almost always gross (luckily this is strictly limited to errors and artifacts, not errors in general), so I'm happy to adapt if folks have some cleaner ideas.