Skip to content

Commit 3d5458e

Browse files
committed
BREAKING CHANGE: refactor -> create custom rule for import of rn stack to prevent override with eslint import rule
1 parent f074874 commit 3d5458e

File tree

7 files changed

+107
-7
lines changed

7 files changed

+107
-7
lines changed

example-app/eslint-breaking-examples/break-react-navigation-stack-import-rule.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Save without formatting: [⌘ + K] > [S]
22

3-
// This should trigger one error breaking eslint-plugin-react-native:
4-
// no-restricted-imports
3+
// This should trigger one error breaking custom react-navigation/stack rule:
4+
// @bam.tech/no-react-navigation-stack
55

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

packages/eslint-plugin/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ This plugin exports some custom rules that you can optionally use in your projec
116116
| [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][] | | |
117117
| [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 || | 🔧 |
118118
| [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+
| [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][] | | |
120+
| [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][] | | 🔧 |
119121
| [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. | | | 🔧 |
120122
| [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 | | | |
121123

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Disallow importing from `@react-navigation/stack` and suggest using `@react-navigation/native-stack` instead (`@bam.tech/no-react-navigation-stack`)
2+
3+
💼 This rule is enabled in the `performance` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
Prevents from using "react-navigation/stack" import to avoid performance issues. "react-navigation/native-stack" should be used instead.
8+
9+
## Rule details
10+
11+
Examples of **incorrect** code for this rule:
12+
13+
```jsx
14+
import { createStackNavigator } from "@react-navigation/stack";
15+
```
16+
17+
Examples of **correct** alternative for this rule:
18+
19+
```jsx
20+
import { createStackNavigator } from "@react-navigation/native-stack";
21+
```

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ export const performanceConfig = defineConfig({
66
"error",
77
{
88
paths: [
9-
{
10-
name: "@react-navigation/stack",
11-
message:
12-
'Please use "@react-navigation/native-stack" instead of "@react-navigation/stack".',
13-
},
149
{
1510
name: "@react-navigation/native",
1611
importNames: ["useIsFocused"],
@@ -24,6 +19,7 @@ export const performanceConfig = defineConfig({
2419
"@bam.tech/avoid-intl-number-format": "error",
2520
"@bam.tech/avoid-react-native-svg": "warn",
2621
"@bam.tech/no-flatlist": "error",
22+
"@bam.tech/no-react-navigation-stack": "error",
2723
},
2824
overrides: [
2925
{

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { noAnimatedWithoutNativeDriverRule } from "./no-animated-without-native-
66
import { preferUserEventRule } from "./prefer-user-event";
77
import { requireNamedEffectRule } from "./require-named-effect";
88
import { noFlatListImportRule } from "./no-flatlist";
9+
import { noReactNavigationStackImportRule } from "./no-react-navigation-stack";
910

1011
export default {
1112
"await-user-event": awaitUserEventRule,
@@ -16,4 +17,5 @@ export default {
1617
"avoid-intl-number-format": avoidIntlNumberFormatRule,
1718
"avoid-react-native-svg": avoidReactNativeSvgImportRule,
1819
"no-flatlist": noFlatListImportRule,
20+
"no-react-navigation-stack": noReactNavigationStackImportRule,
1921
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { Rule } from "eslint";
2+
3+
// Custom Rule: No Import from @react-navigation/stack
4+
export const noReactNavigationStackImportRule: Rule.RuleModule = {
5+
meta: {
6+
type: "problem",
7+
docs: {
8+
description:
9+
"Disallow importing from `@react-navigation/stack` and suggest using `@react-navigation/native-stack` instead.",
10+
category: "Best Practices",
11+
recommended: true,
12+
url: "https://github.com/bamlab/react-native-project-config/tree/main/packages/eslint-plugin/docs/rules/no-react-navigation-stack.md",
13+
},
14+
messages: {
15+
noReactNavigationStackImport:
16+
'"@react-navigation/native-stack" provides out of the box native screens and native transitions for better performance and user experience.',
17+
},
18+
schema: [],
19+
},
20+
21+
create(context) {
22+
return {
23+
ImportDeclaration(node) {
24+
// Check if the import is from "@react-navigation/stack"
25+
if (node.source.value === "@react-navigation/stack") {
26+
context.report({
27+
node,
28+
messageId: "noReactNavigationStackImport",
29+
});
30+
}
31+
},
32+
CallExpression(node) {
33+
// Check if require() is used to import "@react-navigation/stack"
34+
if (
35+
node.callee.type === "Identifier" &&
36+
node.callee.name === "require" &&
37+
node.arguments.length > 0 &&
38+
node.arguments[0].type === "Literal" &&
39+
node.arguments[0].value === "@react-navigation/stack"
40+
) {
41+
context.report({
42+
node,
43+
messageId: "noReactNavigationStackImport",
44+
});
45+
}
46+
},
47+
};
48+
},
49+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Save without formatting: [⌘ + K] > [S]
2+
3+
// This should trigger an error breaking eslint-plugin-bam-custom-rules:
4+
// bam-custom-rules/no-react-navigation-stack
5+
6+
import { noReactNavigationStackImportRule } from "../../../lib/rules/no-react-navigation-stack";
7+
import { RuleTester } from "eslint";
8+
9+
const ruleTester = new RuleTester({
10+
parser: require.resolve("@typescript-eslint/parser"),
11+
});
12+
13+
const valid = [
14+
`import { createStackNavigator } from "@react-navigation/native-stack";`,
15+
];
16+
17+
const invalid = [
18+
`import { createStackNavigator } from "@react-navigation/stack";`,
19+
`import {createStackNavigator} from '@react-navigation/stack';`,
20+
];
21+
22+
ruleTester.run("no-react-navigation-stack", noReactNavigationStackImportRule, {
23+
valid,
24+
invalid: invalid.map((code) => ({
25+
code,
26+
errors: [
27+
`"@react-navigation/native-stack" provides out of the box native screens and native transitions for better performance and user experience.`,
28+
],
29+
})),
30+
});

0 commit comments

Comments
 (0)