-
-
Notifications
You must be signed in to change notification settings - Fork 67
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
Ensure co-located templates with re-exported class do not throw an error #772
Ensure co-located templates with re-exported class do not throw an error #772
Conversation
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.
Thanks for the Fix and Test!!!!
lib/colocated-broccoli-plugin.js
Outdated
@@ -150,7 +150,10 @@ module.exports = class ColocatedTemplateProcessor extends Plugin { | |||
} | |||
); | |||
|
|||
if (hasTemplate && !jsContents.includes('export default')) { | |||
if (hasTemplate && jsContents.includes('export { default }')) { | |||
jsContents = jsContents.replace('export { default }', "import Component"); |
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.
my only reluctance here is if someone potentially already defined Component
for some reason.
It may be safer to rename Component
(since it's just a default import), to __Component
or something?
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 point, will change 👍 Not too sure if that is actually possible, because not changing any of the JS contents didn't work in practice for me. So like:
import { hbs } from 'ember-cli-htmlbars';
const __COLOCATED_TEMPLATE__ = ...;
export { default } from '...';
did not work.
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 have changed it 👍
d2f57be
to
e63af7f
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.
Discussed this with the team today, and decided that this is a kind of troll, because this code implicitly changes the semantics of what a export default
means,
Specifically, the change in this PR breaks ===
and instanceof
.
This assumption with export { default }
only worked pre-co-location.
It was intentional that it would never work with component co-location, because of the broken semantics.
To update a codebase that has many of these exports, you could probably do a regex find & replace for:
export \{ default \} from '([^']+)';
with:
import { default as Component } from '$1';
export default class extends Component {};
(in your components folder)
Additionally, we don't want to encourage this pattern in the future -- which I know would be a huge lift for existing application that make heavy use of this pattern -- but by using the above find and replace, you can keep moving forward 🥳
In general, composing components provides better debuggability, maintenance, and ergonomics over component inheritance.
Hmm, that is unfortunate for us 😕 I understand now why it wasn't intended to work, as it breaks what the RFC calls
Although I don't entirely see the 'risk' of my fix, I do get the point about the implicit change of the semantics and that it doesn't fit the overall vision. For developers not familiar with the internals of co-location (including myself until recently), I do think that it's not very clear why template co-location breaks this though, as it feels like it's just a different file name for a template rather than a change in component semantics. So, @NullVoxPopuli , do you at least agree that the error message here should be changed when it's a re-export and there is co-located template? For me at least it wasn't clear why such components broke with the current error message, I only found it by googling the error message and ending up at #389. (if you have a suggestion for a concise error message, that would be nice 🙏) About the regex to replace it, that is something that won't work for us, as we don't wanna change component re-exports that don't have their own templates. Those components still work and we have a lot of them (the term |
It breaks
fair!
Yes! absolutely! 🥳 Unclear error messages are A BUG.
You were proposing that it would happen anyway in this PR, except the behavior would be hidden from you.
that's why automating the change is important -- though, you can do it incrementally so it's not one big PR, or you can "just do it" and get it over with (my preference) -- I've found that over-focusing on small PRs for automated changes makes simple changes take ages to get finished, and having all the different ways of doing things in one codebase can be more disruptive than if the change were done all at once.
Why is there a need for a codemod? would find and replace not just work? |
Fair enough, can't judge that of course 👍
Yes, but only for backing classes where
Yes, agree, that's indeed the approach we're taking for such changes 👍 The coming days we have a feature-freeze, so that's why I want to get this over with soon 😄
Well, as I mentioned above, only 105 of the 329 components found by the regex would be components that must be fixed by extending a component. Checking if they have a template is not really possible I think, but since the component path is in that same error message, I guess I can extract those 105 component paths with some search-and-replace magic, without needing a codemod. The only downside is that we like to give these classes meaningful names, but that's not really worth creating a codemod for then 🙃 |
@NullVoxPopuli , I've opened #773 to adjust the error message 👍 |
Thank you!!! |
FWIW I went and tested whether the existing codemod gets this case right and it does not. |
Fixes #771. I did try avoiding the error that throws the error by adding
&& !jsContents.includes('export { default }')
, but that doesn't work for re-exports (doesn't surprise me). So, I made it work such that it turnsinto
and then it works. I added a test for this as well, and I also verified it works in practice by locally linking the package within our application.