Skip to content

feat(eslint-plugin): add rules to avoid low performance #118

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Monorepo with packages for setting up ESLint and Typescript for any new React Na

## Presentation

The goal of the project is too have a set of configuration files that can be easily imported into a new project, which would reduce the burden of starting new projects.
The goal of the project is to have a set of configuration files that can be easily imported into a new project, which would reduce the burden of starting new projects.

This repo uses [lerna](https://lerna.js.org/) to maintain, version and publish various packages for configuring ESLint and Typescript.

Expand Down Expand Up @@ -39,7 +39,7 @@ Here are some useful commands:

We use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) to automate the release process.

> If you add a new rule to a config, this is a breaking change, because it could make the CI fail on projects that use the plugin. The commit name where you add the new rule needs to follow this patern `BREAKING CHANGE : the description of your commit`
> If you add a new rule to a config, this is a breaking change, because it could make the CI fail on projects that use the plugin. The commit name where you add the new rule needs to follow this pattern `BREAKING CHANGE : the description of your commit`

## Publishing a new version of a package

Expand Down Expand Up @@ -68,7 +68,7 @@ It will then push a tagged commit `chore(release): Publish` which will then trig

## Unpublish a package version

If you want to unpublish a package, you have to be contributor of @bam.tech/eslint-plugin (in this case for the eslint plugin). Use the following commad :
If you want to unpublish a package, you have to be contributor of @bam.tech/eslint-plugin (in this case for the eslint plugin). Use the following command :
`npm unpublish @bam.tech/[email protected]`

## Running commands
Expand Down
6 changes: 5 additions & 1 deletion example-app/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"root": true,
"extends": ["plugin:@bam.tech/recommended", "plugin:@bam.tech/a11y"],
"extends": [
"plugin:@bam.tech/recommended",
"plugin:@bam.tech/a11y",
"plugin:@bam.tech/performance"
],
"overrides": [
{
"files": ["*.test.tsx"],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Save without formatting: [⌘ + K] > [S]

// This should trigger one error breaking custom FlatList import rule:
// @bam.tech/no-flatlist

import { FlatList } from "react-native";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we already got tests for all those rules, I'd vote for removing those example files, I think they add noise

What do you think @cyrilbo @matthieugicquel ?


export const MyCustomButton = () => {
return <FlatList />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// This code should trigger the ESLint rule `avoidIntlNumberFormatRule`

const number = 1234567.89;

// Incorrect usage: This will be flagged by the ESLint rule
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
});

const formattedNumber = formatter.format(number);

// eslint-disable-next-line no-console
console.log(formattedNumber); // Outputs: $1,234,567.89
21 changes: 21 additions & 0 deletions example-app/eslint-breaking-examples/break-native-driver-rule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Animated, ScrollView, Text } from "react-native";

const fadeAnim = new Animated.Value(0);

Animated.timing(fadeAnim, {
toValue: 1,
duration: 500,
useNativeDriver: false, // This line breaks the custom rule
}).start();

