Skip to content

Commit 5d6b985

Browse files
committed
BREAKING CHANGE: refactor -> create custom rule for FlatList import to prevent from overriding with the existing import rule
1 parent e825692 commit 5d6b985

File tree

6 files changed

+137
-14
lines changed

6 files changed

+137
-14
lines changed

packages/eslint-plugin/README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,20 @@ This plugin exports some custom rules that you can optionally use in your projec
104104

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

110-
| Name                              | Description | 💼 | ⚠️ | 🔧 |
111-
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :--------------------- | :--------------------- | :-- |
112-
| [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][] | | 🔧 |
113-
| [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][] | 🔧 |
114-
| [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 | 🧪 | | 🔧 |
115-
| [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][] | | 🔧 |
116-
| [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. | | | 🔧 |
117-
| [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 | | | |
111+
| Name                              | Description | 💼 | ⚠️ | 🔧 |
112+
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :--------------------- | :--------------------- | :-- |
113+
| [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][] | | 🔧 |
114+
| [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][] | 🔧 |
115+
| [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 | 🧪 | | 🔧 |
116+
| [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][] | | 🔧 |
117+
| [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 || | 🔧 |
118+
| [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][] | | 🔧 |
119+
| [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. | | | 🔧 |
120+
| [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 | | | |
118121

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Disallow importing `FlatList` from `react-native` due to potential performance concerns or the preference for alternative components (`@bam.tech/no-flatlist`)
2+
3+
💼 This rule is enabled in the `performance` config.
4+
5+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6+
7+
<!-- end auto-generated rule header -->
8+
9+
Prevents from using "FlatList" import to avoid performance issues. FlashList should be used instead.
10+
11+
## Rule details
12+
13+
Examples of **incorrect** code for this rule:
14+
15+
```jsx
16+
import { FlatList } from "react-native";
17+
```
18+
19+
```jsx
20+
import { FlatList, SectionList } from "react-native";
21+
```
22+
23+
Examples of **correct** alternative for this rule:
24+
25+
```jsx
26+
import { FlashList } from "@shopify/flash-list";
27+
```

packages/eslint-plugin/lib/configs/performance.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ export const performanceConfig = defineConfig({
66
"error",
77
{
88
paths: [
9-
{
10-
name: "react-native",
11-
importNames: ["FlatList"],
12-
message:
13-
"Please use FlashList from @shopify/flash-list instead of FlatList from react-native.",
14-
},
159
{
1610
name: "@react-navigation/stack",
1711
message:
@@ -29,6 +23,7 @@ export const performanceConfig = defineConfig({
2923
"@bam.tech/no-animated-without-native-driver": "error",
3024
"@bam.tech/avoid-intl-number-format": "error",
3125
"@bam.tech/avoid-react-native-svg": "warn",
26+
"@bam.tech/no-flatlist": "error",
3227
},
3328
overrides: [
3429
{

packages/eslint-plugin/lib/rules/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { noDifferentDisplaynameRule } from "./no-different-displayname";
55
import { noAnimatedWithoutNativeDriverRule } from "./no-animated-without-native-driver";
66
import { preferUserEventRule } from "./prefer-user-event";
77
import { requireNamedEffectRule } from "./require-named-effect";
8+
import { noFlatListImportRule } from "./no-flatlist";
89

910
export default {
1011
"await-user-event": awaitUserEventRule,
@@ -14,4 +15,5 @@ export default {
1415
"no-animated-without-native-driver": noAnimatedWithoutNativeDriverRule,
1516
"avoid-intl-number-format": avoidIntlNumberFormatRule,
1617
"avoid-react-native-svg": avoidReactNativeSvgImportRule,
18+
"no-flatlist": noFlatListImportRule,
1719
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { Rule } from "eslint";
2+
3+
// Custom Rule: No FlatList Import
4+
export const noFlatListImportRule: Rule.RuleModule = {
5+
meta: {
6+
type: "problem",
7+
docs: {
8+
description:
9+
"Disallow importing `FlatList` from `react-native` due to potential performance concerns or the preference for alternative components.",
10+
category: "Possible Errors",
11+
recommended: true,
12+
url: "https://github.com/bamlab/react-native-project-config/tree/main/packages/eslint-plugin/docs/rules/no-flatlist-import.md",
13+
},
14+
messages: {
15+
noFlatListImport:
16+
"Please use FlashList from @shopify/flash-list instead of FlatList from react-native.",
17+
},
18+
schema: [],
19+
fixable: "code",
20+
},
21+
22+
create(context) {
23+
return {
24+
ImportDeclaration(node) {
25+
if (
26+
node.source.value === "react-native" &&
27+
node.specifiers.some(
28+
(specifier) =>
29+
specifier.type === "ImportSpecifier" &&
30+
specifier.imported.name === "FlatList",
31+
)
32+
) {
33+
context.report({
34+
node,
35+
messageId: "noFlatListImport",
36+
});
37+
}
38+
},
39+
VariableDeclarator(node) {
40+
if (
41+
node.init &&
42+
node.init.type === "CallExpression" &&
43+
node.init.callee.type === "Identifier" &&
44+
node.init.callee.name === "require" &&
45+
node.init.arguments.length > 0 &&
46+
node.init.arguments[0].type === "Literal" &&
47+
node.init.arguments[0].value === "react-native"
48+
) {
49+
const flatListBinding =
50+
node.id.type === "ObjectPattern" &&
51+
node.id.properties.some(
52+
(property) =>
53+
property.type === "Property" &&
54+
property.key.type === "Identifier" &&
55+
property.key.name === "FlatList",
56+
);
57+
58+
if (flatListBinding) {
59+
context.report({
60+
node,
61+
messageId: "noFlatListImport",
62+
});
63+
}
64+
}
65+
},
66+
};
67+
},
68+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Save without formatting: [⌘ + K] > [S]
2+
3+
// This should trigger an error breaking eslint-plugin-bam-custom-rules:
4+
// bam-custom-rules/avoid-react-native-svg
5+
6+
import { noFlatListImportRule } from "../../../lib/rules/no-flatlist";
7+
import { RuleTester } from "eslint";
8+
9+
const ruleTester = new RuleTester({
10+
parser: require.resolve("@typescript-eslint/parser"),
11+
});
12+
13+
const valid = [``];
14+
15+
const invalid = [
16+
`import { FlatList } from "react-native";`,
17+
`import { FlatList, SectionList} from 'react-native';`,
18+
];
19+
20+
ruleTester.run("no-flatlist", noFlatListImportRule, {
21+
valid,
22+
invalid: invalid.map((code) => ({
23+
code,
24+
errors: [
25+
"Please use FlashList from @shopify/flash-list instead of FlatList from react-native.",
26+
],
27+
})),
28+
});

0 commit comments

Comments
 (0)