-
Couldn't load subscription status.
- Fork 423
feat(compiler): script transformer experimentalErrorRecoveryMode #5552
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
base: master
Are you sure you want to change the base?
Conversation
|
|
||
| expect(() => { | ||
| transformSync(actual, 'foo.js', TRANSFORM_OPTIONS); | ||
| }).toThrowAggregateError(['LWC1107: Invalid property name "dataInvalidProperty".']); |
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.
| }).toThrowAggregateError(['LWC1107: Invalid property name "dataInvalidProperty".']); | |
| }).toThrow(aggregatErrorMatcher(['LWC1107: Invalid property name "dataInvalidProperty".'])); |
We can use asymmetric matchers to avoid having to a whole new thing.
function aggregateErrorMatcher(messages: string[]) {
return expect.objectContaining({
name: 'AggregateError',
message: 'Multiple errors occurred during compilation.',
errors: messages.map((msg) => {
return expect.objectContaining({
name: 'Error',
message: expect.stringContaining(msg),
});
}),
});
}
packages/@lwc/babel-plugin-component/src/__tests__/index.spec.ts
Outdated
Show resolved
Hide resolved
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's the reasoning for putting this as its own separate lwcErrors vs the errors parameter?
| name = (expressionPath.node.callee as types.V8IntrinsicIdentifier).name as LwcDecoratorName; | ||
| } else { | ||
| throw generateInvalidDecoratorError(decoratorPath, state); | ||
| //TODO: Add a fallback assignment |
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.
Can you clarify what you mean by fallback assignment here?
| } | ||
|
|
||
| let result; | ||
| const extractLwcErrors = (result: babel.BabelFileResult): CompilerDiagnostic[] => { |
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 there a reason for this being an inline function rather than just defined as a separate function or used inline? It's a bit far from where it's used, and it's only ever used once. Feels like inline would be cleaner and let you combine some of the code blocks and flag checks.
| }, | ||
| })!; | ||
|
|
||
| const lwcErrors = extractLwcErrors(result); |
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.
See question above about this being a separate function. Feels like you could combine the code below with the code in the function and get cleaner conditional blocks?
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.
Potentially even combine the code after the try/catch block here too.
| const lwcErrors = extractLwcErrors(result); | ||
|
|
||
| if (!experimentalErrorRecoveryMode || lwcErrors.length === 0) { | ||
| return { |
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.
nit: I personally prefer having the return statement at the end even if it means result isn't a const. It forces a cleaner separation between the 'successful' codepath and the 'error' code paths, and doesn't hide the main successful return value in the middle.
| errors.push(...lwcErrors.map((diagnostic) => CompilerError.from(diagnostic))); | ||
| } catch (e) { | ||
| // If we are here in errorRecoveryMode then it's most likely that we have run into | ||
| // an unforeseen error |
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 mean we don't want to try to aggregate any 'thrown' errors?
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 should only happen when babel.transformSync call throws, compiler-plugin errors should not cause the compilation itself to fail. This will be an instance of Error and we don't have our "lwcErrors" bag of errors , hence nothing to Aggregate this error to.
This also means that when running in errorRecoveryMode compiler can now throw 2 types of errors CompilerError and an AggregateError but I think that might me fine, unless we need to converge them to a single error type for some reason.
Let me know if you have any thoughts here?
| ): types.ObjectProperty[] { | ||
| const properties = wireConfig.get('properties'); | ||
|
|
||
| // Should only occurs in error recovery mode when config validation has already failed |
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 only occurs in error recovery mode when config validation has already failed | |
| // Should only occur in error recovery mode when config validation has already failed |
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.
Also, what does this mean?
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 essentially means that we have reached a path that we shouldn't have reached given the existing constraints/validations in place.
Ex: we reach wire.transfrom only when we have successfully gathered metadata, now say if we failed to identify the decorator type, instead of throwing we just log that error and continue to parse but this causes an issue here where we are expecting wireConfig.get('properties') to be an Array but instead it could be anything like string, undefined.
So to prevent compilation from breaking unexpectedly we just return an skip processing these props in errorRecoveryMode since we know this path already has known issues and is going to fail.
Details
This change introduces a
transformOption:experimentalErrorRecoveryModewhich also sets babel parsers errorRecovery flag.This changes lwc compilers, specifically
babel-plugin-componentbehavior fromWhile in theory we might still get errors in subsequent compile requests as some validation paths were skipped because we couldn't continue in a meaningful way.
These errors are then put in-to a bag of errors,
{metadata: { lwcErrors: CompilerError[]}}, upon completion of the transform process our transformer looks for presence of these errors and throws anAggregateError.Currently, only scriptTransformer is updated to utilize this behavior with other transformers (html, css) to follow suit.
experimentalErrorRecoveryModedefaults to false hence there should be no observable changes in current compilation scenarios.Does this pull request introduce a breaking change?
Does this pull request introduce an observable change?
GUS work item
W-19992686