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:
|
| }; | ||
|
|
||
| ActionListSection.componentId = componentIds.ActionListSection; | ||
| /*#__PURE__*/ Object.assign(ActionListSection, { componentId: componentIds.ActionListSection }); |
There was a problem hiding this comment.
lol check it out now. It got scarier 😆
|
✅ PR title follows Conventional Commits specification. |
|
|
||
| const BaseButton = React.forwardRef(_BaseButton); | ||
| BaseButton.displayName = 'BaseButton'; | ||
| const BaseButton = /*#__PURE__*/ Object.assign(React.forwardRef(_BaseButton), { |
There was a problem hiding this comment.
We can also add the explanation as comment to one particular component (probably button)
There was a problem hiding this comment.
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.
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.
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.
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
|
|
||
| const StyledBaseButton = React.forwardRef(_StyledBaseButton); | ||
| StyledBaseButton.displayName = 'StyledBaseButton'; | ||
| const StyledBaseButton = /*#__PURE__*/ Object.assign(React.forwardRef(_StyledBaseButton), { |
There was a problem hiding this comment.
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.
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.
:p why is this "manual", shouldn't this be "auto"?
There was a problem hiding this comment.
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.
Can't we upgrade our rollup version and use this?
There was a problem hiding this comment.
we can. It's a major version bump though so didn't spend much time right now. can pick it up next quarter
anuraghazra
left a comment
There was a problem hiding this comment.
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! |
| import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects'; | ||
|
|
||
| const CardHeaderIcon: WithComponentId<{ icon: IconComponent }> = ({ icon: Icon }) => { | ||
| const _CardHeaderIcon = ({ icon: Icon }: { icon: IconComponent }): JSX.Element => { |
There was a problem hiding this comment.
curious why JSX.Element and not ReactNode or ReactElement
There was a problem hiding this comment.
That is what we use generally for return type of React components so used the same
There was a problem hiding this comment.
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.
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
divyanshu013
left a comment
There was a problem hiding this comment.
Looks good overall, one comment to make the return types same as earlier.
| import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects'; | ||
|
|
||
| const CardHeaderIcon: WithComponentId<{ icon: IconComponent }> = ({ icon: Icon }) => { | ||
| const _CardHeaderIcon = ({ icon: Icon }: { icon: IconComponent }): JSX.Element => { |
There was a problem hiding this comment.
Let's revert to earlier React.ReactElement otherwise this may break typings for consumers in a patch release
* 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
Buttoncomponent)Fixes #959
Before
After
Issue
😇 This creates no side-effects
😇 This creates no side-effects
👿 This CREATES side-effects
Here, it considers the
.displayNameassignment 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
.componentIdwas creating side-effects as well. Doing the same Object.assign with PURE comment fixed that as well.