@lumelabs/eslint-config 0.1.0 → 0.2.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 CHANGED
@@ -1,34 +1,95 @@
1
- # @lumelabs/eslint-config-react
2
-
3
- > ⚠️ **Experimental / Internal Use Only**
4
- >
5
- > This configuration is primarily intended for personal and internal use.
6
- > It may change, break, or be restructured at any time without notice.
7
- >
8
- > **Do not rely on this package for production or public projects unless you are prepared to maintain your own fork.**
9
- >
10
- > **Issues and feature requests may be closed without response.**
11
-
12
- Opinionated ESLint configuration for **TypeScript + React** projects.
13
-
14
- This preset is designed for modern setups using TypeScript, React, Vite/Webpack, and Prettier.
15
- It prioritizes practical correctness over strict ideology and avoids rules that tend to create noise
16
- or force unnatural code patterns.
17
-
18
- ---
19
-
20
- ## Features
21
-
22
- - ESLint **8.x** only (fully bundled, no peer dependency juggling)
23
- - TypeScript-first (no JS-only compromises)
24
- - React + React Hooks + JSX a11y included
25
- - Prettier-compatible
26
- - Sensible defaults based on real-world usage, not theory
27
- - No legacy or deprecated ESLint patterns
28
-
29
- ---
30
-
31
- ## Installation
32
-
33
- ```bash
34
- npm install --save-dev @lumelabs/eslint-config-react
1
+ # @lumelabs/eslint-config
2
+
3
+ > ⚠️ **Experimental / Internal Use**
4
+ >
5
+ > Primarily intended for personal and internal use. It may change, break, or be
6
+ > restructured at any time. Don't rely on it for public projects unless you're
7
+ > prepared to maintain your own fork. Issues/feature requests may be closed without response.
8
+
9
+ Opinionated, **batteries-included ESLint 9 (flat config)** for **TypeScript** projects —
10
+ both **frontend (React)** and **backend**. Designed for modern setups using TypeScript,
11
+ React, Vite/Webpack, and Prettier. It prioritizes practical correctness over ideology and
12
+ avoids rules that create noise or force unnatural code.
13
+
14
+ ---
15
+
16
+ ## Features
17
+
18
+ - **ESLint 9 flat config** (`eslint.config.js`)
19
+ - **Batteries included** — `eslint` itself and every plugin are bundled as dependencies.
20
+ Install one package; you don't add `eslint` or any plugin yourself.
21
+ - **TypeScript-first** (`typescript-eslint`), works for frontend and backend
22
+ - React + React Hooks (incl. the React Compiler rules, tuned to warn) + JSX a11y
23
+ - `eslint-plugin-import` rules (named-exports enforced; default exports disallowed)
24
+ - **Prettier-compatible** all formatting rules are turned off; Prettier owns formatting
25
+ - Jest globals auto-enabled for test files only
26
+
27
+ ---
28
+
29
+ ## Requirements
30
+
31
+ - **ESLint 9** — bundled with this package; do **not** install `eslint` separately in your project.
32
+ - **TypeScript** — provided by your project (declared as an optional peer dependency).
33
+ - Node.js 18.18+ / 20+ (per ESLint 9).
34
+
35
+ ---
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ npm install --save-dev @lumelabs/eslint-config
41
+ ```
42
+
43
+ That's it — no other ESLint packages to install.
44
+
45
+ ---
46
+
47
+ ## Usage
48
+
49
+ Create `eslint.config.js` (ESM) in your project root:
50
+
51
+ ```js
52
+ import lumelabs from "@lumelabs/eslint-config"
53
+
54
+ export default lumelabs
55
+ ```
56
+
57
+ Or `eslint.config.cjs` (CommonJS):
58
+
59
+ ```js
60
+ const lumelabs = require("@lumelabs/eslint-config")
61
+
62
+ module.exports = lumelabs
63
+ ```
64
+
65
+ ### Overriding rules per project
66
+
67
+ The export is a flat-config array — spread it and append your own config objects:
68
+
69
+ ```js
70
+ import lumelabs from "@lumelabs/eslint-config"
71
+
72
+ export default [
73
+ ...lumelabs,
74
+ {
75
+ rules: {
76
+ // e.g. relax the named-exports rule for a framework that needs default exports
77
+ "import/no-default-export": "off",
78
+ },
79
+ },
80
+ ]
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Notes
86
+
87
+ - **Named exports are enforced** (`import/no-default-export`). `*.cjs` files are ignored, so
88
+ CommonJS config files are unaffected. For frameworks that require default exports
89
+ (e.g. Next.js pages), override the rule for the relevant paths as shown above.
90
+ - **`*.stories.*` and `*.cjs` are not linted** by default.
91
+ - **React Compiler rules** (`react-hooks/*`) are enabled as **warnings** (not errors), so they
92
+ nudge rather than block. The compiler-infrastructure rules are off until you adopt the
93
+ React Compiler — flip them on per-project when you do.
94
+ - Bumping ESLint (e.g. to v10) is centralized here: update this package and republish, and
95
+ every project follows on its next install.
package/index.cjs CHANGED
@@ -1,59 +1,101 @@
1
- module.exports = {
2
- extends: [
3
- "eslint:recommended",
4
-
5
- // TypeScript rules
6
- require.resolve("./rules/typescript-eslint"),
7
-
8
- // Base JS rules
9
- require.resolve("./rules/best-practices"),
10
- require.resolve("./rules/errors"),
11
- require.resolve("./rules/node"),
12
- require.resolve("./rules/style"),
13
- require.resolve("./rules/variables"),
14
- require.resolve("./rules/es6"),
15
- require.resolve("./rules/imports"),
16
- require.resolve("./rules/strict"),
17
-
18
- // React rules
19
- require.resolve("./rules/react"),
20
- require.resolve("./rules/react-hooks"),
21
- require.resolve("./rules/react-a11y"),
22
-
23
- "prettier",
24
-
25
- // Override. Use only if rules are not applied within "rules/*"
26
- require.resolve("./override"),
27
- ],
28
-
29
- ignorePatterns: ["**/node_modules/**", "*.stories.*", "**/*.cjs"],
30
- env: {
31
- browser: true,
32
- es2021: true,
33
- node: true,
34
- jest: true,
1
+ "use strict"
2
+
3
+ // Batteries-included ESLint 9 (flat config) for TypeScript — frontend and backend.
4
+ // Everything (eslint itself + every plugin) is bundled as a dependency of this
5
+ // package, so consuming projects only ever do:
6
+ //
7
+ // // eslint.config.js
8
+ // import lumelabs from "@lumelabs/eslint-config"
9
+ // export default lumelabs
10
+ //
11
+ // Authored as CommonJS so it can be require()'d or import()'ed from either a
12
+ // CJS or ESM eslint.config file.
13
+
14
+ const js = require("@eslint/js")
15
+ const tseslint = require("typescript-eslint")
16
+ const react = require("eslint-plugin-react")
17
+ const reactHooks = require("eslint-plugin-react-hooks")
18
+ const jsxA11y = require("eslint-plugin-jsx-a11y")
19
+ const importPlugin = require("eslint-plugin-import")
20
+ const prettier = require("eslint-config-prettier")
21
+ const globals = require("globals")
22
+
23
+ // Each rule module exports a plain { rules, settings? } object. Plugin wiring and
24
+ // shared presets are applied below; these modules only carry our own overrides.
25
+ const bestPractices = require("./rules/best-practices")
26
+ const errors = require("./rules/errors")
27
+ const es6 = require("./rules/es6")
28
+ const variables = require("./rules/variables")
29
+ const style = require("./rules/style")
30
+ const strict = require("./rules/strict")
31
+ const typescriptRules = require("./rules/typescript-eslint")
32
+ const imports = require("./rules/imports")
33
+ const reactRules = require("./rules/react")
34
+ const reactHooksRules = require("./rules/react-hooks")
35
+ const reactA11yRules = require("./rules/react-a11y")
36
+
37
+ const customRules = {
38
+ ...bestPractices.rules,
39
+ ...errors.rules,
40
+ ...es6.rules,
41
+ ...variables.rules,
42
+ ...style.rules,
43
+ ...strict.rules,
44
+ ...typescriptRules.rules,
45
+ ...imports.rules,
46
+ ...reactRules.rules,
47
+ ...reactHooksRules.rules,
48
+ ...reactA11yRules.rules,
49
+ }
50
+
51
+ module.exports = [
52
+ // Never linted
53
+ {
54
+ ignores: ["**/*.stories.*", "**/*.cjs"],
35
55
  },
36
- parser: "@typescript-eslint/parser",
37
- parserOptions: {
38
- ecmaFeatures: {
39
- jsx: true,
56
+
57
+ // Base presets
58
+ js.configs.recommended,
59
+ ...tseslint.configs.recommended,
60
+ react.configs.flat.recommended,
61
+ jsxA11y.flatConfigs.recommended,
62
+ importPlugin.flatConfigs.recommended,
63
+
64
+ // Language setup, react-hooks plugin, shared settings and all our overrides
65
+ {
66
+ plugins: {
67
+ "react-hooks": reactHooks,
40
68
  },
41
- ecmaVersion: "latest",
42
- sourceType: "module",
43
- },
44
- rules: {
45
- // Import
46
- // "import-helpers/order-imports": "off",
47
- // "import/no-cycle": "warn",
48
- },
49
- overrides: [
50
- // Test files
51
- {
52
- files: ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/tests/**"],
53
- rules: {
54
- "import/no-extraneous-dependencies": "off",
55
- "@typescript-eslint/no-explicit-any": "off",
69
+ languageOptions: {
70
+ ecmaVersion: "latest",
71
+ sourceType: "module",
72
+ globals: {
73
+ ...globals.browser,
74
+ ...globals.node,
75
+ },
76
+ parserOptions: {
77
+ ecmaFeatures: { jsx: true },
56
78
  },
57
79
  },
58
- ],
59
- }
80
+ settings: {
81
+ ...imports.settings,
82
+ ...reactRules.settings,
83
+ },
84
+ rules: customRules,
85
+ },
86
+
87
+ // Test files: expose Jest globals and relax a couple of rules
88
+ {
89
+ files: ["**/*.{test,spec}.{js,jsx,ts,tsx}", "**/tests/**"],
90
+ languageOptions: {
91
+ globals: { ...globals.jest },
92
+ },
93
+ rules: {
94
+ "import/no-extraneous-dependencies": "off",
95
+ "@typescript-eslint/no-explicit-any": "off",
96
+ },
97
+ },
98
+
99
+ // MUST be last — turns off every rule that would fight Prettier
100
+ prettier,
101
+ ]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lumelabs/eslint-config",
3
- "version": "0.1.0",
4
- "description": "Opinionated ESLint configuration for TypeScript projects (both frontend and backend), based on ESLint 8.",
3
+ "version": "0.2.0",
4
+ "description": "Opinionated, batteries-included ESLint 9 (flat config) for TypeScript projects frontend and backend.",
5
5
  "license": "MIT",
6
6
  "author": "Egor Arkhipov (Lumelabs)",
7
7
  "repository": {
@@ -10,7 +10,6 @@
10
10
  },
11
11
  "files": [
12
12
  "index.cjs",
13
- "override.js",
14
13
  "rules",
15
14
  "README.md",
16
15
  "LICENSE.md"
@@ -21,8 +20,11 @@
21
20
  "keywords": [
22
21
  "eslint",
23
22
  "eslint-config",
23
+ "eslintconfig",
24
+ "flat-config",
25
+ "eslint9",
24
26
  "typescript",
25
- "eslint8",
27
+ "react",
26
28
  "lint",
27
29
  "code-style"
28
30
  ],
@@ -31,16 +33,25 @@
31
33
  ".": "./index.cjs"
32
34
  },
33
35
  "dependencies": {
34
- "@typescript-eslint/eslint-plugin": "^8.53.1",
35
- "@typescript-eslint/parser": "^8.53.1",
36
+ "@eslint/js": "^9.39.0",
36
37
  "confusing-browser-globals": "^1.0.11",
37
- "eslint": "^8.57.1",
38
+ "eslint": "^9.39.0",
38
39
  "eslint-config-prettier": "^10.1.8",
39
40
  "eslint-import-resolver-typescript": "^4.4.4",
40
41
  "eslint-plugin-import": "^2.32.0",
41
42
  "eslint-plugin-jsx-a11y": "^6.10.2",
42
43
  "eslint-plugin-react": "^7.37.5",
43
- "eslint-plugin-react-hooks": "^7.0.1"
44
+ "eslint-plugin-react-hooks": "^7.1.0",
45
+ "globals": "^17.0.0",
46
+ "typescript-eslint": "^8.61.0"
47
+ },
48
+ "peerDependencies": {
49
+ "typescript": ">=5.0.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "typescript": {
53
+ "optional": true
54
+ }
44
55
  },
45
56
  "devDependencies": {
46
57
  "prettier": "^3.8.1"
package/rules/errors.js CHANGED
@@ -14,7 +14,7 @@ module.exports = {
14
14
 
15
15
  // Disallow await inside of loops
16
16
  // https://eslint.org/docs/rules/no-await-in-loop
17
- "no-await-in-loop": "error",
17
+ "no-await-in-loop": "warn",
18
18
 
19
19
  // Disallow comparisons to negative zero
20
20
  // https://eslint.org/docs/rules/no-compare-neg-zero
@@ -185,10 +185,6 @@ module.exports = {
185
185
  // disallow comparisons with the value NaN
186
186
  "use-isnan": "error",
187
187
 
188
- // ensure JSDoc comments are valid
189
- // https://eslint.org/docs/rules/valid-jsdoc
190
- "valid-jsdoc": "off",
191
-
192
188
  // ensure that the results of typeof are compared against a valid string
193
189
  // https://eslint.org/docs/rules/valid-typeof
194
190
  "valid-typeof": ["error", { requireStringLiterals: true }],
package/rules/imports.js CHANGED
@@ -22,11 +22,14 @@ module.exports = {
22
22
 
23
23
  // ensure imports point to files/modules that can be resolved
24
24
  // https://github.com/import-js/eslint-plugin-import/blob/master/docs/rules/no-unresolved.md
25
- "import/no-unresolved": ["error", { commonjs: true, caseSensitive: true }],
25
+ // Off: TypeScript already resolves and type-checks every import path, so this is
26
+ // redundant here and notably slow on TS projects.
27
+ "import/no-unresolved": "off",
26
28
 
27
29
  // ensure named imports coupled with named exports
28
30
  // https://github.com/import-js/eslint-plugin-import/blob/master/docs/rules/named.md#when-not-to-use-it
29
- "import/named": "error",
31
+ // Off: TypeScript already verifies named imports against their exports.
32
+ "import/named": "off",
30
33
 
31
34
  // ensure default import coupled with default export
32
35
  // https://github.com/import-js/eslint-plugin-import/blob/master/docs/rules/default.md#when-not-to-use-it
@@ -225,11 +228,11 @@ module.exports = {
225
228
  // https://github.com/import-js/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/group-exports.md
226
229
  "import/group-exports": "off",
227
230
 
228
- // forbid default exports. this is a terrible rule, do not use it.
231
+ // Forbid default exports.
229
232
  // https://github.com/import-js/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/no-default-export.md
230
233
  "import/no-default-export": "error",
231
234
 
232
- // Prohibit named exports. this is a terrible rule, do not use it.
235
+ // Allows named exports.
233
236
  // https://github.com/import-js/eslint-plugin-import/blob/1ec80fa35fa1819e2d35a70e68fb6a149fb57c5e/docs/rules/no-named-export.md
234
237
  "import/no-named-export": "off",
235
238
 
@@ -239,7 +242,9 @@ module.exports = {
239
242
 
240
243
  // Forbid cyclical dependencies between modules
241
244
  // https://github.com/import-js/eslint-plugin-import/blob/d81f48a2506182738409805f5272eff4d77c9348/docs/rules/no-cycle.md
242
- "import/no-cycle": ["error", { maxDepth: "∞" }],
245
+ // Warn (not error): cyclic imports are common and often unavoidable in TypeScript
246
+ // and backend code (e.g. type-only cycles), so flag rather than block.
247
+ "import/no-cycle": ["warn", { maxDepth: "∞" }],
243
248
 
244
249
  // Ensures that there are no useless path segments
245
250
  // https://github.com/import-js/eslint-plugin-import/blob/ebafcbf59ec9f653b2ac2a0156ca3bcba0a7cf57/docs/rules/no-useless-path-segments.md
@@ -1,13 +1,76 @@
1
1
  module.exports = {
2
2
  plugins: ["react-hooks"],
3
3
 
4
- extends: ["plugin:react-hooks/recommended"],
5
-
4
+ // NOTE: We intentionally do NOT extend "plugin:react-hooks/recommended".
5
+ // As of eslint-plugin-react-hooks v7 that preset enables the entire React
6
+ // Compiler ruleset at "error". We enumerate every rule explicitly so that a
7
+ // future plugin upgrade can never silently turn new rules on again.
8
+ // Full list: https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
6
9
  rules: {
7
- // Verify the list of the dependencies for Hooks like useEffect and similar
8
- // https://github.com/facebook/react/blob/1204c789776cb01fbaf3e9f032e7e2ba85a44137/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
10
+ // --- Core Hooks rules ---
11
+
12
+ // Hooks must be called at the top level, unconditionally
13
+ // https://react.dev/reference/rules/rules-of-hooks
14
+ "react-hooks/rules-of-hooks": "error",
15
+
16
+ // Verify the dependency list of useEffect/useCallback/useMemo/etc.
17
+ // https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md
9
18
  "react-hooks/exhaustive-deps": "warn",
10
19
 
11
- "react-hooks/immutability": "off",
20
+ // --- Rules of React (from the React Compiler ruleset) ---
21
+ // Kept as warnings: each catches a real "you're probably holding it wrong"
22
+ // case, but each also has legitimate exceptions, so we notify instead of block.
23
+
24
+ // Calling setState synchronously inside an effect (cascading re-renders).
25
+ // Valid in some cases (e.g. deriving state), so warn rather than error.
26
+ "react-hooks/set-state-in-effect": "warn",
27
+
28
+ // Calling setState during render (infinite-loop risk). Valid only for the
29
+ // documented "adjust state while rendering on prop change" pattern, so warn.
30
+ "react-hooks/set-state-in-render": "warn",
31
+
32
+ // Render must be pure — no side effects during render
33
+ "react-hooks/purity": "warn",
34
+
35
+ // Do not mutate props, state, hook arguments, or values returned by hooks
36
+ "react-hooks/immutability": "warn",
37
+
38
+ // Do not read or write ref.current during render
39
+ "react-hooks/refs": "warn",
40
+
41
+ // Do not read or mutate global/module state during render
42
+ "react-hooks/globals": "warn",
43
+
44
+ // Correct useMemo usage
45
+ "react-hooks/use-memo": "warn",
46
+
47
+ // Components/hooks should be defined statically, not recreated on each render
48
+ // (overlaps with react/no-unstable-nested-components)
49
+ "react-hooks/static-components": "warn",
50
+
51
+ // Do not define components or hooks inside factory functions
52
+ "react-hooks/component-hook-factories": "warn",
53
+
54
+ // Correct error-boundary usage
55
+ "react-hooks/error-boundaries": "warn",
56
+
57
+ // --- React Compiler infrastructure rules ---
58
+ // Off: these only mean anything once you actually adopt the React Compiler.
59
+ // Turn them on per-project if/when you enable the compiler.
60
+
61
+ // Existing useMemo/useCallback must stay valid so the compiler can preserve them
62
+ "react-hooks/preserve-manual-memoization": "off",
63
+
64
+ // React Compiler runtime feature-gating config
65
+ "react-hooks/gating": "off",
66
+
67
+ // Validates the React Compiler config object
68
+ "react-hooks/config": "off",
69
+
70
+ // Warns about syntax the compiler cannot handle
71
+ "react-hooks/unsupported-syntax": "off",
72
+
73
+ // Warns about libraries that are incompatible with the compiler
74
+ "react-hooks/incompatible-library": "off",
12
75
  },
13
76
  }