Skip to content

Commit 4b5a71f

Browse files
committed
chore(lint): drop @react-native/eslint-config, bump @shopify/flash-list
Two related peer-warning fixes: 1) @shopify/flash-list -- examples/ExpoMessaging pinned 2.0.2 while core's peerDependency required >=2.1.0. Bump every workspace to a 2.3.x baseline: - examples/ExpoMessaging: 2.0.2 -> ^2.3.1 - examples/SampleApp: ^2.1.0 -> ^2.3.1 - package (peer): >=2.1.0 -> >=2.3.0 - package (devDep): ^2.1.0 -> ^2.3.1 2) @react-native/eslint-config (and its companion -plugin) -- drop both entirely. The config contributed: - No @react-native/* rules of its own (the plugin was unused). - ~120 mostly-default rule levels that overlap with what we already get from @eslint/js.recommended, tsEslint.configs.recommended and eslintPluginReact.configs.flat.recommended. - 43 globals, all but one (__DEV__) covered by the standard `globals` package presets (browser + node + es2021). The bundled @typescript-eslint/eslint-plugin@7.18.0 it dragged in was also the source of an "eslint 9.39.4 doesn't satisfy ^8.57.0" peer warning -- gone now that the package is removed. What changes in our config: - Replace dynamic globals/rules extraction with explicit values: globals.browser + globals.node + globals.es2021 + __DEV__. - Replace `...reactNativeRules` spread with explicit rule declarations. Behavior preserved for everything our codebase touches; a handful of recommended-preset rules (no-constant-condition, no-empty, no-inner-declarations, no-redeclare, react/display-name, react/no-unknown-property, react/react-in-jsx-scope) are silenced explicitly to match the prior state. - Re-enable three rules at `warn` that the RN config had on and that catch real footguns: react/no-unstable-nested-components, no-bitwise, no-extend-native. Keeps existing eslint-disable directives in example apps valid. - Fix the React-version warning placement: settings are now a standalone top-level config object so they cover eslintPluginReact.configs.flat.recommended (previously they only applied to the 'default' overlay, so the warning kept firing). Adds `globals: ^17.6.0` to root devDependencies. Removes @react-native/eslint-config and @react-native/eslint-plugin from root devDependencies.
1 parent 7980de5 commit 4b5a71f

6 files changed

Lines changed: 89 additions & 449 deletions

File tree

eslint.config.mjs

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
11
import jsEslint from '@eslint/js';
2-
import eslintReactNativeConfig from '@react-native/eslint-config';
3-
import eslintPluginReactNativeOfficial from '@react-native/eslint-plugin';
2+
import eslintConfigPrettier from 'eslint-config-prettier';
43
import eslintPluginComments from 'eslint-plugin-eslint-comments';
54
import eslintPluginImport from 'eslint-plugin-import';
65
import eslintPluginJest from 'eslint-plugin-jest';
76
import eslintPluginPrettier from 'eslint-plugin-prettier';
87
import eslintPluginReact from 'eslint-plugin-react';
98
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
109
import eslintPluginReactNative from 'eslint-plugin-react-native';
11-
10+
import globals from 'globals';
1211
import tsEslint from 'typescript-eslint';
13-
import eslintConfigPrettier from 'eslint-config-prettier';
1412

15-
/**
16-
* @react-native/eslint-config stores globals as `true`/`false`. We rebuild the
17-
* map with `'readonly'` so flat config accepts them.
18-
*/
19-
const reactNativeGlobals = Object.keys(eslintReactNativeConfig.globals ?? {}).reduce((acc, key) => {
20-
if (eslintReactNativeConfig.globals[key]) {
21-
acc[key] = 'readonly';
22-
}
23-
return acc;
24-
}, {});
25-
26-
const reactNativeRules = Object.fromEntries(
27-
Object.entries(eslintReactNativeConfig.rules ?? {}).filter(([key]) => !key.startsWith('jest/')),
28-
);
13+
// React Native runtime globals that aren't in the standard `globals` package presets.
14+
const reactNativeGlobals = {
15+
__DEV__: 'readonly',
16+
};
2917

