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: tree-shaking in blade components #1045
Conversation
🦋 Changeset detectedLatest commit: 4cda6f6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 4cda6f6:
|
@@ -130,9 +129,9 @@ const ActionListSection: WithComponentId<ActionListSectionProps> = ({ | |||
); | |||
}; | |||
|
|||
ActionListSection.componentId = componentIds.ActionListSection; | |||
/*#__PURE__*/ Object.assign(ActionListSection, { componentId: componentIds.ActionListSection }); |
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 looks scary
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.
lol check it out now. It got scarier 😆
✅ PR title follows Conventional Commits specification. |
@@ -443,7 +443,8 @@ const _BaseButton: React.ForwardRefRenderFunction<BladeElementRef, BaseButtonPro | |||
); | |||
}; | |||
|
|||
const BaseButton = React.forwardRef(_BaseButton); | |||
BaseButton.displayName = 'BaseButton'; | |||
const BaseButton = /*#__PURE__*/ Object.assign(React.forwardRef(_BaseButton), { |
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 can also add the explanation as comment to one particular component (probably button)
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 might need a lint rule to enforce this, easy to miss out. How did you even figure this out lol 😸
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.
Btw is there a babel plugin that automatically does this transformation? Seems like a common issue libraries might be running into 🤔
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.
There is this babel plugin - https://github.com/mbrowne/babel-plugin-pure-static-props. I tried this first but it didn't work. I think it works when props are on react component and not when they are on refs. Even for react components it didn't work at lots of places for some reason. Couldn't find any other plugin either.
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 might need a lint rule to enforce this, easy to miss out.
Yes definitely! Exploring if it can be eslint rule or babel plugin.
How did you even figure this out lol 😸
Lol it was tricky
@@ -101,7 +101,8 @@ const _StyledBaseButton: React.ForwardRefRenderFunction<BladeElementRef, StyledB | |||
); | |||
}; | |||
|
|||
const StyledBaseButton = React.forwardRef(_StyledBaseButton); | |||
StyledBaseButton.displayName = 'StyledBaseButton'; | |||
const StyledBaseButton = /*#__PURE__*/ Object.assign(React.forwardRef(_StyledBaseButton), { |
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.
So here, what is the main factor?
The PURE comment or the Object.assign? which one does the magic?
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 PURE comment.
You can also define component inside a function and add prop inside that and return the component with imediate function call and this would probably work.
const StyledBaseButton = /*#__PURE__*/ (() => {
const MyComponent = () => {};
MyComponent.displayName = 'MyComponent';
return MyComponent;
})();
The PURE comment only works on function calls so Object.assign
just seems cleaner way compared to any other option.
Input is getting bundled because we have this autocompleteSuggestionType array as separate variable. This might need some change in logic (maybe move this array inside component render or try something else) so I didn't get much into this right now. I'll create separate issue for input treeshaking. |
Not sure why we are using the Pure pragma and not bundler level config? @saurabhdaware |
sideEffects property goes on consumer's package.json usually. You can see in the issue you mentioned, rollup doesn't have sideEffects property support yet. I also tried adding sideEffects option to consumer with current latest blade but that didn't strip off Blade's bundle either 🤔 Also I am not sure if I would prefer that because that can cause unexpected results in production unless you have a good eslint setup that stops you from adding side-effects (which I couldn't find yet). In my opinion, Ideal way to maintain treeshaking might be having a bundle-size check in PRs, having eslint rules to stop us from adding side-effects and a babel plugin to take care of common patterns 🤔 |
I feel like something is wrong with our rollup setup or maybe with how the consumer end's config, because why is a variable which is defined as "const" treated as a side effect? If we did something like "window.someVar = []" or "var someVar = []" then I could understand that. Although we do transpile through babel which converts the const to var. so I think something can be done on the consumer's end. @saurabhdaware can you push your testing setup on the /packages/example folder? |
Let me check this actually. I didn't try to fix it yet and assumed it is because of that variable (don't see anything else as of now) Update: It might be because we're outputting ES5 but with ESM modules. I can see all variables turn into |
* Once we upgrade to rollup 3.5.0, we can use treeshake.manualPureFunctions config from rollup instead of this plugin. | ||
* https://rollupjs.org/configuration-options/#treeshake-manualpurefunctions | ||
*/ | ||
const manualPureFunctions = () => ({ |
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.
:p why is this "manual", shouldn't this be "auto"?
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.
cause we manually define which functions should be pure 😆
This is what rollup follows - https://rollupjs.org/configuration-options/#treeshake-manualpurefunctions (we're just on older version so can't use this 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.
Can't we upgrade our rollup version and use this?
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 can. It's a major version bump though so didn't spend much time right now. can pick it up next quarter
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.
LGTM, check bundling the code and do a sanity on the examples package.
Yup did. Working 👯 |
I think its high time we add bundle size check on our PRs now 😅 |
Yes!! Definitely required! |
|
||
const CardHeaderIcon: WithComponentId<{ icon: IconComponent }> = ({ icon: Icon }) => { | ||
const _CardHeaderIcon = ({ icon: Icon }: { icon: IconComponent }): JSX.Element => { |
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.
curious why JSX.Element
and not ReactNode
or ReactElement
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.
That is what we use generally for return type of React components so used the same
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.
Let's revert to earlier React.ReactElement
otherwise this may break typings for consumers in a patch release
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've been adding JSX.Element
on all components I built 🙈 and there are few other instances of it in code.
I reverted the change here to how it was to not create breaking changes.
Created this issue to later change all JSX.Element instances to React.ReactElement #1089
dd61197
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.
Looks good overall, one comment to make the return types same as earlier.
|
||
const CardHeaderIcon: WithComponentId<{ icon: IconComponent }> = ({ icon: Icon }) => { | ||
const _CardHeaderIcon = ({ icon: Icon }: { icon: IconComponent }): JSX.Element => { |
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.
Let's revert to earlier React.ReactElement
otherwise this may break typings for consumers in a patch release
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.
* fix: tree-shaking in blade components * fix: displayName getting removed * feat: add assignWithoutSideEffects function * feat: remove .componentId assignments * feat: eslint rule * fix: baseinput test * Create .changeset/red-trees-hear.md * feat: revert to using React.ReactElement * fix: revert some JSX.Element changes * fix: revert ActionListHeader type
Description
Reduces bundle-size from 44kb to 13kb (for an App that is only using
Button
component)Fixes #959
Before
After
Issue
😇 This creates no side-effects
😇 This creates no side-effects
👿 This CREATES side-effects
Here, it considers the
.displayName
assignment on ref as a side-effect and bundles the entire file😇 Solution - This creates no side-effects
Also, in ActionList's case, having multiple components using each other in same file and defining
.componentId
was creating side-effects as well. Doing the same Object.assign with PURE comment fixed that as well.