Skip to content

Commit

Permalink
fix: tree-shaking in blade components (#1045)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
saurabhdaware committed Mar 23, 2023
1 parent dd09679 commit 8818725
Show file tree
Hide file tree
Showing 35 changed files with 470 additions and 322 deletions.
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 = () => ({
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 }): React.ReactElement => {
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 }): React.ReactElement => {
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 };
32 changes: 22 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,15 @@ 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;
}): React.ReactElement => {
const { isDisabled } = React.useContext(ActionListItemContext);

return (
Expand All @@ -160,7 +168,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 +210,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 +343,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): React.ReactElement => {
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): React.ReactElement => {
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): React.ReactElement => {
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 };

0 comments on commit 8818725

Please sign in to comment.