3018
export default tsEslint.config(
3119
jsEslint.configs.recommended,
3220
tsEslint.configs.recommended,
3321
eslintPluginReact.configs.flat.recommended,
22+
// Settings that apply to every config object below. Without this, eslint-plugin-react's
23+
// recommended preset runs with no React version and logs "React version not specified"
24+
// on every invocation -- `detect` fails because React isn't a direct root dep.
25+
{ settings: { react: { version: '19' } } },
3426
{
3527
ignores: [
3628
// Dependencies and build outputs
@@ -90,7 +82,12 @@ export default tsEslint.config(
9082
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
9183
languageOptions: {
9284
ecmaVersion: 2020,
93-
globals: reactNativeGlobals,
85+
globals: {
86+
...globals.browser,
87+
...globals.node,
88+
...globals.es2021,
89+
...reactNativeGlobals,
90+
},
9491
parser: tsEslint.parser,
9592
parserOptions: {
9693
ecmaFeatures: {
@@ -101,7 +98,6 @@ export default tsEslint.config(
10198
sourceType: 'module',
10299
},
103100
plugins: {
104-
'@react-native': eslintPluginReactNativeOfficial,
105101
'eslint-comments': eslintPluginComments,
106102
import: eslintPluginImport,
107103
prettier: eslintPluginPrettier,
@@ -136,16 +132,30 @@ export default tsEslint.config(
136132
},
137133
},
138134
rules: {
139-
...reactNativeRules,
140135
...eslintConfigPrettier.rules,
136+
137+
// -- Silencing recommended-preset rules that don't fit this codebase.
138+
// These were previously masked by @react-native/eslint-config; we keep them off
139+
// explicitly to preserve behavior now that the config is owned in-tree.
140+
'no-constant-condition': 'off',
141+
'no-empty': 'off',
142+
'no-inner-declarations': 'off',
143+
'no-redeclare': 'off',
141144
'no-undef': 'off',
145+
'no-unused-vars': 'off',
146+
'react/display-name': 'off',
147+
'react/no-unknown-property': 'off',
148+
'react/react-in-jsx-scope': 'off',
149+
'react/prop-types': 'off',
150+
151+
// -- Project conventions.
142152
'prettier/prettier': 'warn',
143153
'arrow-body-style': 'off',
144154
'prefer-arrow-callback': 'off',
145-
'array-callback-return': 2,
146-
'comma-dangle': 0,
147-
'default-case': 2,
148-
eqeqeq: [2, 'smart'],
155+
'array-callback-return': 'error',
156+
'comma-dangle': 'off',
157+
'default-case': 'error',
158+
eqeqeq: ['error', 'smart'],
149159
// TypeScript already catches unresolved imports; the eslint-import-resolver-node
150160
// resolver doesn't understand Yarn workspace hoisting or TS path aliases, so we
151161
// turn this off repo-wide to avoid false positives in example apps.
@@ -170,23 +180,24 @@ export default tsEslint.config(
170180
},
171181
],
172182
'jsx-quotes': ['error', 'prefer-single'],
173-
'linebreak-style': [2, 'unix'],
174-
'no-console': 0,
175-
'no-mixed-spaces-and-tabs': 1,
176-
'no-self-compare': 2,
177-
'no-underscore-dangle': [2, { allowAfterThis: true }],
178-
'no-unused-vars': 'off',
179-
'no-useless-concat': 2,
180-
'no-var': 2,
181-
'object-shorthand': 1,
182-
'prefer-const': 1,
183-
'react/prop-types': 0,
184-
'require-await': 2,
185-
semi: [1, 'always'],
186-
'valid-typeof': 2,
187-
'@typescript-eslint/explicit-module-boundary-types': 0,
188-
'@typescript-eslint/no-empty-interface': 0,
189-
'@typescript-eslint/ban-ts-comment': 0,
183+
'linebreak-style': ['error', 'unix'],
184+
'no-console': 'off',
185+
'no-mixed-spaces-and-tabs': 'warn',
186+
'no-self-compare': 'error',
187+
'no-shadow': 'off',
188+
'no-underscore-dangle': ['error', { allowAfterThis: true }],
189+
'no-useless-concat': 'error',
190+
'no-var': 'error',
191+
'object-shorthand': 'warn',
192+
'prefer-const': 'warn',
193+
'require-await': 'error',
194+
semi: ['warn', 'always'],
195+
'valid-typeof': 'error',
196+
197+
// -- TypeScript-specific.
198+
'@typescript-eslint/explicit-module-boundary-types': 'off',
199+
'@typescript-eslint/no-empty-interface': 'off',
200+
'@typescript-eslint/ban-ts-comment': 'off',
190201
'@typescript-eslint/no-unused-vars': [
191202
'warn',
192203
{
@@ -197,13 +208,20 @@ export default tsEslint.config(
197208
},
198209
],
199210
'@typescript-eslint/no-unused-expressions': 'off',
200-
'@typescript-eslint/no-var-requires': 0,
211+
'@typescript-eslint/no-var-requires': 'off',
201212
'@typescript-eslint/no-require-imports': 'off',
202-
'react-hooks/exhaustive-deps': 1,
203-
'react-native/no-inline-styles': 0,
204-
'babel/no-invalid-this': 0,
205213
'@typescript-eslint/no-invalid-this': 'error',
206-
'no-shadow': 0,
214+
215+
// -- React / React Native.
216+
'react-hooks/exhaustive-deps': 'warn',
217+
'react-hooks/rules-of-hooks': 'error',
218+
'react-native/no-inline-styles': 'off',
219+
'react/no-unstable-nested-components': 'warn',
220+
221+
// -- Hygiene rules previously enabled via @react-native/eslint-config that we
222+
// kept after dropping the package -- they catch real footguns.
223+
'no-bitwise': 'warn',
224+
'no-extend-native': 'warn',
207225
},
208226
},
209227
{
@@ -221,10 +239,8 @@ export default tsEslint.config(
221239
'jest/expect-expect': 'off',
222240
'jest/no-conditional-expect': 'off',
223241
'jest/prefer-inline-snapshots': 'off',
224-
'jest/lowercase-name': 'off',
225242
'jest/prefer-expect-assertions': 'off',
226243
'jest/no-hooks': 'off',
227-
'jest/no-if': 'off',
228244
'jest/prefer-spy-on': 'off',
229245
},
230246
},

examples/ExpoMessaging/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"@react-navigation/elements": "^2.9.10",
2525
"@react-navigation/native": "^7.1.33",
2626
"@react-navigation/native-stack": "^7.14.5",
27-
"@shopify/flash-list": "2.0.2",
27+
"@shopify/flash-list": "^2.3.1",
2828
"expo": "^55.0.6",
2929
"expo-audio": "~55.0.8",
3030
"expo-build-properties": "~55.0.9",

examples/SampleApp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"@react-navigation/drawer": "7.4.1",
4545
"@react-navigation/native": "^7.1.19",
4646
"@react-navigation/native-stack": "^7.6.2",
47-
"@shopify/flash-list": "^2.1.0",
47+
"@shopify/flash-list": "^2.3.1",
4848
"emoji-mart": "^5.6.0",
4949
"lodash.mergewith": "^4.6.2",
5050
"react": "19.1.4",

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
"@commitlint/cli": "^21.0.0",
3838
"@commitlint/config-conventional": "^21.0.0",
3939
"@eslint/js": "^9.39.4",
40-
"@react-native/eslint-config": "^0.81.6",
41-
"@react-native/eslint-plugin": "^0.81.6",
4240
"@semantic-release/changelog": "^6.0.3",
4341
"@semantic-release/exec": "^6.0.3",
4442
"@semantic-release/git": "^10.0.1",
@@ -54,6 +52,7 @@
5452
"eslint-plugin-react-hooks": "^7.1.1",
5553
"eslint-plugin-react-native": "^5.0.0",
5654
"execa": "^5.1.1",
55+
"globals": "^17.6.0",
5756
"husky": "^9.1.7",
5857
"prettier": "^3.8.3",
5958
"semantic-release": "^25.0.3",

package/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
"@op-engineering/op-sqlite": "^14.0.3",
120120
"@react-native-community/netinfo": "^11.4.1",
121121
"@react-native/babel-preset": "0.79.3",
122-
"@shopify/flash-list": "^2.1.0",
122+
"@shopify/flash-list": "^2.3.1",
123123
"@testing-library/jest-native": "^5.4.3",
124124
"@testing-library/react-native": "13.2.0",
125125
"@total-typescript/shoehorn": "^0.1.2",

0 commit comments

Comments
 (0)