@jmlweb/eslint-config-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # @jmlweb/eslint-config-react
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@jmlweb/eslint-config-react)](https://www.npmjs.com/package/@jmlweb/eslint-config-react)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D20.11.0-339933.svg)](https://nodejs.org/)
6
+ [![ESLint](https://img.shields.io/badge/ESLint-9.0%2B-4B32C3.svg)](https://eslint.org/)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0%2B-3178C6.svg)](https://www.typescriptlang.org/)
8
+ [![React](https://img.shields.io/badge/React-18%2B-61DAFB.svg)](https://react.dev/)
9
+
10
+ > ESLint configuration for React libraries with TypeScript. Extends `@jmlweb/eslint-config-base` with React-specific rules, hooks validation, and JSX best practices.
11
+
12
+ ## ✨ Features
13
+
14
+ - 🔒 **Strict Type Checking**: Inherits all strict TypeScript rules from base config
15
+ - ⚛️ **React Best Practices**: Enforces React-specific rules and patterns
16
+ - 🪝 **Hooks Validation**: Validates React Hooks rules and exhaustive dependencies
17
+ - 🎨 **JSX Support**: Optimized for modern JSX transform (React 17+)
18
+ - 📦 **Import Management**: Enforces type-only imports with inline style + automatic sorting
19
+ - 🎯 **Code Quality**: Prevents common React pitfalls and anti-patterns
20
+ - 🎨 **Prettier Integration**: Disables all ESLint rules that conflict with Prettier
21
+ - 🚀 **Flat Config**: Uses ESLint 9+ flat config format (latest stable)
22
+
23
+ ## 📦 Installation
24
+
25
+ ```bash
26
+ npm install --save-dev @jmlweb/eslint-config-react eslint @eslint/js typescript-eslint eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-simple-import-sort @jmlweb/eslint-config-base
27
+ ```
28
+
29
+ ## 🚀 Quick Start
30
+
31
+ Create an `eslint.config.js` file in your project root:
32
+
33
+ ```javascript
34
+ import reactConfig from '@jmlweb/eslint-config-react';
35
+
36
+ export default [
37
+ ...reactConfig,
38
+ // Add your project-specific overrides here
39
+ ];
40
+ ```
41
+
42
+ ## 💡 Examples
43
+
44
+ ### Basic Setup
45
+
46
+ ```javascript
47
+ // eslint.config.js
48
+ import reactConfig from '@jmlweb/eslint-config-react';
49
+
50
+ export default [...reactConfig];
51
+ ```
52
+
53
+ ### With Project-Specific Overrides
54
+
55
+ ```javascript
56
+ // eslint.config.js
57
+ import reactConfig from '@jmlweb/eslint-config-react';
58
+
59
+ export default [
60
+ ...reactConfig,
61
+ {
62
+ files: ['**/*.test.tsx', '**/*.spec.tsx'],
63
+ rules: {
64
+ // Allow any in tests
65
+ '@typescript-eslint/no-explicit-any': 'off',
66
+ // Allow console in tests
67
+ 'no-console': 'off',
68
+ // Relax React rules in tests
69
+ 'react/display-name': 'off',
70
+ },
71
+ },
72
+ {
73
+ ignores: ['dist/', 'build/', 'node_modules/', '*.config.ts'],
74
+ },
75
+ ];
76
+ ```
77
+
78
+ ### With Custom React Settings
79
+
80
+ ```javascript
81
+ // eslint.config.js
82
+ import reactConfig from '@jmlweb/eslint-config-react';
83
+
84
+ export default [
85
+ ...reactConfig,
86
+ {
87
+ files: ['**/*.tsx', '**/*.jsx'],
88
+ settings: {
89
+ react: {
90
+ version: '18.2', // Specify React version explicitly
91
+ },
92
+ },
93
+ },
94
+ ];
95
+ ```
96
+
97
+ ## 📋 Configuration Details
98
+
99
+ ### React Files
100
+
101
+ This configuration applies React-specific rules to:
102
+
103
+ - `**/*.tsx` - TypeScript React files
104
+ - `**/*.jsx` - JavaScript React files
105
+
106
+ ### Key Rules Enforced
107
+
108
+ | Rule | Level | Description |
109
+ | -------------------------------- | ------- | ------------------------------------------ |
110
+ | `react-hooks/rules-of-hooks` | `error` | Enforces Rules of Hooks |
111
+ | `react-hooks/exhaustive-deps` | `warn` | Validates exhaustive dependencies in hooks |
112
+ | `react/jsx-key` | `error` | Prevents missing keys in lists |
113
+ | `react/jsx-no-duplicate-props` | `error` | Prevents duplicate props |
114
+ | `react/jsx-pascal-case` | `error` | Enforces PascalCase for component names |
115
+ | `react/no-array-index-key` | `warn` | Warns against using array index as key |
116
+ | `react/jsx-boolean-value` | `error` | Enforces `{prop}` over `prop={true}` |
117
+ | `react/jsx-curly-brace-presence` | `error` | Prevents unnecessary curly braces |
118
+ | `react/jsx-fragments` | `error` | Enforces shorthand fragment syntax |
119
+ | `react/jsx-sort-props` | `error` | Enforces consistent prop ordering |
120
+
121
+ ### What's Included
122
+
123
+ - ✅ All TypeScript ESLint rules from `@jmlweb/eslint-config-base`
124
+ - ✅ React recommended rules
125
+ - ✅ React JSX runtime rules (for React 17+)
126
+ - ✅ React Hooks rules and exhaustive deps validation
127
+ - ✅ JSX best practices and anti-pattern prevention
128
+ - ✅ Automatic import/export sorting
129
+ - ✅ Prettier conflict resolution
130
+ - ✅ React version auto-detection
131
+
132
+ ## 🔄 Import Sorting
133
+
134
+ The configuration automatically sorts imports and enforces type-only imports:
135
+
136
+ **Before:**
137
+
138
+ ```typescript
139
+ import { Component } from './component';
140
+ import React, { useState } from 'react';
141
+ import type { User } from './types';
142
+ import fs from 'fs';
143
+ ```
144
+
145
+ **After auto-fix:**
146
+
147
+ ```typescript
148
+ import fs from 'fs';
149
+ import React, { useState } from 'react';
150
+ import type { User } from './types';
151
+ import { Component } from './component';
152
+ ```
153
+
154
+ Fix import order automatically:
155
+
156
+ ```bash
157
+ npx eslint --fix .
158
+ ```
159
+
160
+ ## 🎯 When to Use
161
+
162
+ Use this configuration when you want:
163
+
164
+ - ✅ React library development with TypeScript
165
+ - ✅ Maximum type safety with React
166
+ - ✅ Strict code quality standards for React code
167
+ - ✅ Consistent React patterns across the team
168
+ - ✅ Prevention of common React pitfalls
169
+ - ✅ Best practices enforcement for hooks and JSX
170
+
171
+ **For non-React TypeScript projects**, use [`@jmlweb/eslint-config-base`](../eslint-config-base) instead.
172
+
173
+ **For JavaScript-only React projects**, you can extend `@jmlweb/eslint-config-base-js` and add React plugins manually.
174
+
175
+ ## 🔧 Extending the Configuration
176
+
177
+ You can extend or override the configuration for your specific needs:
178
+
179
+ ```javascript
180
+ import reactConfig from '@jmlweb/eslint-config-react';
181
+
182
+ export default [
183
+ ...reactConfig,
184
+ {
185
+ files: ['**/*.test.tsx', '**/*.spec.tsx'],
186
+ rules: {
187
+ // Test-specific rules
188
+ '@typescript-eslint/no-explicit-any': 'off',
189
+ 'react/display-name': 'off',
190
+ },
191
+ },
192
+ {
193
+ ignores: ['dist/', 'build/', 'node_modules/'],
194
+ },
195
+ ];
196
+ ```
197
+
198
+ ## 📝 Usage with Scripts
199
+
200
+ Add linting scripts to your `package.json`:
201
+
202
+ ```json
203
+ {
204
+ "scripts": {
205
+ "lint": "eslint .",
206
+ "lint:fix": "eslint . --fix"
207
+ }
208
+ }
209
+ ```
210
+
211
+ ## 📋 Requirements
212
+
213
+ - **Node.js** >= 20.11.0 (required for `import.meta.dirname` in config files)
214
+ - **ESLint** >= 9.0.0 (flat config format)
215
+ - **TypeScript** project with `tsconfig.json`
216
+ - **React** >= 17.0.0 (for JSX runtime support)
217
+ - **TypeScript project service** enabled (automatic with this config)
218
+
219
+ ## 📦 Peer Dependencies
220
+
221
+ This package requires the following peer dependencies:
222
+
223
+ - `eslint` (^9.0.0)
224
+ - `@eslint/js` (^9.0.0)
225
+ - `typescript-eslint` (^8.0.0)
226
+ - `eslint-config-prettier` (^9.1.0)
227
+ - `eslint-plugin-react` (^7.37.0)
228
+ - `eslint-plugin-react-hooks` (^5.0.0)
229
+ - `eslint-plugin-simple-import-sort` (^12.0.0)
230
+ - `@jmlweb/eslint-config-base` (^1.0.0)
231
+
232
+ ## 🔗 Related Packages
233
+
234
+ - [`@jmlweb/eslint-config-base`](../eslint-config-base) - Base TypeScript ESLint config (extended by this package)
235
+ - [`@jmlweb/tsconfig-react`](../tsconfig-react) - TypeScript configuration for React libraries
236
+ - [`@jmlweb/prettier-config-base`](../prettier-config-base) - Prettier config for consistent formatting
237
+
238
+ ## 📄 License
239
+
240
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ default: () => index_default
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_eslint_config_base = __toESM(require("@jmlweb/eslint-config-base"), 1);
37
+ var import_eslint_config_prettier = __toESM(require("eslint-config-prettier"), 1);
38
+ var import_eslint_plugin_react = __toESM(require("eslint-plugin-react"), 1);
39
+ var import_eslint_plugin_react_hooks = __toESM(require("eslint-plugin-react-hooks"), 1);
40
+ var import_eslint_plugin_simple_import_sort = __toESM(require("eslint-plugin-simple-import-sort"), 1);
41
+ var config = [
42
+ ...import_eslint_config_base.default,
43
+ // React recommended config
44
+ import_eslint_plugin_react.default.configs.flat.recommended,
45
+ // React JSX runtime config (for React 17+)
46
+ import_eslint_plugin_react.default.configs.flat["jsx-runtime"],
47
+ // React Hooks recommended config
48
+ import_eslint_plugin_react_hooks.default.configs.recommended,
49
+ {
50
+ files: ["**/*.tsx", "**/*.jsx"],
51
+ plugins: {
52
+ react: import_eslint_plugin_react.default,
53
+ "react-hooks": import_eslint_plugin_react_hooks.default,
54
+ "simple-import-sort": import_eslint_plugin_simple_import_sort.default
55
+ },
56
+ languageOptions: {
57
+ parserOptions: {
58
+ ecmaFeatures: {
59
+ jsx: true
60
+ }
61
+ },
62
+ globals: {
63
+ JSX: "readonly"
64
+ }
65
+ },
66
+ settings: {
67
+ react: {
68
+ version: "detect"
69
+ }
70
+ },
71
+ rules: {
72
+ ...import_eslint_config_prettier.default.rules,
73
+ "simple-import-sort/imports": "error",
74
+ "simple-import-sort/exports": "error",
75
+ // React plugin overrides
76
+ "react/react-in-jsx-scope": "off",
77
+ // Not needed with new JSX transform
78
+ "react/prop-types": "off",
79
+ // TypeScript handles prop validation
80
+ "react/display-name": "off",
81
+ // Not needed for libraries
82
+ "react/jsx-uses-react": "off",
83
+ // Not needed with new JSX transform
84
+ "react/jsx-uses-vars": "error",
85
+ // React Hooks rules
86
+ "react-hooks/rules-of-hooks": "error",
87
+ "react-hooks/exhaustive-deps": "warn",
88
+ // React best practices
89
+ "react/jsx-key": "error",
90
+ "react/jsx-no-duplicate-props": "error",
91
+ "react/jsx-no-undef": "error",
92
+ "react/jsx-pascal-case": "error",
93
+ "react/no-array-index-key": "warn",
94
+ "react/no-children-prop": "error",
95
+ "react/no-danger-with-children": "error",
96
+ "react/no-deprecated": "warn",
97
+ "react/no-direct-mutation-state": "error",
98
+ "react/no-find-dom-node": "error",
99
+ "react/no-is-mounted": "error",
100
+ "react/no-render-return-value": "error",
101
+ "react/no-string-refs": "error",
102
+ "react/no-unescaped-entities": "error",
103
+ "react/no-unknown-property": "error",
104
+ "react/require-render-return": "error",
105
+ "react/self-closing-comp": "error",
106
+ "react/jsx-boolean-value": ["error", "never"],
107
+ "react/jsx-curly-brace-presence": [
108
+ "error",
109
+ { props: "never", children: "never" }
110
+ ],
111
+ "react/jsx-fragments": ["error", "syntax"],
112
+ "react/jsx-no-useless-fragment": "error",
113
+ "react/jsx-sort-props": [
114
+ "error",
115
+ {
116
+ callbacksLast: true,
117
+ shorthandFirst: true,
118
+ ignoreCase: true,
119
+ reservedFirst: true
120
+ }
121
+ ]
122
+ }
123
+ }
124
+ ];
125
+ var index_default = config;
@@ -0,0 +1,10 @@
1
+ import { Linter } from 'eslint';
2
+
3
+ /**
4
+ * React ESLint configuration that extends the base TypeScript config.
5
+ * Includes React-specific rules, hooks rules, and JSX best practices.
6
+ * For React library development with TypeScript.
7
+ */
8
+ declare const config: Linter.Config[];
9
+
10
+ export { config as default };
@@ -0,0 +1,10 @@
1
+ import { Linter } from 'eslint';
2
+
3
+ /**
4
+ * React ESLint configuration that extends the base TypeScript config.
5
+ * Includes React-specific rules, hooks rules, and JSX best practices.
6
+ * For React library development with TypeScript.
7
+ */
8
+ declare const config: Linter.Config[];
9
+
10
+ export { config as default };
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ // src/index.ts
2
+ import baseConfig from "@jmlweb/eslint-config-base";
3
+ import prettierConfig from "eslint-config-prettier";
4
+ import react from "eslint-plugin-react";
5
+ import reactHooks from "eslint-plugin-react-hooks";
6
+ import simpleImportSort from "eslint-plugin-simple-import-sort";
7
+ var config = [
8
+ ...baseConfig,
9
+ // React recommended config
10
+ react.configs.flat.recommended,
11
+ // React JSX runtime config (for React 17+)
12
+ react.configs.flat["jsx-runtime"],
13
+ // React Hooks recommended config
14
+ reactHooks.configs.recommended,
15
+ {
16
+ files: ["**/*.tsx", "**/*.jsx"],
17
+ plugins: {
18
+ react,
19
+ "react-hooks": reactHooks,
20
+ "simple-import-sort": simpleImportSort
21
+ },
22
+ languageOptions: {
23
+ parserOptions: {
24
+ ecmaFeatures: {
25
+ jsx: true
26
+ }
27
+ },
28
+ globals: {
29
+ JSX: "readonly"
30
+ }
31
+ },
32
+ settings: {
33
+ react: {
34
+ version: "detect"
35
+ }
36
+ },
37
+ rules: {
38
+ ...prettierConfig.rules,
39
+ "simple-import-sort/imports": "error",
40
+ "simple-import-sort/exports": "error",
41
+ // React plugin overrides
42
+ "react/react-in-jsx-scope": "off",
43
+ // Not needed with new JSX transform
44
+ "react/prop-types": "off",
45
+ // TypeScript handles prop validation
46
+ "react/display-name": "off",
47
+ // Not needed for libraries
48
+ "react/jsx-uses-react": "off",
49
+ // Not needed with new JSX transform
50
+ "react/jsx-uses-vars": "error",
51
+ // React Hooks rules
52
+ "react-hooks/rules-of-hooks": "error",
53
+ "react-hooks/exhaustive-deps": "warn",
54
+ // React best practices
55
+ "react/jsx-key": "error",
56
+ "react/jsx-no-duplicate-props": "error",
57
+ "react/jsx-no-undef": "error",
58
+ "react/jsx-pascal-case": "error",
59
+ "react/no-array-index-key": "warn",
60
+ "react/no-children-prop": "error",
61
+ "react/no-danger-with-children": "error",
62
+ "react/no-deprecated": "warn",
63
+ "react/no-direct-mutation-state": "error",
64
+ "react/no-find-dom-node": "error",
65
+ "react/no-is-mounted": "error",
66
+ "react/no-render-return-value": "error",
67
+ "react/no-string-refs": "error",
68
+ "react/no-unescaped-entities": "error",
69
+ "react/no-unknown-property": "error",
70
+ "react/require-render-return": "error",
71
+ "react/self-closing-comp": "error",
72
+ "react/jsx-boolean-value": ["error", "never"],
73
+ "react/jsx-curly-brace-presence": [
74
+ "error",
75
+ { props: "never", children: "never" }
76
+ ],
77
+ "react/jsx-fragments": ["error", "syntax"],
78
+ "react/jsx-no-useless-fragment": "error",
79
+ "react/jsx-sort-props": [
80
+ "error",
81
+ {
82
+ callbacksLast: true,
83
+ shorthandFirst: true,
84
+ ignoreCase: true,
85
+ reservedFirst: true
86
+ }
87
+ ]
88
+ }
89
+ }
90
+ ];
91
+ var index_default = config;
92
+ export {
93
+ index_default as default
94
+ };
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@jmlweb/eslint-config-react",
3
+ "version": "1.0.0",
4
+ "description": "ESLint configuration for React libraries with TypeScript, extending base config with React-specific rules",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "require": {
12
+ "types": "./dist/index.d.cts",
13
+ "default": "./dist/index.cjs"
14
+ },
15
+ "import": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "clean": "rm -rf dist",
28
+ "prepublishOnly": "pnpm build"
29
+ },
30
+ "keywords": [
31
+ "eslint",
32
+ "eslint-config",
33
+ "react",
34
+ "typescript",
35
+ "linting",
36
+ "code-quality",
37
+ "react-hooks"
38
+ ],
39
+ "author": "jmlweb",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/jmlweb/tooling.git"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/jmlweb/tooling/issues"
47
+ },
48
+ "homepage": "https://github.com/jmlweb/tooling/tree/main/packages/eslint-config-react#readme",
49
+ "engines": {
50
+ "node": ">=20.11.0"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "peerDependencies": {
56
+ "@eslint/js": "^9.0.0",
57
+ "@jmlweb/eslint-config-base": "^1.0.0",
58
+ "eslint": "^9.0.0",
59
+ "eslint-config-prettier": "^9.1.0",
60
+ "eslint-plugin-react": "^7.37.0",
61
+ "eslint-plugin-react-hooks": "^5.0.0",
62
+ "eslint-plugin-simple-import-sort": "^12.0.0",
63
+ "typescript-eslint": "^8.0.0"
64
+ },
65
+ "dependencies": {
66
+ "@jmlweb/eslint-config-base": "workspace:*"
67
+ },
68
+ "devDependencies": {
69
+ "@eslint/js": "^9.39.2",
70
+ "@jmlweb/eslint-config-base": "workspace:*",
71
+ "@jmlweb/tsconfig-internal": "workspace:*",
72
+ "@types/eslint": "^9.6.1",
73
+ "eslint": "^9.39.2",
74
+ "eslint-config-prettier": "^10.1.8",
75
+ "eslint-plugin-react": "^7.37.5",
76
+ "eslint-plugin-react-hooks": "^5.1.0",
77
+ "eslint-plugin-simple-import-sort": "^12.1.1",
78
+ "tsup": "^8.5.1",
79
+ "typescript": "^5.9.3",
80
+ "typescript-eslint": "^8.34.1"
81
+ }
82
+ }