export const CustomScrollView = () => {
return (
<ScrollView
onScroll={Animated.event([], {
useNativeDriver: false,
})}
>
<Text>{"Something to scroll"}</Text>
</ScrollView>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Save without formatting: [⌘ + K] > [S]

// This should trigger one error breaking eslint-plugin-react-native:
// no-restricted-imports

import Svg, { Circle, Rect } from "react-native-svg";

export const SvgComponent = () => {
return (
<Svg height="50%" width="50%" viewBox="0 0 100 100">
<Circle
cx="50"
cy="50"
r="45"
stroke="blue"
strokeWidth="2.5"
fill="green"
/>
<Rect
x="15"
y="15"
width="70"
height="70"
stroke="red"
strokeWidth="2"
fill="yellow"
/>
</Svg>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Save without formatting: [⌘ + K] > [S]

// This should trigger one error breaking custom react-navigation/stack rule:
// @bam.tech/no-react-navigation-stack

import { createStackNavigator } from "@react-navigation/stack";

const Stack = createStackNavigator();

export const MyStack = () => {
return <Stack.Navigator />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Save without formatting: [⌘ + K] > [S]

// This should trigger one error breaking custom performance rule:
// @bam.tech/no-use-is-focused

import { useIsFocused } from "@react-navigation/native";
import { Text } from "react-native";

export const MyComponent = () => {
const isFocused = useIsFocused();

return <Text>{isFocused ? "focused" : "unfocused"}</Text>;
};
5 changes: 4 additions & 1 deletion example-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"devDependencies": {
"@bam.tech/eslint-plugin": "*",
"@bam.tech/typescript-config": "*",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.4.1",
"@testing-library/react-native": "^12.3.1",
"@types/jest": "^29.5.2",
"@types/react": "^18.2.14",
Expand All @@ -37,6 +39,7 @@
"dependencies": {
"expo": "^49.0.0",
"react": "^18.2.0",
"react-native": "^0.73.0"
"react-native": "^0.73.0",
"react-native-svg": "^15.5.0"
}
}
19 changes: 13 additions & 6 deletions packages/eslint-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,23 @@ This plugin exports some custom rules that you can optionally use in your projec
<!-- begin auto-generated rules list -->

💼 Configurations enabled in.\
⚠️ Configurations set to warn in.\
✅ Set in the `recommended` configuration.\
🧪 Set in the `tests` configuration.\
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).

| Name | Description | 💼 | 🔧 |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :-- | :-- |
| [await-user-event](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/await-user-event.md) | Enforces awaiting userEvent calls | 🧪 | 🔧 |
| [no-different-displayname](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/no-different-displayname.md) | Enforce component displayName to match with component name | ✅ | 🔧 |
| [prefer-user-event](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/prefer-user-event.md) | Enforces usage of userEvent over fireEvent in tests. | | 🔧 |
| [require-named-effect](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/require-named-effect.md) | Enforces the use of named functions inside a useEffect | | |
| Name                              | Description | 💼 | ⚠️ | 🔧 |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :--------------------- | :--------------------- | :-- |
| [avoid-intl-number-format](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/avoid-intl-number-format.md) | Disallow the use of `Intl.NumberFormat` due to potential performance issues. | ![badge-performance][] | | |
| [avoid-react-native-svg](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/avoid-react-native-svg.md) | Disallow importing the `react-native-svg` package. | | ![badge-performance][] | |
| [await-user-event](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/await-user-event.md) | Enforces awaiting userEvent calls | 🧪 | | 🔧 |
| [no-animated-without-native-driver](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/no-animated-without-native-driver.md) | Disallow the use of `Animated` with `useNativeDriver: false` | ![badge-performance][] | | |
| [no-different-displayname](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/no-different-displayname.md) | Enforce component displayName to match with component name | ✅ | | 🔧 |
| [no-flatlist](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/no-flatlist.md) | Disallow importing `FlatList` from `react-native` due to potential performance concerns or the preference for alternative components. | ![badge-performance][] | | 🔧 |
| [no-react-navigation-stack](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/no-react-navigation-stack.md) | Disallow importing from `@react-navigation/stack` and suggest using `@react-navigation/native-stack` instead. | ![badge-performance][] | | |
| [no-use-is-focused](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/no-use-is-focused.md) | Disallow importing `useIsFocused` from `@react-navigation/native` to encourage using `useFocusEffect` instead. | ![badge-performance][] | | 🔧 |
| [prefer-user-event](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/prefer-user-event.md) | Enforces usage of userEvent over fireEvent in tests. | | | 🔧 |
| [require-named-effect](https://github.com/bamlab/react-native-project-config/blob/main/packages/eslint-plugin/docs/rules/require-named-effect.md) | Enforces the use of named functions inside a useEffect | | | |

<!-- end auto-generated rules list -->

Expand Down
7 changes: 7 additions & 0 deletions packages/eslint-plugin/docs/rules/avoid-intl-number-format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Disallow the use of `Intl.NumberFormat` due to potential performance issues (`@bam.tech/avoid-intl-number-format`)

💼 This rule is enabled in the `performance` config.

<!-- end auto-generated rule header -->

Prevents from the using `Intl.NumberFormat` to improve performance.
19 changes: 19 additions & 0 deletions packages/eslint-plugin/docs/rules/avoid-react-native-svg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Disallow importing the `react-native-svg` package (`@bam.tech/avoid-react-native-svg`)

⚠️ This rule _warns_ in the `performance` config.

<!-- end auto-generated rule header -->

Prevents from using "react-native-svg" import to avoid performance issues.

## Rule details

Examples of **incorrect** code for this rule:

```jsx
import Svg from "react-native-svg";
```

```jsx
const Svg = require("react-native-svg");
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Disallow the use of `Animated` with `useNativeDriver: false` (`@bam.tech/no-animated-without-native-driver`)

💼 This rule is enabled in the `performance` config.

<!-- end auto-generated rule header -->

Enforces the usage of native driver when using `Animated` from `react-native` to improve performance.

## Rule details

Example of **incorrect** code for this rule:

```jsx
Animated.timing(fadeAnim, {
toValue: 1,
duration: 500,
useNativeDriver: false,
}).start();
```

Example of **correct** code for this rule:

```jsx
Animated.timing(fadeAnim, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
```
27 changes: 27 additions & 0 deletions packages/eslint-plugin/docs/rules/no-flatlist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Disallow importing `FlatList` from `react-native` due to potential performance concerns or the preference for alternative components (`@bam.tech/no-flatlist`)

💼 This rule is enabled in the `performance` config.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->

Prevents from using "FlatList" import to avoid performance issues. FlashList should be used instead.

## Rule details

Examples of **incorrect** code for this rule:

```jsx
import { FlatList } from "react-native";
```

```jsx
import { FlatList, SectionList } from "react-native";
```

Examples of **correct** alternative for this rule:

```jsx
import { FlashList } from "@shopify/flash-list";
```
21 changes: 21 additions & 0 deletions packages/eslint-plugin/docs/rules/no-react-navigation-stack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Disallow importing from `@react-navigation/stack` and suggest using `@react-navigation/native-stack` instead (`@bam.tech/no-react-navigation-stack`)

💼 This rule is enabled in the `performance` config.

<!-- end auto-generated rule header -->

Prevents from using "react-navigation/stack" import to avoid performance issues. "react-navigation/native-stack" should be used instead.

## Rule details

Examples of **incorrect** code for this rule:

```jsx
import { createStackNavigator } from "@react-navigation/stack";
```

Examples of **correct** alternative for this rule:

```jsx
import { createStackNavigator } from "@react-navigation/native-stack";
```
17 changes: 17 additions & 0 deletions packages/eslint-plugin/docs/rules/no-use-is-focused.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Disallow importing `useIsFocused` from `@react-navigation/native` to encourage using `useFocusEffect` instead (`@bam.tech/no-use-is-focused`)

💼 This rule is enabled in the `performance` config.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->

Prevents from using "useIsFocused" to avoid performance issues. "useFocusEffect" should be used instead.

## Rule details

Examples of **incorrect** code for this rule:

```jsx
import { useIsFocused } from "@react-navigation/native";
```
2 changes: 1 addition & 1 deletion packages/eslint-plugin/lib/configs/a11y.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineConfig } from "eslint-define-config";

export const a11yconfig = defineConfig({
export const a11yConfig = defineConfig({
extends: ["plugin:react-native-a11y/all"],
rules: {
"react-native-a11y/has-accessibility-hint": "off",
Expand Down
6 changes: 4 additions & 2 deletions packages/eslint-plugin/lib/configs/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { a11yconfig } from "./a11y";
import { a11yConfig } from "./a11y";
import { importConfig } from "./import";
import { recommendedConfig } from "./recommended";
import { testsConfig } from "./tests";
import { performanceConfig } from "./performance";

export default {
recommended: recommendedConfig,
tests: testsConfig,
a11y: a11yconfig,
a11y: a11yConfig,
import: importConfig,
performance: performanceConfig,
};
17 changes: 17 additions & 0 deletions packages/eslint-plugin/lib/configs/performance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineConfig } from "eslint-define-config";

export const performanceConfig = defineConfig({
rules: {
"@bam.tech/no-animated-without-native-driver": "error",
"@bam.tech/avoid-intl-number-format": "error",
"@bam.tech/avoid-react-native-svg": "warn",
"@bam.tech/no-flatlist": "error",
"@bam.tech/no-react-navigation-stack": "error",
"@bam.tech/no-use-is-focused": "error",
},
overrides: [
{
files: ["*.js"],
},
],
});
Loading
Loading