Skip to content

Commit 92d7a57

Browse files
committed
BREAKING CHANGE: prevents the usage of Intl.NumberFormat for performance purposes
1 parent 3f5064a commit 92d7a57

File tree

7 files changed

+116
-6
lines changed

7 files changed

+116
-6
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// This code should trigger the ESLint rule `avoidIntlNumberFormatRule`
2+
3+
const number = 1234567.89;
4+
5+
// Incorrect usage: This will be flagged by the ESLint rule
6+
const formatter = new Intl.NumberFormat("en-US", {
7+
style: "currency",
8+
currency: "USD",
9+
});
10+
11+
const formattedNumber = formatter.format(number);
12+
13+
// eslint-disable-next-line no-console
14+
console.log(formattedNumber); // Outputs: $1,234,567.89

packages/eslint-plugin/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,13 @@ 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-
| [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 | | |
109+
| Name                              | Description | 💼 | 🔧 |
110+
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :--------------------- | :-- |
111+
| [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][] | 🔧 |
112+
| [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 | 🧪 | 🔧 |
113+
| [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][] | 🔧 |
114+
| [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. | | 🔧 |
115+
| [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 | | |
115116

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Disallow the use of `Intl.NumberFormat` due to potential performance issues (`@bam.tech/avoid-intl-number-format`)
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 the using `Intl.NumberFormat` to improve performance.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const performanceConfig = defineConfig({
3232
},
3333
],
3434
"@bam.tech/no-animated-without-native-driver": "error",
35+
"@bam.tech/avoid-intl-number-format": "error",
3536
},
3637
overrides: [
3738
{
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { Rule } from "eslint";
2+
3+
// Custom Rule: No Intl.NumberFormat Usage
4+
export const avoidIntlNumberFormatRule: Rule.RuleModule = {
5+
meta: {
6+
type: "problem",
7+
docs: {
8+
description:
9+
"Disallow the use of `Intl.NumberFormat` due to potential performance issues.",
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-intl-numberformat.md",
13+
},
14+
messages: {
15+
noIntlNumberFormat:
16+
"Avoid using `Intl.NumberFormat` as it can lead to performance issues. Consider using a lightweight formatting alternative or memoizing the formatter instance.",
17+
},
18+
schema: [],
19+
fixable: "code",
20+
},
21+
22+
create(context) {
23+
return {
24+
NewExpression(node) {
25+
if (
26+
node.callee.type === "MemberExpression" &&
27+
node.callee.object.type === "Identifier" &&
28+
node.callee.object.name === "Intl" &&
29+
node.callee.property.type === "Identifier" &&
30+
node.callee.property.name === "NumberFormat"
31+
) {
32+
context.report({
33+
node,
34+
messageId: "noIntlNumberFormat",
35+
});
36+
}
37+
},
38+
};
39+
},
40+
};

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { avoidIntlNumberFormatRule } from "./avoid-intl-number-format";
12
import { awaitUserEventRule } from "./await-user-event";
23
import { noAnimatedWithoutNativeDriverRule } from "./no-animated-without-native-driver";
34
import { preferUserEventRule } from "./prefer-user-event";
@@ -8,4 +9,5 @@ export default {
89
"prefer-user-event": preferUserEventRule,
910
"require-named-effect": requireNamedEffectRule,
1011
"no-animated-without-native-driver": noAnimatedWithoutNativeDriverRule,
12+
"avoid-intl-number-format": avoidIntlNumberFormatRule,
1113
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Save without formatting: [⌘ + K] > [S]
2+
3+
// This should trigger an error breaking eslint-plugin-bam-custom-rules:
4+
// bam-custom-rules/avoid-intl-number-format
5+
6+
import { avoidIntlNumberFormatRule } from "../../../lib/rules/avoid-intl-number-format";
7+
import { RuleTester } from "eslint";
8+
9+
const ruleTester = new RuleTester({
10+
parser: require.resolve("@typescript-eslint/parser"),
11+
});
12+
13+
const valid = [
14+
` const formatCurrency = (number: number) => {
15+
return numeral(number).format('$0,0.00');
16+
};
17+
18+
const number = 1234567.89;
19+
console.log(formatCurrency(number));`,
20+
];
21+
22+
const invalid = [
23+
` const number = 1234567.89;
24+
25+
const formatter = new Intl.NumberFormat('en-US', {
26+
style: 'currency',
27+
currency: 'USD',
28+
});
29+
30+
const formattedNumber = formatter.format(number);
31+
32+
console.log(formattedNumber);`,
33+
];
34+
35+
ruleTester.run("no-animated-without-native-driver", avoidIntlNumberFormatRule, {
36+
valid,
37+
invalid: invalid.map((code) => ({
38+
code,
39+
errors: [
40+
"Avoid using `Intl.NumberFormat` as it can lead to performance issues. Consider using a lightweight formatting alternative or memoizing the formatter instance.",
41+
],
42+
})),
43+
});

0 commit comments

Comments
 (0)