Skip to content

Commit 9347cfe

Browse files
committed
feat: Add React Native Support
- Add a new react-native configuration module, integrating @react-native/eslint-config - Update the main configuration logic to support React Native options - Adjust React configuration to filter out DOM-related rules when React Native is enabled - Update type definitions and exports to include React Native modules - Add @react-native/eslint-config as a development dependency - Simplify the team description in the README to general instructions
1 parent 3f2e8d2 commit 9347cfe

File tree

9 files changed

+1506
-11
lines changed

9 files changed

+1506
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @sj-distributor/eslint-config
22

3-
The ESLint configuration for the SJ Distributor team, based on the latest ESLint Flat Config (v9+), offering out-of-the-box best practices for React + TypeScript.
3+
Based on the latest ESLint Flat Config (v9+), offering out-of-the-box best practices for React + TypeScript.
44

55
[中文文档](./README_CN.md)
66

README_CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @sj-distributor/eslint-config
22

3-
SJ Distributor 团队专用的 ESLint 配置,基于最新的 ESLint Flat Config (v9+),提供开箱即用的 React + TypeScript 最佳实践。
3+
基于最新的 ESLint Flat Config (v9+),提供开箱即用的 React + TypeScript 最佳实践。
44

55
[English Documentation](./README.md)
66

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"typescript-eslint": "^8.56.1"
4141
},
4242
"devDependencies": {
43+
"@react-native/eslint-config": "^0.84.1",
4344
"@types/node": "^25.3.0",
4445
"eslint": "^10.0.2",
4546
"eslint-typegen": "^2.3.1",

pnpm-lock.yaml

Lines changed: 1387 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/configs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './ignores';
22
export * from './javascript';
33
export * from './react';
4+
export * from './react-native';
45
export * from './stylistic';
56
export * from './typescript';
67
export * from './unicorn';

src/configs/react-native.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import type { TypedFlatConfigItem } from '../types';
2+
import type { Overrides } from '../types';
3+
import { interopDefault } from '../utils';
4+
5+
export interface ReactNativeOptions {
6+
/**
7+
* Files to apply the React Native rules to.
8+
* @default ['**\\/*.{js,jsx,mjs,cjs,ts,tsx}']
9+
*/
10+
files?: string[];
11+
12+
/**
13+
* Override React Native rules.
14+
*/
15+
overrides?: Overrides;
16+
}
17+
18+
export async function reactNative(
19+
options: ReactNativeOptions = {},
20+
): Promise<TypedFlatConfigItem[]> {
21+
const {
22+
files = ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
23+
overrides = {},
24+
} = options;
25+
26+
try {
27+
const rnConfigPath = '@react-native/eslint-config/flat';
28+
const [
29+
reactNativeConfig,
30+
] = await Promise.all([
31+
interopDefault(import(rnConfigPath)),
32+
] as const);
33+
34+
return [
35+
{
36+
name: 'sj-distributor/react-native/setup',
37+
languageOptions: {
38+
globals: {
39+
__DEV__: 'readonly',
40+
},
41+
},
42+
},
43+
...(Array.isArray(reactNativeConfig) ? reactNativeConfig : [reactNativeConfig]),
44+
{
45+
name: 'sj-distributor/react-native/overrides',
46+
files,
47+
rules: overrides,
48+
},
49+
];
50+
}
51+
catch (error) {
52+
if (error instanceof Error && 'code' in error && (error as any).code === 'ERR_MODULE_NOT_FOUND') {
53+
throw new Error(
54+
'You have enabled React Native support but "@react-native/eslint-config" is not installed.\n'
55+
+ 'Please install it using your package manager.\n'
56+
+ 'e.g. pnpm add -D @react-native/eslint-config',
57+
);
58+
}
59+
throw error;
60+
}
61+
}

src/configs/react.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ export interface ReactOptions {
1515
*/
1616
typescript?: boolean;
1717

18+
/**
19+
* Enable React Native support.
20+
* @default false
21+
*/
22+
reactNative?: boolean;
23+
1824
/**
1925
* Override React rules.
2026
*/
@@ -27,6 +33,7 @@ export async function react(
2733
const {
2834
files = ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
2935
typescript = true,
36+
reactNative = false,
3037
overrides = {},
3138
} = options;
3239

@@ -40,20 +47,43 @@ export async function react(
4047
interopDefault(import('eslint-plugin-react-refresh')),
4148
] as const);
4249

50+
// eslint-disable-next-line ts/no-explicit-any
4351
const plugins = (pluginReact.configs.all as any).plugins;
4452

53+
let baseRules = {
54+
// recommended rules from @eslint-react/eslint-plugin
55+
...pluginReact.configs.recommended.rules,
56+
...(typescript ? pluginReact.configs['recommended-typescript'].rules : {}),
57+
};
58+
59+
if (reactNative) {
60+
// eslint-disable-next-line ts/no-explicit-any
61+
const filteredRules: Record<string, any> = {};
62+
for (const key of Object.keys(baseRules)) {
63+
if (!key.startsWith('@eslint-react/dom/') && !key.startsWith('@eslint-react/web-api/')) {
64+
// eslint-disable-next-line ts/no-explicit-any
65+
filteredRules[key] = (baseRules as any)[key];
66+
}
67+
}
68+
baseRules = filteredRules;
69+
}
70+
4571
return [
4672
{
4773
name: 'sj-distributor/react/setup',
4874
plugins: {
4975
'react-hooks': pluginReactHooks,
5076
'react-refresh': pluginReactRefresh,
5177
'@eslint-react': plugins['@eslint-react'],
52-
'@eslint-react/dom': plugins['@eslint-react/dom'],
78+
...(reactNative
79+
? {}
80+
: {
81+
'@eslint-react/dom': plugins['@eslint-react/dom'],
82+
'@eslint-react/web-api': plugins['@eslint-react/web-api'],
83+
}),
5384
'@eslint-react/hooks-extra': plugins['@eslint-react/hooks-extra'],
5485
'@eslint-react/naming-convention': plugins['@eslint-react/naming-convention'],
5586
'@eslint-react/rsc': plugins['@eslint-react/rsc'],
56-
'@eslint-react/web-api': plugins['@eslint-react/web-api'],
5787
},
5888
},
5989
{
@@ -68,9 +98,7 @@ export async function react(
6898
sourceType: 'module',
6999
},
70100
rules: {
71-
// recommended rules from @eslint-react/eslint-plugin
72-
...pluginReact.configs.recommended.rules,
73-
...(typescript ? pluginReact.configs['recommended-typescript'].rules : {}),
101+
...baseRules,
74102

75103
// react-hooks
76104
...pluginReactHooks.configs.recommended.rules,

src/main.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ignores, javascript, react, stylistic, typescript, unicorn } from './configs';
1+
import { ignores, javascript, react, reactNative, stylistic, typescript, unicorn } from './configs';
22
import type { AvengerOptions, UserConfig } from './types';
33

44
export async function avenger(
@@ -7,6 +7,7 @@ export async function avenger(
77
): Promise<UserConfig[]> {
88
const {
99
react: enableReact = false,
10+
reactNative: enableReactNative = false,
1011
typescript: enableTypescript = true,
1112
stylistic: enableStylistic = true,
1213
unicorn: enableUnicorn = true,
@@ -33,21 +34,30 @@ export async function avenger(
3334
if (enableTypescript && reactOptions.typescript === undefined) {
3435
reactOptions.typescript = true;
3536
}
37+
if (enableReactNative && reactOptions.reactNative === undefined) {
38+
reactOptions.reactNative = true;
39+
}
3640
configs.push(...(await react(reactOptions)));
3741
}
3842

39-
// 5. Stylistic
43+
// 5. React Native
44+
if (enableReactNative) {
45+
const reactNativeOptions = typeof enableReactNative === 'object' ? enableReactNative : {};
46+
configs.push(...(await reactNative(reactNativeOptions)));
47+
}
48+
49+
// 6. Stylistic
4050
if (enableStylistic) {
4151
const stylisticOptions = typeof enableStylistic === 'object' ? enableStylistic : {};
4252
configs.push(...stylistic(stylisticOptions));
4353
}
4454

45-
// 6. Unicorn
55+
// 7. Unicorn
4656
if (enableUnicorn) {
4757
configs.push(...unicorn());
4858
}
4959

50-
// 7. User overrides
60+
// 8. User overrides
5161
if (userConfigs.length > 0) {
5262
configs.push(...userConfigs);
5363
}

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Linter } from 'eslint';
22
import type { ReactOptions } from './configs/react';
3+
import type { ReactNativeOptions } from './configs/react-native';
34
import type { StylisticOptions } from './configs/stylistic';
45
import type { RuleOptions } from './typegen';
56

@@ -36,6 +37,12 @@ export interface AvengerOptions {
3637
*/
3738
react?: boolean | ReactOptions;
3839

40+
/**
41+
* Enable React Native support.
42+
* @default false
43+
*/
44+
reactNative?: boolean | ReactNativeOptions;
45+
3946
/**
4047
* Enable TypeScript support.
4148
* @default true

0 commit comments

Comments
 (0)