Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: tree-shaking in blade components #1045

Merged
merged 13 commits into from
Mar 23, 2023
5 changes: 5 additions & 0 deletions .changeset/red-trees-hear.md
@@ -0,0 +1,5 @@
---
"@razorpay/blade": patch
---

fix: tree-shaking in blade components
13 changes: 13 additions & 0 deletions .eslintrc.js
Expand Up @@ -32,6 +32,19 @@ module.exports = {
},
],
'import/extensions': ['error', 'never', { css: 'always' }],
'no-restricted-properties': [
'error',
{
property: 'displayName',
message:
"Please define displayName using `assignWithoutSideEffects` instead. This will make sure the code doesn't create side-effects and tree-shaking continues to work",
},
{
property: 'componentId',
message:
"Please define componentId using `assignWithoutSideEffects` instead. This will make sure the code doesn't create side-effects and tree-shaking continues to work",
},
],
},
env: {
browser: true,
Expand Down
3 changes: 3 additions & 0 deletions packages/blade/.babelrc.js
@@ -1,3 +1,5 @@
const manualPureFunctions = require('./scripts/manualPureFunctions');

const alias = {
'@storybook/react': '@storybook/react-native',
'^styled-components$': 'styled-components/native',
Expand Down Expand Up @@ -128,6 +130,7 @@ const configs = {
['@babel/preset-react', { runtime: 'automatic' }],
],
plugins: [
manualPureFunctions,
[
'@babel/plugin-transform-runtime',
{
Expand Down
19 changes: 19 additions & 0 deletions packages/blade/scripts/manualPureFunctions.js
@@ -0,0 +1,19 @@
const pureFunctions = ['assignWithoutSideEffects'];

/**
* This plugin marks defined function name as PURE. This is temporary implementation of manualPureFunctions.
*
* 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 = () => ({
Copy link
Member

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"?

Copy link
Member Author

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)

Copy link
Collaborator

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?

Copy link
Member Author

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

visitor: {
Identifier(path) {
if (pureFunctions.includes(path.node.name)) {
path.addComment('leading', '#__PURE__');
}
},
},
});

module.exports = manualPureFunctions;
14 changes: 9 additions & 5 deletions packages/blade/src/components/ActionList/ActionListFooter.tsx
Expand Up @@ -16,9 +16,9 @@ import {
metaAttribute,
isValidAllowedChildren,
} from '~utils';
import type { WithComponentId } from '~utils';
import { Text } from '~components/Typography';
import type { TestID } from '~src/_helpers/types';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

type ActionListFooterProps = {
title?: string;
Expand Down Expand Up @@ -67,7 +67,7 @@ const StyledActionListFooter = styled(BaseBox)((props) => {
* />
* ```
*/
const ActionListFooter: WithComponentId<ActionListFooterProps> = (props): JSX.Element => {
const _ActionListFooter = (props: ActionListFooterProps): JSX.Element => {
const footerRef = React.useRef<HTMLDivElement | null>(null);
const {
setShouldIgnoreBlur,
Expand Down Expand Up @@ -159,13 +159,17 @@ const ActionListFooter: WithComponentId<ActionListFooterProps> = (props): JSX.El
);
};

ActionListFooter.componentId = componentIds.ActionListFooter;
const ActionListFooter = assignWithoutSideEffects(_ActionListFooter, {
componentId: componentIds.ActionListFooter,
});

const ActionListFooterIcon: WithComponentId<{ icon: IconComponent }> = ({ icon }) => {
const _ActionListFooterIcon = ({ icon }: { icon: IconComponent }): JSX.Element => {
const Icon = icon;
return <Icon color="surface.text.muted.lowContrast" size="small" />;
};

ActionListFooterIcon.componentId = componentIds.ActionListFooterIcon;
const ActionListFooterIcon = assignWithoutSideEffects(_ActionListFooterIcon, {
componentId: componentIds.ActionListFooterIcon,
});

export { ActionListFooter, ActionListFooterIcon, ActionListFooterProps };
14 changes: 9 additions & 5 deletions packages/blade/src/components/ActionList/ActionListHeader.tsx
Expand Up @@ -4,9 +4,9 @@ import { componentIds } from './componentIds';
import BaseBox from '~components/Box/BaseBox';
import type { IconComponent } from '~components/Icons';
import { isValidAllowedChildren, makeSize, metaAttribute, MetaConstants } from '~utils';
import type { WithComponentId } from '~utils';
import { Text } from '~components/Typography';
import type { TestID } from '~src/_helpers/types';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

const StyledActionListHeader = styled(BaseBox)((props) => {
return {
Expand Down Expand Up @@ -46,7 +46,7 @@ type ActionListHeaderProps = {
* />
* ```
*/
const ActionListHeader: WithComponentId<ActionListHeaderProps> = (props): JSX.Element => {
const _ActionListHeader = (props: ActionListHeaderProps): JSX.Element => {
React.useEffect(() => {
React.Children.map(props.leading, (child) => {
if (!isValidAllowedChildren(child, componentIds.ActionListHeaderIcon)) {
Expand All @@ -71,13 +71,17 @@ const ActionListHeader: WithComponentId<ActionListHeaderProps> = (props): JSX.El
);
};

ActionListHeader.componentId = componentIds.ActionListHeader;
const ActionListHeader = assignWithoutSideEffects(_ActionListHeader, {
componentId: componentIds.ActionListHeader,
});

const ActionListHeaderIcon: WithComponentId<{ icon: IconComponent }> = ({ icon }) => {
const _ActionListHeaderIcon = ({ icon }: { icon: IconComponent }): JSX.Element => {
const Icon = icon;
return <Icon color="surface.text.muted.lowContrast" size="small" />;
};

ActionListHeaderIcon.componentId = componentIds.ActionListHeaderIcon;
const ActionListHeaderIcon = assignWithoutSideEffects(_ActionListHeaderIcon, {
componentId: componentIds.ActionListHeaderIcon,
});

export { ActionListHeader, ActionListHeaderIcon, ActionListHeaderProps };
28 changes: 18 additions & 10 deletions packages/blade/src/components/ActionList/ActionListItem.tsx
Expand Up @@ -16,10 +16,10 @@ import { useDropdown } from '~components/Dropdown/useDropdown';
import type { Feedback } from '~tokens/theme/theme';
import { Text } from '~components/Typography';
import { isReactNative, makeAccessible, makeSize, metaAttribute, MetaConstants } from '~utils';
import type { WithComponentId } from '~utils';
import { Checkbox } from '~components/Checkbox';
import size from '~tokens/global/size';
import type { StringChildrenType, TestID } from '~src/_helpers/types';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

type ActionListItemProps = {
title: string;
Expand Down Expand Up @@ -97,12 +97,12 @@ type ActionListSectionProps = {
*/
_hideDivider?: boolean;
} & TestID;
const ActionListSection: WithComponentId<ActionListSectionProps> = ({
const _ActionListSection = ({
title,
children,
testID,
_hideDivider,
}): JSX.Element => {
}: ActionListSectionProps): JSX.Element => {
return (
<BaseBox
{...makeAccessible({
Expand Down Expand Up @@ -131,9 +131,11 @@ const ActionListSection: WithComponentId<ActionListSectionProps> = ({
);
};

ActionListSection.componentId = componentIds.ActionListSection;
const ActionListSection = assignWithoutSideEffects(_ActionListSection, {
componentId: componentIds.ActionListSection,
});

const ActionListItemIcon: WithComponentId<{ icon: IconComponent }> = ({ icon }): JSX.Element => {
const _ActionListItemIcon = ({ icon }: { icon: IconComponent }): JSX.Element => {
const Icon = icon;
const { intent, isDisabled } = React.useContext(ActionListItemContext);
return (
Expand All @@ -148,9 +150,11 @@ const ActionListItemIcon: WithComponentId<{ icon: IconComponent }> = ({ icon }):
);
};

ActionListItemIcon.componentId = componentIds.ActionListItemIcon;
const ActionListItemIcon = assignWithoutSideEffects(_ActionListItemIcon, {
componentId: componentIds.ActionListItemIcon,
});

const ActionListItemText: WithComponentId<{ children: StringChildrenType }> = ({ children }) => {
const _ActionListItemText = ({ children }: { children: StringChildrenType }): JSX.Element => {
const { isDisabled } = React.useContext(ActionListItemContext);

return (
Expand All @@ -160,7 +164,9 @@ const ActionListItemText: WithComponentId<{ children: StringChildrenType }> = ({
);
};

ActionListItemText.componentId = componentIds.ActionListItemText;
const ActionListItemText = assignWithoutSideEffects(_ActionListItemText, {
componentId: componentIds.ActionListItemText,
});

const ActionListCheckboxWrapper = styled(BaseBox)<{ hasDescription: boolean }>((_props) => ({
pointerEvents: 'none',
Expand Down Expand Up @@ -200,7 +206,7 @@ const makeActionListItemClickable = (
* </ActionList>
* ```
*/
const ActionListItem: WithComponentId<ActionListItemProps> = (props): JSX.Element => {
const _ActionListItem = (props: ActionListItemProps): JSX.Element => {
const {
activeIndex,
dropdownBaseId,
Expand Down Expand Up @@ -333,7 +339,9 @@ const ActionListItem: WithComponentId<ActionListItemProps> = (props): JSX.Elemen
);
};

ActionListItem.componentId = componentIds.ActionListItem;
const ActionListItem = assignWithoutSideEffects(_ActionListItem, {
componentId: componentIds.ActionListItem,
});

export {
ActionListItem,
Expand Down
@@ -1,8 +1,8 @@
import { Image } from 'react-native';
import type { ImageSourcePropType } from 'react-native';
import { componentIds } from './componentIds';
import type { WithComponentId } from '~utils';
import size from '~tokens/global/size';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

type ActionListItemAssetProps = {
/**
Expand All @@ -19,7 +19,7 @@ type ActionListItemAssetProps = {
/**
*
*/
const ActionListItemAsset: WithComponentId<ActionListItemAssetProps> = (props) => {
const _ActionListItemAsset = (props: ActionListItemAssetProps): JSX.Element => {
const source = typeof props.src === 'string' ? { uri: props.src } : props.src;

return (
Expand All @@ -33,6 +33,8 @@ const ActionListItemAsset: WithComponentId<ActionListItemAssetProps> = (props) =
);
};

ActionListItemAsset.componentId = componentIds.ActionListItemAsset;
const ActionListItemAsset = assignWithoutSideEffects(_ActionListItemAsset, {
componentId: componentIds.ActionListItemAsset,
});

export { ActionListItemAsset, ActionListItemAssetProps };
@@ -1,6 +1,6 @@
import { componentIds } from './componentIds';
import type { WithComponentId } from '~utils';
import size from '~tokens/global/size';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

type ActionListItemAssetProps = {
/**
Expand All @@ -12,10 +12,12 @@ type ActionListItemAssetProps = {
*/
alt: string;
};
const ActionListItemAsset: WithComponentId<ActionListItemAssetProps> = (props) => {
const _ActionListItemAsset = (props: ActionListItemAssetProps): JSX.Element => {
return <img src={props.src} alt={props.alt} width={size[16]} height={size[12]} />;
};

ActionListItemAsset.componentId = componentIds.ActionListItemAsset;
const ActionListItemAsset = assignWithoutSideEffects(_ActionListItemAsset, {
componentId: componentIds.ActionListItemAsset,
});

export { ActionListItemAsset, ActionListItemAssetProps };
7 changes: 5 additions & 2 deletions packages/blade/src/components/Box/LayoutPrimitivesDocs.tsx
Expand Up @@ -7,6 +7,7 @@ import { Code, Heading, Text, Title } from '~components/Typography';
import { Sandbox, SandboxProvider, SandboxHighlighter } from '~src/_helpers/storybook/Sandbox';
import { List, ListItem, ListItemCode, ListItemLink } from '~components/List';
import { Link } from '~components/Link';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';
import { castWebType } from '~utils';

if (window.top) {
Expand All @@ -15,7 +16,7 @@ if (window.top) {

// Storybook's docs page is under iframe so just href="#target" doesn't work (it tries to scroll on parent of iframe)
// This function uses scrollIntoView instead to scroll then
const ScrollIntoViewLink = ({
const _ScrollIntoViewLink = ({
href,
children,
}: {
Expand All @@ -33,7 +34,9 @@ const ScrollIntoViewLink = ({
);

// lmao. sorry
ScrollIntoViewLink.componentId = 'ListItemLink';
const ScrollIntoViewLink = assignWithoutSideEffects(_ScrollIntoViewLink, {
componentId: 'ListItemLink',
});

const Section = (props: BaseBoxProps): JSX.Element => {
return <BaseBox paddingY="spacing.6" {...props} />;
Expand Down
Expand Up @@ -46,6 +46,7 @@ import type { BaseTextProps } from '~components/Typography/BaseText/types';
import type { BladeElementRef } from '~src/hooks/useBladeInnerRef';
import { useBladeInnerRef } from '~src/hooks/useBladeInnerRef';
import { getStringFromReactText } from '~src/utils/getStringChildren';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

type BaseButtonCommonProps = {
size?: 'xsmall' | 'small' | 'medium' | 'large';
Expand Down Expand Up @@ -453,7 +454,8 @@ const _BaseButton: React.ForwardRefRenderFunction<BladeElementRef, BaseButtonPro
);
};

const BaseButton = React.forwardRef(_BaseButton);
BaseButton.displayName = 'BaseButton';
const BaseButton = assignWithoutSideEffects(React.forwardRef(_BaseButton), {
displayName: 'BaseButton',
});

export default BaseButton;
Expand Up @@ -9,6 +9,7 @@ import { getIn } from '~utils';
import { useStyledProps } from '~components/Box/styledProps';
import { useTheme } from '~components/BladeProvider';
import type { BladeElementRef } from '~src/hooks/useBladeInnerRef';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

const StyledPressable = styled(Animated.createAnimatedComponent(Pressable))<
Omit<StyledBaseButtonProps, 'accessibilityProps'>
Expand Down Expand Up @@ -111,7 +112,8 @@ const _StyledBaseButton: React.ForwardRefRenderFunction<BladeElementRef, StyledB
);
};

const StyledBaseButton = React.forwardRef(_StyledBaseButton);
StyledBaseButton.displayName = 'StyledBaseButton';
const StyledBaseButton = assignWithoutSideEffects(React.forwardRef(_StyledBaseButton), {
displayName: 'StyledBaseButton',
});

export default StyledBaseButton;
4 changes: 2 additions & 2 deletions packages/blade/src/components/Button/Button/Button.tsx
Expand Up @@ -6,6 +6,7 @@ import type { Platform } from '~utils';
import type { StyledPropsBlade } from '~components/Box/styledProps';
import type { BladeElementRef } from '~src/hooks/useBladeInnerRef';
import type { StringChildrenType, TestID } from '~src/_helpers/types';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

type ButtonCommonProps = {
variant?: 'primary' | 'secondary' | 'tertiary';
Expand Down Expand Up @@ -78,7 +79,6 @@ const _Button: React.ForwardRefRenderFunction<BladeElementRef, ButtonProps> = (
);
};

const Button = React.forwardRef(_Button);
Button.displayName = 'Button';
const Button = assignWithoutSideEffects(React.forwardRef(_Button), { displayName: 'Button' });

export default Button;
6 changes: 3 additions & 3 deletions packages/blade/src/components/Card/Card.tsx
Expand Up @@ -2,11 +2,11 @@ import React from 'react';
import { CardSurface } from './CardSurface';
import { CardProvider, useVerifyInsideCard, useVerifyAllowedComponents } from './CardContext';
import BaseBox from '~components/Box/BaseBox';
import type { WithComponentId } from '~utils';
import { metaAttribute, MetaConstants } from '~utils';
import { getStyledProps } from '~components/Box/styledProps';
import type { StyledPropsBlade } from '~components/Box/styledProps';
import type { TestID } from '~src/_helpers/types';
import { assignWithoutSideEffects } from '~src/utils/assignWithoutSideEffects';

export const ComponentIds = {
CardHeader: 'CardHeader',
Expand Down Expand Up @@ -81,11 +81,11 @@ type CardBodyProps = {
children: React.ReactNode;
} & TestID;

const CardBody: WithComponentId<CardBodyProps> = ({ children, testID }) => {
const _CardBody = ({ children, testID }: CardBodyProps): JSX.Element => {
useVerifyInsideCard('CardBody');

return <BaseBox {...metaAttribute({ name: MetaConstants.CardBody, testID })}>{children}</BaseBox>;
};
CardBody.componentId = ComponentIds.CardBody;
const CardBody = assignWithoutSideEffects(_CardBody, { componentId: ComponentIds.CardBody });

export { Card, CardBody };