-
Notifications
You must be signed in to change notification settings - Fork 392
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
fix(babel-plugin-component): avoid unnecessary registerDecorators #2741
Conversation
/nucleus test |
@@ -216,6 +216,12 @@ function decorators({ types: t }) { | |||
return; | |||
} | |||
|
|||
if (node.superClass === null) { | |||
// Components *must* extend from either LightningElement or some other superclass (e.g. a mixin). | |||
// We can skip classes without a superclass to avoid adding unnecessary registerDecorators() calls |
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.
Just to confirm, we also don't need to call registerDecorators()
or perform any of the subsequent logic on LightningElement, mixins, or other "superclasses" 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.
(I believe we do have decorators in mixins, but I'm not familiar enough with this area to know whether we need to process the mixin class itself or whether it'll be sufficient to process the class extending/using the mixin)
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.
Both superclasses and mixins may have fields, so we do need to call registerDecorators()
on them, yes. For LightningElement no, we don't need to do that.
Luckily both superclasses and mixins extend from another class. I'll add a Karma test for this, though, since I realized we don't seem to have one for mixins.
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.
Great! Just a nitpick then, but my impression from the comment was that superclasses/mixins were similar to LightningElement in being top-level classes with no supers. If everything (components, mixins, etc) that has fields must extend from another class, can we make that clearer?
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 you're right. Yes we can change this comment.
d585620
to
6a96e20
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.
One of the side-effects of this change is that decorators applied to classes that aren't extending from a superclass will remain in the generated output. Until now all the decorators were transformed. Instead of failing silently at runtime, rollup will fail to parse the generated output:
Error: Unexpected character '@'
Before merging this PR, I would like to make sure we have considered all the ramifications of this change.
Thanks @pmdartus. This seems like a question we can answer with static analysis:
|
We could be running static analysis against database components to make decorators are applied to classes with a superclass. On top of this, I would like to make sure we have the correct linting in place to catch this error early on. As mentioned in my previous comment, rollup will report a tokenization error if decorators aren't applied correctly. In terms of developer experience, it would be preferable to report a more meaningful error when the compiler detects an invalid usage of a decorator. |
We could write an ESLint rule. Or were you thinking of just changing the error message in Rollup? |
Will the ESLint rule capture all possible paths, or are there environments where internal/external devs could bypass the ESLint rule? If the latter is at all possible, we may need to have some mechanism to detect the invalid usage (either explicitly detecting the usage during transformation/compilation or implicitly detecting the error message) in addition to the ESLint rule. |
Yes, I was thinking about creating a new ESLint rule. Today we have
ESLint should be able to capture all the possible usages of JavaScript decorators. As always internal developers could bypass it by using an eslint disablement mechanism. In which case, they are on their own. |
Filed W-10875815 to capture this. |
6a96e20
to
3d7ae32
Compare
After looking into this, I'm not sure this is actually a concern. It looks like, today, if you apply
So it seems that users are already prevented from applying decorators to non- |
I agree with your previous statement. Running the example you shared with this change results in the following error:
If we can produce a better error than this, I would be more than happy to merge this PR. |
0778132
to
56fe5e0
Compare
if (!errored) { | ||
fail('Expected an error to be thrown'); | ||
} | ||
}); |
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.
@pmdartus Added a more helpful error message ^
How it looks in the console:
[!] (plugin rollup-plugin-lwc-compiler) Error: SyntaxError: LWC1150: Decorators like @api, @track, and @wire are only supported in LightningElement classes. /Users/nlawson/workspace/lwc-barebone/src/modules/x/app/app.js: Decorators are not enabled.
If you are using ["@babel/plugin-proposal-decorators", { "legacy": true }], make sure it comes *before* "@babel/plugin-proposal-class-properties" and enable loose mode, like so:
["@babel/plugin-proposal-decorators", { "legacy": true }]
["@babel/plugin-proposal-class-properties", { "loose": true }]
1 | import { LightningElement, api } from 'lwc';
2 |
> 3 | class NotAnLwcComponent {
| ^
4 | @api foo = 'foo'
5 | }
6 |
src/modules/x/app/app.js
SyntaxError: /Users/nlawson/workspace/lwc-barebone/src/modules/x/app/app.js: Decorators are not enabled.
If you are using ["@babel/plugin-proposal-decorators", { "legacy": true }], make sure it comes *before* "@babel/plugin-proposal-class-properties" and enable loose mode, like so:
["@babel/plugin-proposal-decorators", { "legacy": true }]
["@babel/plugin-proposal-class-properties", { "loose": true }]
1 | import { LightningElement, api } from 'lwc';
2 |
> 3 | class NotAnLwcComponent {
| ^
4 | @api foo = 'foo'
5 | }
6 |
at File.buildCodeFrameError (/Users/nlawson/workspace/lwc/node_modules/@babel/core/lib/transformation/file/file.js:249:12)
at NodePath.buildCodeFrameError (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/path/index.js:143:21)
at shouldTransform (/Users/nlawson/workspace/lwc/node_modules/@babel/helper-create-class-features-plugin/lib/features.js:127:16)
at PluginPass.Class (/Users/nlawson/workspace/lwc/node_modules/@babel/helper-create-class-features-plugin/lib/index.js:92:44)
at newFn (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/visitors.js:177:21)
at NodePath._call (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:53:20)
at NodePath.call (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:40:17)
at NodePath.visit (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/path/context.js:100:31)
at TraversalContext.visitQueue (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/context.js:103:16)
at TraversalContext.visitMultiple (/Users/nlawson/workspace/lwc/node_modules/@babel/core/node_modules/@babel/traverse/lib/context.js:72:17)
After static analysis, the risk/reward ratio seems too high to make this worthwhile. |
Details
Fixes #2701
This makes it so that we don't unnecessarily call
registerDecorators
on classes that don't extend anything. (This is an effective test because LWC components must extend something – eitherLightningElement
or a superclass/mixin.)Does this pull request introduce a breaking change?
Does this pull request introduce an observable change?
There is maybe some very clever way you could detect this if you really wanted to. But I don't think it actually matters in practice.
GUS work item
W-10736098