eslint-plugin-functype 1.1.2 → 1.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,231 +1,283 @@
1
1
  # eslint-plugin-functype
2
2
 
3
- A curated ESLint configuration bundle for functional TypeScript programming. This plugin combines and configures rules from established ESLint plugins to enforce immutability patterns and functional programming best practices.
3
+ Custom ESLint rules for functional TypeScript programming with [functype](https://github.com/jordanburke/functype) library patterns. Enforces immutability, type safety, and functional programming best practices for ESLint 9+.
4
4
 
5
- ## What This Plugin Does
5
+ [![npm version](https://badge.fury.io/js/eslint-plugin-functype.svg)](https://www.npmjs.com/package/eslint-plugin-functype)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
- Instead of recreating functional programming rules, this plugin provides carefully curated configurations that combine rules from:
8
+ ## Features
8
9
 
9
- - **eslint-plugin-functional**: Core functional programming rules
10
- - **@typescript-eslint/eslint-plugin**: TypeScript-specific functional patterns
11
- - **ESLint core**: JavaScript immutability basics
10
+ - 🔧 **8 Custom ESLint Rules** - Purpose-built for functional TypeScript patterns
11
+ - 🏗️ **Functype Library Integration** - Smart detection when functype is already being used properly
12
+ - 🛠️ **Auto-Fixable** - Most violations can be automatically fixed with `--fix`
13
+ - ⚡ **ESLint 9+ Flat Config** - Modern ESLint configuration format
14
+ - 🎯 **TypeScript Native** - Built specifically for TypeScript AST patterns
15
+ - 📊 **99 Tests** - Comprehensive test coverage including real functype integration
16
+
17
+ ## Rules
18
+
19
+ | Rule | Description | Auto-Fix |
20
+ |------|-------------|----------|
21
+ | `prefer-option` | Prefer `Option<T>` over nullable types (`T \| null \| undefined`) | ✅ |
22
+ | `prefer-either` | Prefer `Either<E, T>` over try/catch and throw statements | ✅ |
23
+ | `prefer-list` | Prefer `List<T>` over native arrays for immutable collections | ✅ |
24
+ | `prefer-fold` | Prefer `.fold()` over complex if/else chains | ✅ |
25
+ | `prefer-map` | Prefer `.map()` over imperative transformations | ✅ |
26
+ | `prefer-flatmap` | Prefer `.flatMap()` over `.map().flat()` patterns | ✅ |
27
+ | `no-get-unsafe` | Disallow unsafe `.get()` calls on Option/Either types | ❌ |
28
+ | `no-imperative-loops` | Prefer functional iteration over imperative loops | ✅ |
12
29
 
13
30
  ## Installation
14
31
 
15
32
  ```bash
16
- npm install --save-dev eslint-plugin-functype @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-functional
33
+ npm install --save-dev eslint-plugin-functype
17
34
  # or
18
- pnpm add -D eslint-plugin-functype @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-functional
19
- ```
20
-
21
- ## Usage
22
-
23
- ### ESLint 8 (.eslintrc) - Quick Start
24
-
25
- ```javascript
26
- // .eslintrc.js
27
- module.exports = {
28
- extends: ["plugin:functype/recommended"],
29
- parser: "@typescript-eslint/parser",
30
- parserOptions: {
31
- project: "./tsconfig.json",
32
- },
33
- }
35
+ pnpm add -D eslint-plugin-functype
34
36
  ```
35
37
 
36
- ### ESLint 8 - Strict Mode
38
+ **Optional:** Install functype library for enhanced integration:
37
39
 
38
- ```javascript
39
- // .eslintrc.js
40
- module.exports = {
41
- extends: ["plugin:functype/strict"],
42
- parser: "@typescript-eslint/parser",
43
- parserOptions: {
44
- project: "./tsconfig.json",
45
- },
46
- }
40
+ ```bash
41
+ npm install functype
42
+ # or
43
+ pnpm add functype
47
44
  ```
48
45
 
49
- ### ESLint 9+ (Flat Config)
46
+ ## Usage
50
47
 
51
- For ESLint 9+, create your own flat config using our rule selections:
48
+ ### ESLint 9+ Flat Config (Recommended)
52
49
 
53
50
  ```javascript
54
- // eslint.config.js
55
- import js from "@eslint/js"
56
- import tseslint from "@typescript-eslint/eslint-plugin"
57
- import functional from "eslint-plugin-functional"
58
- import parser from "@typescript-eslint/parser"
51
+ // eslint.config.mjs
52
+ import functypePlugin from 'eslint-plugin-functype'
53
+ import tsParser from '@typescript-eslint/parser'
59
54
 
60
55
  export default [
61
- js.configs.recommended,
62
56
  {
63
- files: ["**/*.ts", "**/*.tsx"],
57
+ files: ['**/*.ts', '**/*.tsx'],
58
+ plugins: {
59
+ functype: functypePlugin,
60
+ },
64
61
  languageOptions: {
65
- parser,
62
+ parser: tsParser,
66
63
  parserOptions: {
67
- ecmaVersion: "latest",
68
- sourceType: "module",
69
- project: "./tsconfig.json",
64
+ ecmaVersion: 2022,
65
+ sourceType: 'module',
70
66
  },
71
67
  },
72
- plugins: {
73
- "@typescript-eslint": tseslint,
74
- functional,
68
+ rules: {
69
+ // All rules as errors
70
+ 'functype/prefer-option': 'error',
71
+ 'functype/prefer-either': 'error',
72
+ 'functype/prefer-list': 'error',
73
+ 'functype/prefer-fold': 'error',
74
+ 'functype/prefer-map': 'error',
75
+ 'functype/prefer-flatmap': 'error',
76
+ 'functype/no-get-unsafe': 'error',
77
+ 'functype/no-imperative-loops': 'error',
75
78
  },
79
+ },
80
+ ]
81
+ ```
82
+
83
+ ### Individual Rule Configuration
84
+
85
+ ```javascript
86
+ // eslint.config.mjs - Selective rules
87
+ export default [
88
+ {
89
+ files: ['**/*.ts'],
90
+ plugins: { functype: functypePlugin },
76
91
  rules: {
77
- // Core immutability
78
- "prefer-const": "error",
79
- "no-var": "error",
92
+ // Start with just type safety rules
93
+ 'functype/prefer-option': 'warn',
94
+ 'functype/no-get-unsafe': 'error',
80
95
 
81
- // TypeScript functional patterns
82
- "@typescript-eslint/consistent-type-imports": "error",
83
- "@typescript-eslint/no-explicit-any": "error",
84
- "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
85
-
86
- // Functional programming rules
87
- "functional/no-let": "error",
88
- "functional/immutable-data": "warn",
89
- "functional/no-loop-statements": "off", // Enable as "error" for strict mode
96
+ // Add more as your codebase evolves
97
+ 'functype/prefer-list': 'off', // Disable for gradual adoption
90
98
  },
91
- }
99
+ },
92
100
  ]
93
101
  ```
94
102
 
95
- ### Individual Rule Usage
103
+ ## Examples
96
104
 
97
- You can also use individual rules without our presets:
105
+ ### Before (violations flagged)
98
106
 
99
- ```javascript
100
- {
101
- "plugins": ["@typescript-eslint", "functional"],
102
- "rules": {
103
- "functional/no-let": "error",
104
- "functional/immutable-data": "warn",
105
- "@typescript-eslint/no-explicit-any": "error",
106
- "prefer-const": "error"
107
- }
107
+ ```typescript
108
+ // prefer-option: nullable types
109
+ const user: User | null = findUser(id)
110
+ function getAge(): number | undefined { /* ... */ }
111
+
112
+ // prefer-either: try/catch blocks
113
+ try {
114
+ const result = riskyOperation()
115
+ return result
116
+ } catch (error) {
117
+ console.error(error)
118
+ return null
108
119
  }
109
- ```
110
120
 
111
- ## What Rules Are Included
121
+ // prefer-list: native arrays
122
+ const items: number[] = [1, 2, 3]
123
+ const readonlyItems: ReadonlyArray<string> = ["a", "b"]
112
124
 
113
- ### Recommended Configuration
114
- - `prefer-const` / `no-var` - Basic immutability
115
- - ✅ `functional/no-let` - Disallow `let` declarations
116
- - ⚠️ `functional/immutable-data` - Warn on data mutation
117
- - ⚠️ `functional/no-mutation` - Warn on object/array mutations
118
- - ✅ `@typescript-eslint/consistent-type-imports` - Clean imports
119
- - ✅ `@typescript-eslint/no-explicit-any` - Type safety
125
+ // no-imperative-loops: for/while loops
126
+ for (let i = 0; i < items.length; i++) {
127
+ console.log(items[i])
128
+ }
120
129
 
121
- ### Strict Configuration
122
- All recommended rules plus:
123
- - ✅ `functional/no-loop-statements` - Disallow imperative loops
124
- - `functional/immutable-data` - Error on data mutation
125
- - ✅ `functional/prefer-immutable-types` - Encourage readonly types
126
- - `@typescript-eslint/explicit-function-return-type` - Explicit return types
130
+ // prefer-fold: complex if/else chains
131
+ if (condition1) {
132
+ return value1
133
+ } else if (condition2) {
134
+ return value2
135
+ } else {
136
+ return defaultValue
137
+ }
138
+ ```
127
139
 
128
- ## Examples
140
+ ### ✅ After (auto-fixed or manually corrected)
129
141
 
130
142
  ```typescript
131
- // Bad (will be flagged)
132
- let x = 1; // functional/no-let
133
- arr.push(item); // functional/immutable-data
134
- for(let i = 0; i < 10; i++) // functional/no-loop-statements (strict only)
135
-
136
- // ✅ Good
137
- const x = 1;
138
- const newArr = [...arr, item];
139
- arr.forEach(item => process(item));
143
+ import { Option, Either, List } from 'functype'
144
+
145
+ // prefer-option: use Option<T>
146
+ const user: Option<User> = Option.fromNullable(findUser(id))
147
+ function getAge(): Option<number> { /* ... */ }
148
+
149
+ // prefer-either: use Either<E, T>
150
+ function safeOperation(): Either<Error, Result> {
151
+ try {
152
+ const result = riskyOperation()
153
+ return Either.right(result)
154
+ } catch (error) {
155
+ return Either.left(error as Error)
156
+ }
157
+ }
158
+
159
+ // prefer-list: use List<T>
160
+ const items: List<number> = List.from([1, 2, 3])
161
+ const readonlyItems: List<string> = List.from(["a", "b"])
162
+
163
+ // no-imperative-loops: use functional methods
164
+ items.forEach(item => console.log(item))
165
+
166
+ // prefer-fold: use fold for conditional logic
167
+ const result = Option.fromBoolean(condition1)
168
+ .map(() => value1)
169
+ .orElse(() => Option.fromBoolean(condition2).map(() => value2))
170
+ .getOrElse(defaultValue)
140
171
  ```
141
172
 
142
- ## Configurations
173
+ ## Functype Integration
143
174
 
144
- - **`recommended`**: Balanced functional programming rules suitable for most projects
145
- - **`strict`**: Maximum functional programming enforcement for pure FP codebases
175
+ The plugin is **functype-aware** and won't flag code that's already using functype properly:
146
176
 
147
- ## Philosophy
177
+ ```typescript
178
+ import { Option, List } from 'functype'
148
179
 
149
- This plugin follows the principle of **composition over recreation**. Rather than maintaining custom rules, we curate and combine battle-tested rules from the community, ensuring:
180
+ // These won't be flagged - already using functype correctly
181
+ const user = Option.some({ name: "Alice" })
182
+ const items = List.from([1, 2, 3])
183
+ const result = user.map(u => u.name).getOrElse("Unknown")
150
184
 
151
- - Less maintenance burden
152
- - Better rule quality and edge case handling
153
- - Automatic updates from upstream plugins
154
- - ✅ Community-driven improvements
185
+ // ❌ These will still be flagged - bad patterns even with functype available
186
+ const badUser: User | null = null // prefer-option
187
+ const badItems = [1, 2, 3] // prefer-list
188
+ ```
155
189
 
156
190
  ## CLI Tools
157
191
 
158
- ### List All Supported Rules
159
-
160
- See exactly which rules are configured in each preset:
192
+ ### List All Rules
161
193
 
162
194
  ```bash
163
- # Basic rule listing
195
+ # After installation
196
+ npx functype-list-rules
197
+
198
+ # During development
164
199
  pnpm run list-rules
165
200
 
166
- # Show rule options/configuration
167
- pnpm run list-rules:verbose
201
+ # Verbose output with configurations
202
+ pnpm run list-rules:verbose
168
203
 
169
- # Show usage examples
204
+ # Usage examples
170
205
  pnpm run list-rules:usage
206
+ ```
171
207
 
172
- # Check peer dependency status
173
- pnpm run check-deps
208
+ ### Development Commands
174
209
 
175
- # Show help
176
- pnpm run cli:help
177
- ```
210
+ ```bash
211
+ # Install dependencies
212
+ pnpm install
213
+
214
+ # Build plugin
215
+ pnpm run build
178
216
 
179
- ### After Installation
217
+ # Run tests (99 tests)
218
+ pnpm test
180
219
 
181
- Once installed globally or in a project, you can also use:
220
+ # Lint codebase
221
+ pnpm run lint
182
222
 
183
- ```bash
184
- # If installed globally
185
- functype-list-rules --help
223
+ # Type check
224
+ pnpm run typecheck
186
225
 
187
- # Or with npx
188
- npx eslint-plugin-functype functype-list-rules
226
+ # Run all quality checks
227
+ pnpm run check
189
228
  ```
190
229
 
191
- ## CI/CD
230
+ ## Architecture
192
231
 
193
- This plugin includes GitHub Actions workflows for:
194
- - ✅ **Testing** on Node.js 18, 20, 22
195
- - ✅ **Linting** with our own rules
196
- - ✅ **Building** and validation
197
- - ✅ **Publishing** to npm on version changes
232
+ ### Philosophy: Custom Rules for Precise Control
198
233
 
199
- ## Development
234
+ This plugin provides **custom ESLint rules** specifically designed for functional TypeScript patterns, rather than composing existing rules. This approach offers:
200
235
 
201
- ```bash
202
- # Install dependencies
203
- pnpm install
236
+ - 🎯 **Precise AST Analysis** - Rules understand TypeScript-specific patterns
237
+ - 🔧 **Smart Auto-Fixing** - Context-aware fixes that maintain code intent
238
+ - 📚 **Functype Integration** - Built-in detection of functype library usage
239
+ - 🚀 **Better Performance** - Single-pass analysis instead of multiple rule evaluations
204
240
 
205
- # Build
206
- pnpm run build
241
+ ### ESLint 9+ Flat Config Only
207
242
 
208
- # Lint
209
- pnpm run lint
243
+ - **Modern Configuration** - Uses ESLint 9.x flat config format
244
+ - **No Legacy Support** - Clean architecture without backwards compatibility burden
245
+ - **Plugin-First Design** - Designed specifically as an ESLint plugin
210
246
 
211
- # List rules during development
212
- pnpm run list-rules
213
- ```
247
+ ### Test Coverage
248
+
249
+ - **99 Tests Total** across 10 test suites
250
+ - **Integration Tests** with real functype library usage
251
+ - **Auto-Fix Verification** ensures fixes produce valid code
252
+ - **False Positive Prevention** tests ensure proper functype patterns aren't flagged
214
253
 
215
- ## Troubleshooting
254
+ ## Contributing
216
255
 
217
- ### Missing Peer Dependencies
256
+ 1. **Fork** the repository
257
+ 2. **Create** a feature branch: `git checkout -b feature-name`
258
+ 3. **Make** your changes and add tests
259
+ 4. **Ensure** all quality checks pass: `pnpm run check`
260
+ 5. **Submit** a pull request
218
261
 
219
- If you see errors like `Definition for rule '@typescript-eslint/no-explicit-any' was not found`, you're missing peer dependencies.
262
+ ### Development Setup
220
263
 
221
- **Quick Check:**
222
264
  ```bash
223
- pnpm run check-deps
265
+ git clone https://github.com/jordanburke/eslint-plugin-functype.git
266
+ cd eslint-plugin-functype
267
+ pnpm install
268
+ pnpm run build
269
+ pnpm test
224
270
  ```
225
271
 
226
- This will show you exactly which dependencies are missing and provide the installation command.
272
+ ## License
227
273
 
228
- **Manual Installation:**
229
- ```bash
230
- pnpm add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-functional eslint-plugin-prettier eslint-plugin-simple-import-sort prettier
231
- ```
274
+ [MIT](LICENSE) © [Jordan Burke](https://github.com/jordanburke)
275
+
276
+ ## Related
277
+
278
+ - **[functype](https://github.com/jordanburke/functype)** - Functional programming library for TypeScript
279
+ - **[eslint-config-functype](https://github.com/jordanburke/eslint-config-functype)** - Complete ESLint config for functional TypeScript projects
280
+
281
+ ---
282
+
283
+ **Need help?** [Open an issue](https://github.com/jordanburke/eslint-plugin-functype/issues) or check the [functype documentation](https://jordanburke.github.io/functype/).