Skip to content

Commit 3f5064a

Browse files
committed
BREAKING CHANGE: add custom rule for native driver use with Animated to performance rules
1 parent fcfc066 commit 3f5064a

File tree

7 files changed

+141
-5
lines changed

7 files changed

+141
-5
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Animated } from "react-native";
2+
3+
const fadeAnim = new Animated.Value(0);
4+
5+
Animated.timing(fadeAnim, {
6+
toValue: 1,
7+
duration: 500,
8+
useNativeDriver: false, // This line breaks the custom rule
9+
}).start();

packages/eslint-plugin/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,12 @@ This plugin exports some custom rules that you can optionally use in your projec
106106
🧪 Set in the `tests` configuration.\
107107
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
108108

109-
| Name | Description | 💼 | 🔧 |
110-
| :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------- | :-- | :-- |
111-
| [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 | 🧪 | 🔧 |
112-
| [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. | | 🔧 |
113-
| [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 | | |
109+
| Name                              | Description | 💼 | 🔧 |
110+
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------- | :--------------------- | :-- |
111+
| [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 | 🧪 | 🔧 |
112+
| [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][] | 🔧 |
113+
| [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. | | 🔧 |
114+
| [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 | | |
114115

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Disallow the use of `Animated` with `useNativeDriver: false` (`@bam.tech/no-animated-without-native-driver`)
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+
Enforces the usage of native driver when using `Animated` from `react-native` to improve performance.
10+
11+
## Rule details
12+
13+
Example of **incorrect** code for this rule:
14+
15+
```jsx
16+
Animated.timing(fadeAnim, {
17+
toValue: 1,
18+
duration: 500,
19+
useNativeDriver: false,
20+
}).start();
21+
```
22+
23+
Example of **correct** code for this rule:
24+
25+
```jsx
26+
Animated.timing(fadeAnim, {
27+
toValue: 1,
28+
duration: 500,
29+
useNativeDriver: true,
30+
}).start();
31+
```

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const performanceConfig = defineConfig({
3131
],
3232
},
3333
],
34+
"@bam.tech/no-animated-without-native-driver": "error",
3435
},
3536
overrides: [
3637
{
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { awaitUserEventRule } from "./await-user-event";
2+
import { noAnimatedWithoutNativeDriverRule } from "./no-animated-without-native-driver";
23
import { preferUserEventRule } from "./prefer-user-event";
34
import { requireNamedEffectRule } from "./require-named-effect";
45

56
export default {
67
"await-user-event": awaitUserEventRule,
78
"prefer-user-event": preferUserEventRule,
89
"require-named-effect": requireNamedEffectRule,
10+
"no-animated-without-native-driver": noAnimatedWithoutNativeDriverRule,
911
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { Rule } from "eslint";
2+
3+
// Custom Rule: No Animated with useNativeDriver: false
4+
export const noAnimatedWithoutNativeDriverRule: Rule.RuleModule = {
5+
meta: {
6+
type: "problem",
7+
docs: {
8+
description:
9+
"Disallow the use of `Animated` with `useNativeDriver: false`",
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-animated-without-native-driver.md",
13+
},
14+
messages: {
15+
noNativeDriverFalse:
16+
"Do not use Animated with useNativeDriver: false. Always set useNativeDriver: true for better performance.",
17+
},
18+
schema: [],
19+
fixable: "code",
20+
},
21+
22+
create(context) {
23+
return {
24+
CallExpression(node) {
25+
if (
26+
node.callee.type === "MemberExpression" &&
27+
node.callee.object.type === "Identifier" &&
28+
node.callee.object.name === "Animated" &&
29+
node.arguments.length > 0 &&
30+
node.arguments[1].type === "ObjectExpression"
31+
) {
32+
const useNativeDriverProperty = node.arguments[1].properties.find(
33+
(prop) =>
34+
prop.type === "Property" &&
35+
prop.key.type === "Identifier" &&
36+
prop.key.name === "useNativeDriver" &&
37+
prop.value.type === "Literal" &&
38+
prop.value.value === false,
39+
);
40+
41+
if (useNativeDriverProperty) {
42+
context.report({
43+
node: useNativeDriverProperty,
44+
messageId: "noNativeDriverFalse",
45+
});
46+
}
47+
}
48+
},
49+
};
50+
},
51+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Save without formatting: [⌘ + K] > [S]
2+
3+
// This should trigger an error breaking eslint-plugin-bam-custom-rules:
4+
// bam-custom-rules/no-animated-without-native-driver
5+
6+
import { noAnimatedWithoutNativeDriverRule } from "../../../lib/rules/no-animated-without-native-driver";
7+
import { RuleTester } from "eslint";
8+
9+
const ruleTester = new RuleTester({
10+
parser: require.resolve("@typescript-eslint/parser"),
11+
});
12+
13+
const valid = [
14+
`Animated.timing(fadeAnim, {
15+
toValue: 1,
16+
duration: 500,
17+
useNativeDriver: true,
18+
}).start();`,
19+
];
20+
21+
const invalid = [
22+
`Animated.timing(fadeAnim, {
23+
toValue: 1,
24+
duration: 500,
25+
useNativeDriver: false,
26+
}).start();`,
27+
];
28+
29+
ruleTester.run(
30+
"no-animated-without-native-driver",
31+
noAnimatedWithoutNativeDriverRule,
32+
{
33+
valid,
34+
invalid: invalid.map((code) => ({
35+
code,
36+
errors: [
37+
"Do not use Animated with useNativeDriver: false. Always set useNativeDriver: true for better performance.",
38+
],
39+
})),
40+
},
41+
);

0 commit comments

Comments
 (0)