-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add reactNative preset + tsconfig (react-native-web ready) #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| # React Native support | ||
|
|
||
| ## What ships today | ||
|
|
||
| The `reactNative` preset (`eslint/react-native.js`, exported as | ||
| `@zoldytech/javascript/eslint/react-native`) is the `react` preset's foundation | ||
| applied to React Native: | ||
|
|
||
| - **antfu `react: true`** — `@eslint-react` + `react-refresh` + `react-hooks` | ||
| (`react/exhaustive-deps`, `react/rules-of-hooks`, Fast-Refresh checks). These | ||
| are React-semantic, not DOM-specific, so they apply to RN unchanged. | ||
| - **The full SonarQube layer** (`sonarLayer` + `sonarReactRules` + `sonarTestOff`). | ||
| React Native is just React + TS/JS; SonarQube itself ships **no** RN-specific | ||
| rules, so this is full SonarQube parity for RN code. | ||
| - **RN runtime globals** (`__DEV__`, `HermesInternal`) so they don't trip | ||
| `no-undef`. antfu already provides the browser + node globals RN relies on | ||
| (`fetch`, `navigator`, `process`, `require`, timers, …). | ||
| - **`tsconfig/react-native.json`** — extends base, `jsx: react-jsx`, includes the | ||
| DOM lib (see [react-native-web](#react-native-web) below). | ||
|
|
||
| Same option shape as every other preset (`typeChecked`, `tsconfigPath`, `tsdoc`, | ||
| `ignores`, `testGlobs`, `overrides`, `antfuOptions`). | ||
|
|
||
| ## react-native-web | ||
|
|
||
| Universal codebases that also target the web via | ||
| [`react-native-web`](https://necolas.github.io/react-native-web/) are supported | ||
| with no extra ESLint config: | ||
|
|
||
| - **Linting is identical.** RNW runs in the browser, so antfu's `browser` globals | ||
| already cover the web APIs, `__DEV__` is declared by the preset, and web-only | ||
| files (`Foo.web.tsx`, `Foo.web.ts`) already match the preset's globs. The same | ||
| React rules apply on both platforms. Nothing platform-specific to wire in. | ||
| - **TypeScript.** `tsconfig/react-native.json` deliberately keeps the **DOM lib** | ||
| (`["ES2022", "DOM", "DOM.Iterable"]`) so web-targeted and web-only code | ||
| type-checks — TypeScript can't vary `lib` per file, so the web target sets the | ||
| floor. RNW ships augmented type definitions; install `@types/react-native-web` | ||
| and, if you want the web-augmented surface, add | ||
| `"types": ["react-native-web"]` in your project `tsconfig.json`. | ||
|
|
||
| ```json | ||
| { | ||
| "extends": "@zoldytech/javascript/tsconfig/react-native.json", | ||
| "compilerOptions": { "types": ["react-native-web"] }, | ||
| "include": ["src"] | ||
| } | ||
| ``` | ||
|
|
||
| **Native-only** projects (no web target) can drop the DOM lib to catch | ||
| accidental DOM usage on native, by overriding `lib` in their own tsconfig: | ||
| `"compilerOptions": { "lib": ["ES2022"] }`. | ||
| - The bundler alias (`react-native$` → `react-native-web`) is a build-tool concern | ||
| (webpack/Metro/Vite), out of scope for this package. | ||
|
|
||
| ## Deferred: RN-specific style rules (blocked on ESLint 10) | ||
|
|
||
| The RN-idiom lint rules — `no-inline-styles`, `no-color-literals`, | ||
| `no-unused-styles`, `split-platform-components`, `no-single-element-style-arrays`, | ||
| `no-raw-text`, `sort-styles` — live only in | ||
| [`eslint-plugin-react-native`](https://github.com/intellicode/eslint-plugin-react-native). | ||
| They are **not** wired in yet because that plugin is incompatible with ESLint 10, | ||
| which this package requires (peer `eslint >=10.4`). | ||
|
|
||
| **Verified against ESLint 10.7 (2026-07):** | ||
|
|
||
| | Rule | Status on ESLint 10 | | ||
| | --- | --- | | ||
| | `no-unused-styles`, `no-inline-styles`, `no-color-literals`, `sort-styles` | **crash** — call `context.getSourceCode()` (removed in ESLint 10) | | ||
| | `split-platform-components` | **crash** — calls `context.getFilename()` (removed in ESLint 10) | | ||
| | `no-raw-text`, `no-single-element-style-arrays` | load but are the least useful rules | | ||
|
|
||
| `eslint-plugin-react-native@5.0.0` is the latest release and declares peer | ||
| `eslint ^3 … ^9`. `@react-native/eslint-plugin` ships no style rules | ||
| (`platform-colors`, `no-deep-imports` only). `@react-native/eslint-config` peers | ||
| `eslint ^8 || ^9` and pulls in `eslint-plugin-react`, which this package | ||
| deliberately avoids (see `eslint/next.js` header). | ||
|
|
||
| Registering the raw rule objects ourselves does **not** work around this: the | ||
| rule bodies themselves call the removed APIs. | ||
|
|
||
| ## Plan for adding the style rules | ||
|
|
||
| When `eslint-plugin-react-native` publishes an ESLint-10-compatible release (drops | ||
| `context.getSourceCode()`/`getFilename()`, widens its `eslint` peer range): | ||
|
|
||
| 1. Add it as a dependency (`npm i -D eslint-plugin-react-native`) and confirm | ||
| `npm install` resolves cleanly against `eslint >=10.4` (no `--legacy-peer-deps`). | ||
| 2. In `eslint/react-native.js`, extend `reactNativeGlobals()` into a full | ||
| `reactNativeLayer()` block that also registers the plugin and its rules | ||
| (mirror `nextCoreWebVitals()` in `eslint/next.js`): | ||
|
|
||
| ```js | ||
| import reactNativePlugin from 'eslint-plugin-react-native'; | ||
|
|
||
| // Replace reactNativeGlobals() with a full layer block that also registers | ||
| // the plugin (mirror nextCoreWebVitals()): | ||
| const reactNativeLayer = { | ||
| name: 'zoldytech/react-native', | ||
| files: ['**/*.{js,jsx,ts,tsx}'], | ||
| plugins: { 'react-native': reactNativePlugin }, | ||
| // the plugin exposes the RN env globals directly: | ||
| languageOptions: { | ||
| globals: { ...reactNativePlugin.environments['react-native'].globals }, | ||
| }, | ||
| rules: { | ||
| 'react-native/no-unused-styles': 'error', | ||
| 'react-native/no-single-element-style-arrays': 'error', | ||
| 'react-native/split-platform-components': 'error', | ||
| 'react-native/no-inline-styles': 'error', | ||
| 'react-native/no-color-literals': 'error', | ||
| 'react-native/no-raw-text': 'error', // noisiest — relax via `overrides` if needed | ||
| }, | ||
| }; | ||
| ``` | ||
|
|
||
| (Omit `sort-styles` — pure ordering, Prettier's domain.) | ||
| 3. Add a `dirty.tsx` case to `test/fixtures/react-native/` that trips a couple of | ||
| the style rules and extend the `presetSuite('react-native', …)` expected-rules | ||
| list in `test/presets.test.js` accordingly. | ||
| 4. Verify with `npm test` and a manual lint of a snippet with an inline style, a | ||
| color literal, and an unused `StyleSheet` entry. | ||
|
|
||
| Track upstream: <https://github.com/intellicode/eslint-plugin-react-native/issues> | ||
| (ESLint 10 / flat-config support). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // react-native — house style for React Native apps. Same foundation as the | ||
| // `react` preset (antfu's react support = @eslint-react + react-refresh, which | ||
| // already enables exhaustive-deps / rules-of-hooks / Fast-Refresh checks) plus | ||
| // the SonarQube-compatibility layer. React Native ships no SonarQube-specific | ||
| // rules — it is just React + TS/JS — so the `react` guardrails apply verbatim; | ||
| // this preset adds the RN runtime globals (`__DEV__`, Hermes) so they don't trip | ||
| // `no-undef`, and a matching `tsconfig/react-native.json` (no DOM lib). | ||
| // | ||
| // NOTE: the RN-specific style rules (no-inline-styles, no-color-literals, | ||
| // no-unused-styles, split-platform-components, …) are intentionally NOT wired in | ||
| // yet: their only source, eslint-plugin-react-native, crashes on ESLint 10 (it | ||
| // calls the removed context.getSourceCode/getFilename APIs). They will be added | ||
| // once that plugin supports ESLint 10 — see docs/react-native.md. | ||
|
|
||
| import antfu from '@antfu/eslint-config'; | ||
| import prettier from 'eslint-config-prettier/flat'; | ||
| import { antfuTypescript, sonarLayer, sonarReactRules, sonarTestOff } from './_shared.js'; | ||
|
|
||
| /** | ||
| * RN runtime globals that antfu's browser+node globals don't already cover. | ||
| * Declaring them stops `no-undef` firing on `__DEV__` etc. in `.js` files (in TS | ||
| * files typescript-eslint disables `no-undef` and the type checker handles it). | ||
| */ | ||
| function reactNativeGlobals() { | ||
| return { | ||
| name: 'zoldytech/react-native-globals', | ||
| languageOptions: { | ||
| globals: { | ||
| __DEV__: 'readonly', | ||
| HermesInternal: 'readonly', | ||
| }, | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * @typedef {object} ReactNativeOptions | ||
| * @property {boolean} [typeChecked=false] Enable type-aware linting (needs `tsconfigPath`). | ||
| * @property {string} [tsconfigPath='tsconfig.json'] tsconfig for type-aware linting. | ||
| * @property {boolean} [tsdoc=false] Enable the `tsdoc/syntax` gate. | ||
| * @property {string[]} [ignores=[]] Project-specific ignore globs. | ||
| * @property {string[]} [testGlobs] Override the tests/config globs for the sonarjs-off block. | ||
| * @property {import('eslint').Linter.Config[]} [overrides=[]] Extra blocks, appended last. | ||
| * @property {Record<string, unknown>} [antfuOptions] Extra options merged into antfu(). | ||
| */ | ||
|
|
||
| /** | ||
| * React Native house-style preset. Returns antfu's FlatConfigComposer (thenable). | ||
| * @param {ReactNativeOptions} [options] | ||
| */ | ||
| export function reactNative(options = {}) { | ||
| const { | ||
| typeChecked = false, | ||
| tsconfigPath, | ||
| tsdoc = false, | ||
| ignores = [], | ||
| testGlobs, | ||
| overrides = [], | ||
| antfuOptions = {}, | ||
| } = options; | ||
|
|
||
| return antfu( | ||
| { | ||
| type: 'app', | ||
| react: true, | ||
| typescript: antfuTypescript({ typeChecked, tsconfigPath }), | ||
| stylistic: false, | ||
| ignores, | ||
| ...antfuOptions, | ||
| }, | ||
| reactNativeGlobals(), | ||
| ...sonarLayer({ tsdoc }), | ||
| sonarReactRules(), | ||
| sonarTestOff(testGlobs), | ||
| prettier, | ||
| ...overrides | ||
| ); | ||
| } | ||
|
|
||
| export default reactNative; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { Text, View } from 'react-native'; | ||
|
|
||
| export function Greeting({ name }: { name: string }) { | ||
| // `__DEV__` is an RN runtime global the preset declares, so it must not trip no-undef. | ||
| const label = __DEV__ ? `[dev] ${name}` : name; | ||
| return ( | ||
| <View> | ||
| <Text>Hello {label}</Text> | ||
| </View> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| // Guards the preset's RN globals block: in a .js file `no-undef` is active, so | ||
| // this errors unless `__DEV__` is declared as a global. (In .tsx, no-undef is | ||
| // off, so clean.tsx can't catch a regression here.) | ||
| export const isDev = __DEV__; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { useEffect, useState } from 'react'; | ||
| import { Text } from 'react-native'; | ||
|
|
||
| export function Counter({ start }: { start: number }) { | ||
| const [n, setN] = useState(0); | ||
| useEffect(() => { | ||
| setN(start); | ||
| }, []); | ||
| console.log(n); | ||
| return <Text>{n}</Text>; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| import { reactNative } from '../../../eslint/react-native.js'; | ||
| export default reactNative(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "$schema": "https://json.schemastore.org/tsconfig", | ||
| "display": "@zoldytech/javascript react-native", | ||
| "extends": "./base.json", | ||
| "compilerOptions": { | ||
| "jsx": "react-jsx", | ||
| "lib": ["ES2022", "DOM", "DOM.Iterable"], | ||
| "noEmit": true, | ||
| "allowImportingTsExtensions": true | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.