Skip to content

Commit

Permalink
chore: linter rules to detect forwardRef usage (callstack#3632)
Browse files Browse the repository at this point in the history
  • Loading branch information
DimitarNestorov committed Feb 6, 2023
1 parent 0556ba6 commit e4aefd0
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 8 deletions.
14 changes: 14 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

"extends": "@callstack",

"plugins": ["eslint-plugin-local-rules"],

"rules": {
"local-rules/no-import-react-forwardref": "error",
"local-rules/no-react-forwardref-usage": "error",

"one-var": "off",
"no-multi-assign": "off",
"no-nested-ternary": "off",
Expand Down Expand Up @@ -49,6 +54,15 @@
"react-native-a11y/has-valid-accessibility-descriptors": "off"
},

"overrides": [
{
"files": ["*.test.js", "*.test.tsx"],
"rules": {
"local-rules/no-react-forwardref-usage": "off"
}
}
],

"settings": {
"import/extensions": [".js", ".ts", ".tsx"],
"import/parsers": {
Expand Down
2 changes: 2 additions & 0 deletions eslint-local-rules/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Object.assign(exports, require('./no-import-react-forwardref'));
Object.assign(exports, require('./no-react-forwardref-usage'));
27 changes: 27 additions & 0 deletions eslint-local-rules/no-import-react-forwardref.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
exports['no-import-react-forwardref'] = {
meta: {
docs: {
description: 'Disallow importing of React.forwardRef',
category: 'Possible Errors',
recommended: false,
},
schema: [],
},
create(context) {
return {
ImportDeclaration(node) {
if (node.source.value !== 'react') return;

for (const specifier of node.specifiers) {
if (specifier.type !== 'ImportSpecifier') continue;
if (specifier.imported.name !== 'forwardRef') continue;

context.report({
loc: specifier.loc,
message: 'Import forwardRef from src/utils/forwardRef instead',
});
}
},
};
},
};
28 changes: 28 additions & 0 deletions eslint-local-rules/no-react-forwardref-usage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
exports['no-react-forwardref-usage'] = {
meta: {
docs: {
description: 'Disallow usage of React.forwardRef',
category: 'Possible Errors',
recommended: false,
},
schema: [],
},
create(context) {
return {
CallExpression(node) {
if (node.callee?.type !== 'MemberExpression') return;

const { callee } = node;
if (callee.object.type !== 'Identifier') return;
if (callee.object.name !== 'React') return;
if (callee.property.type !== 'Identifier') return;
if (callee.property.name !== 'forwardRef') return;

context.report({
loc: callee.loc,
message: 'Use forwardRef from src/utils/forwardRef instead',
});
},
};
},
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"dedent": "^0.7.0",
"eslint": "8.31.0",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-local-rules": "^1.3.2",
"expo-constants": "^9.3.5",
"flow-bin": "0.92.0",
"glob": "^7.1.3",
Expand Down
5 changes: 3 additions & 2 deletions src/components/Surface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useInternalTheme } from '../core/theming';
import overlay, { isAnimatedValue } from '../styles/overlay';
import shadow from '../styles/shadow';
import type { ThemeProp, MD3Elevation } from '../types';
import { forwardRef } from '../utils/forwardRef';

export type Props = React.ComponentPropsWithRef<typeof View> & {
/**
Expand Down Expand Up @@ -39,7 +40,7 @@ export type Props = React.ComponentPropsWithRef<typeof View> & {
ref?: React.RefObject<View>;
};

const MD2Surface = React.forwardRef<View, Props>(
const MD2Surface = forwardRef<View, Props>(
({ style, theme: overrideTheme, ...rest }: Omit<Props, 'elevation'>, ref) => {
const { elevation = 4 } = (StyleSheet.flatten(style) || {}) as ViewStyle;
const { dark: isDarkTheme, mode, colors } = useInternalTheme(overrideTheme);
Expand Down Expand Up @@ -105,7 +106,7 @@ const MD2Surface = React.forwardRef<View, Props>(
* });
* ```
*/
const Surface = React.forwardRef<View, Props>(
const Surface = forwardRef<View, Props>(
(
{
elevation = 1,
Expand Down
3 changes: 2 additions & 1 deletion src/components/ToggleButton/ToggleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import color from 'color';
import { useInternalTheme } from '../../core/theming';
import { black, white } from '../../styles/themes/v2/colors';
import type { ThemeProp } from '../../types';
import { forwardRef } from '../../utils/forwardRef';
import type { IconSource } from '../Icon';
import IconButton from '../IconButton/IconButton';
import { ToggleButtonGroupContext } from './ToggleButtonGroup';
Expand Down Expand Up @@ -97,7 +98,7 @@ export type Props = {
*
* ```
*/
const ToggleButton = React.forwardRef<View, Props>(
const ToggleButton = forwardRef<View, Props>(
(
{
icon,
Expand Down
3 changes: 2 additions & 1 deletion src/components/Typography/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {

import { useInternalTheme } from '../../core/theming';
import { Font, MD3TypescaleKey, ThemeProp } from '../../types';
import { forwardRef } from '../../utils/forwardRef';

export type Props = React.ComponentProps<typeof NativeText> & {
/**
Expand Down Expand Up @@ -149,4 +150,4 @@ const styles = StyleSheet.create({
},
});

export default React.forwardRef(Text);
export default forwardRef(Text);
3 changes: 2 additions & 1 deletion src/components/Typography/v2/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import type { MD2Theme } from 'src/types';

import { useInternalTheme } from '../../../core/theming';
import { forwardRef } from '../../../utils/forwardRef';

type Props = React.ComponentProps<typeof NativeText> & {
style?: StyleProp<TextStyle>;
Expand Down Expand Up @@ -58,4 +59,4 @@ const styles = StyleSheet.create({
},
});

export default React.forwardRef(Text);
export default forwardRef(Text);
6 changes: 3 additions & 3 deletions src/utils/forwardRef.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
forwardRef as reactForwardRef,
import * as React from 'react';
import type {
ForwardRefRenderFunction,
PropsWithoutRef,
RefAttributes,
Expand All @@ -20,4 +20,4 @@ export type ForwarRefComponent<T, P = {}> = ForwardRefExoticComponent<
*/
export const forwardRef: <T, P = {}>(
render: ForwardRefRenderFunction<T, P>
) => ForwarRefComponent<T, P> = reactForwardRef;
) => ForwarRefComponent<T, P> = React.forwardRef;
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5206,6 +5206,11 @@ eslint-plugin-jest@^27.0.1:
dependencies:
"@typescript-eslint/utils" "^5.10.0"

eslint-plugin-local-rules@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-local-rules/-/eslint-plugin-local-rules-1.3.2.tgz#b9c9522915faeb9e430309fb909fc1dbcd7aedb3"
integrity sha512-X4ziX+cjlCYnZa+GB1ly3mmj44v2PeIld3tQVAxelY6AMrhHSjz6zsgsT6nt0+X5b7eZnvL/O7Q3pSSK2kF/+Q==

eslint-plugin-prettier@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
Expand Down

0 comments on commit e4aefd0

Please sign in to comment.