eslint-config-agent 1.1.4 โ†’ 1.2.1

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +3 -85
  3. package/index.js +14 -5
  4. package/package.json +2 -2
  5. package/rules/max-file-lines/examples/invalid/long-file.js +113 -0
  6. package/rules/max-file-lines/examples/invalid/medium-file.js +127 -0
  7. package/rules/max-file-lines/examples/invalid/very-long-file.ts +144 -0
  8. package/rules/max-file-lines/examples/valid/short-file.js +66 -0
  9. package/rules/max-file-lines/examples/valid/spec-file.spec.js +44 -0
  10. package/rules/max-file-lines/examples/valid/test-file.test.js +25 -0
  11. package/rules/plugin/import/group-exports/examples/invalid/mixed-declaration-styles.js +8 -0
  12. package/rules/plugin/import/group-exports/examples/invalid/multiple-export-statements.js +8 -0
  13. package/rules/plugin/import/group-exports/examples/invalid/scattered-exports.js +10 -0
  14. package/rules/plugin/import/group-exports/examples/valid/direct-exports.js +2 -0
  15. package/rules/plugin/import/group-exports/examples/valid/mixed-with-default.js +7 -0
  16. package/rules/plugin/import/group-exports/examples/valid/single-export-statement.js +6 -0
  17. package/rules/plugin/import/group-exports/index.js +3 -0
  18. package/rules/plugin/import/index.js +5 -1
  19. package/rules/plugin/import/no-unused-modules/examples/invalid/dead-code.js +2 -0
  20. package/rules/plugin/import/no-unused-modules/examples/invalid/unused-class.js +6 -0
  21. package/rules/plugin/import/no-unused-modules/examples/invalid/unused-export.js +2 -0
  22. package/rules/plugin/import/no-unused-modules/examples/invalid/unused-function.js +4 -0
  23. package/rules/plugin/import/no-unused-modules/examples/invalid/unused-module.js +4 -0
  24. package/rules/plugin/import/no-unused-modules/examples/valid/consumed-import.js +8 -0
  25. package/rules/plugin/import/no-unused-modules/examples/valid/entry-point.js +5 -0
  26. package/rules/plugin/import/no-unused-modules/examples/valid/used-class.js +6 -0
  27. package/rules/plugin/import/no-unused-modules/examples/valid/used-export.js +2 -0
  28. package/rules/plugin/import/no-unused-modules/examples/valid/used-function.js +4 -0
  29. package/rules/plugin/import/no-unused-modules/index.js +6 -0
  30. package/rules/plugin/typescript-eslint/index.js +3 -1
  31. package/rules/plugin/typescript-eslint/no-explicit-any/examples/invalid/any-array.ts +2 -0
  32. package/rules/plugin/typescript-eslint/no-explicit-any/examples/invalid/any-parameter.ts +4 -0
  33. package/rules/plugin/typescript-eslint/no-explicit-any/examples/invalid/any-return-type.ts +4 -0
  34. package/rules/plugin/typescript-eslint/no-explicit-any/examples/invalid/any-variable.ts +5 -0
  35. package/rules/plugin/typescript-eslint/no-explicit-any/examples/valid/generics.ts +16 -0
  36. package/rules/plugin/typescript-eslint/no-explicit-any/examples/valid/specific-types.ts +9 -0
  37. package/rules/plugin/typescript-eslint/no-explicit-any/examples/valid/unknown-type.ts +8 -0
  38. package/rules/plugin/typescript-eslint/no-explicit-any/index.js +3 -0
  39. package/rules/README.md +0 -42
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file. See [Conven
4
4
 
5
5
 
6
6
 
7
+ ## [1.2.1](https://github.com/tupe12334/eslint-config/compare/v1.2.0...v1.2.1) (2025-09-11)
8
+
9
+ ### Features
10
+
11
+ * remove outdated TODO and ESLint rules documentation ([4d820ce](https://github.com/tupe12334/eslint-config/commit/4d820cec99d8ffb8e9e23c8c313e644351871d70))
12
+
13
+ ## [1.2.0](https://github.com/tupe12334/eslint-config/compare/v1.1.4...v1.2.0) (2025-09-11)
14
+
15
+ ### Features
16
+
17
+ * add examples for valid and invalid export patterns and update import rules configuration ([a78e03d](https://github.com/tupe12334/eslint-config/commit/a78e03d201e335b423c1830593c015ddd0346028))
18
+ * add no-explicit-any rule configuration and examples for valid/invalid usage ([2bc0ad8](https://github.com/tupe12334/eslint-config/commit/2bc0ad836ff42199456ebc7ebd0b6948cca1a7b1))
19
+ * add no-unused-modules rule configuration and examples for unused exports ([336db1b](https://github.com/tupe12334/eslint-config/commit/336db1b26bf4ac0d3e9bd56c95817748bfda4799))
20
+ * refine ESLint configuration to include specific index.js files and update CI test command ([5693dbc](https://github.com/tupe12334/eslint-config/commit/5693dbc65dc37cbddb6a41498b4886d360c17684))
21
+ * update ESLint configuration to disable size limits for test/spec files and add examples for valid/invalid file lengths ([ac57e22](https://github.com/tupe12334/eslint-config/commit/ac57e223f7d1cedab32976cee5f305ca197ca0c8))
22
+ * update export statements in test files to use default export syntax ([c4a846b](https://github.com/tupe12334/eslint-config/commit/c4a846bc78105f287fafcb4248c8de334c84b0fb))
23
+
7
24
  ## [1.1.4](https://github.com/tupe12334/eslint-config/compare/v1.1.3...v1.1.4) (2025-09-10)
8
25
 
9
26
  ### Features
package/README.md CHANGED
@@ -209,36 +209,6 @@ This ESLint configuration prioritizes **explicit code** over convenient shortcut
209
209
  - **๐Ÿ“š Self-Documenting**: Code that explains its intent without extensive comments
210
210
  - **๐Ÿ› ๏ธ Maintainable**: Patterns that remain clear even as the codebase grows
211
211
 
212
- ### Key Rules Enforced
213
-
214
- #### Type Safety Rules
215
- - **๐Ÿšซ No Optional Chaining** (`?.`): Forces explicit null/undefined checks
216
- ```typescript
217
- // โŒ Avoid
218
- const name = user?.profile?.name;
219
-
220
- // โœ… Preferred
221
- const name = user && user.profile ? user.profile.name : undefined;
222
- ```
223
-
224
- - **๐Ÿšซ No Nullish Coalescing** (`??`): Use explicit checks instead
225
- ```typescript
226
- // โŒ Avoid
227
- const name = userName ?? 'Anonymous';
228
-
229
- // โœ… Preferred
230
- const name = userName !== null && userName !== undefined ? userName : 'Anonymous';
231
- ```
232
-
233
- #### File Organization Rules
234
- - **๐Ÿ“ JSX File Extensions**: JSX syntax only allowed in `.tsx` and `.jsx` files
235
- - **๐Ÿ“ Function Length**: Maximum 100 lines per function (warning level)
236
- - **๐Ÿงน No Trailing Spaces**: Enforces clean, consistent formatting
237
-
238
- #### React & TypeScript Rules
239
- - **โš›๏ธ React Hooks**: Full enforcement of hooks rules and dependency arrays
240
- - **๐Ÿ”ค TypeScript**: Strict type checking with explicit type assertions
241
- - **๐Ÿ“ฆ Import/Export**: Consistent module patterns and import ordering
242
212
 
243
213
  ### Supported File Types & Configurations
244
214
 
@@ -263,21 +233,9 @@ This ESLint configuration prioritizes **explicit code** over convenient shortcut
263
233
  - Compatible with Preact-specific patterns and optimizations
264
234
  - Shared configuration with React rules where applicable
265
235
 
266
- ### Rule Categories
236
+ ### Configuration Philosophy
267
237
 
268
- #### **Enabled Rules** (Errors & Warnings)
269
- - TypeScript strict type checking
270
- - React hooks validation
271
- - Import/export consistency
272
- - Function complexity limits
273
- - Whitespace and formatting
274
-
275
- #### **Disabled Rules**
276
- Many common ESLint and React rules are intentionally disabled to reduce noise while focusing on the most impactful code quality issues:
277
- - `react/react-in-jsx-scope` (not needed with modern React)
278
- - `@typescript-eslint/no-unused-vars` (TypeScript handles this)
279
- - `no-undef` (TypeScript handles this)
280
- - Plus 50+ other rules optimized for developer productivity
238
+ This configuration focuses on enforcing patterns that improve long-term maintainability while reducing noise from less impactful rules. The ruleset is carefully curated to balance developer productivity with code quality.
281
239
 
282
240
  ## Migration Guide
283
241
 
@@ -398,37 +356,6 @@ npm install --save-dev eslint@^9.34.0
398
356
 
399
357
  ### Frequently Asked Questions
400
358
 
401
- #### **Q: Why are optional chaining and nullish coalescing disabled?**
402
- A: These features, while convenient, can hide potential runtime errors. Explicit null checks make your code's intention clearer and prevent unexpected behavior when dealing with complex nested objects.
403
-
404
- #### **Q: Can I enable optional chaining for specific files?**
405
- A: Yes! Use file-specific overrides:
406
- ```javascript
407
- export default [
408
- ...config,
409
- {
410
- files: ['src/legacy/**/*.ts'],
411
- rules: {
412
- '@typescript-eslint/prefer-nullish-coalescing': 'off',
413
- '@typescript-eslint/prefer-optional-chain': 'off',
414
- }
415
- }
416
- ];
417
- ```
418
-
419
- #### **Q: How do I handle large existing codebases?**
420
- A: Start with warnings instead of errors:
421
- ```javascript
422
- export default [
423
- ...config,
424
- {
425
- rules: {
426
- '@typescript-eslint/prefer-nullish-coalescing': 'warn',
427
- '@typescript-eslint/prefer-optional-chain': 'warn',
428
- }
429
- }
430
- ];
431
- ```
432
359
 
433
360
  #### **Q: Does this work with monorepos?**
434
361
  A: Yes! Place the `eslint.config.js` at the root of each package, or use a shared config:
@@ -443,16 +370,7 @@ export default sharedConfig;
443
370
  ```
444
371
 
445
372
  #### **Q: How does this help with AI coding assistants like GitHub Copilot, Claude, or ChatGPT?**
446
- A: This configuration prevents AI assistants from generating "convenient" but problematic code patterns:
447
-
448
- - **๐Ÿšซ Prevents**: AI generating `user?.profile?.name` shortcuts
449
- - **โœ… Enforces**: AI writing `user && user.profile ? user.profile.name : undefined`
450
- - **๐Ÿšซ Prevents**: AI using `value ?? fallback` patterns
451
- - **โœ… Enforces**: AI writing explicit null/undefined checks
452
- - **๐Ÿ“ Limits**: Function length so AI doesn't generate massive functions
453
- - **๐Ÿ—‚๏ธ Organizes**: File structure so AI puts JSX only in proper file extensions
454
-
455
- This means you get AI assistance while maintaining code that you can actually understand and debug months later.
373
+ A: This configuration helps AI assistants generate more maintainable and debuggable code by enforcing consistent patterns and preventing problematic shortcuts. This means you get AI assistance while maintaining code that you can actually understand and debug months later.
456
374
 
457
375
  #### **Q: How do I contribute or report issues?**
458
376
  A: Please use the [GitHub repository](https://github.com/tupe12334/eslint-config) for issues and contributions.
package/index.js CHANGED
@@ -423,7 +423,7 @@ const config = [
423
423
  "**/*.cjs",
424
424
  "**/*.mjs",
425
425
  "**/*.stories.{js,jsx,ts,tsx}",
426
- "**/rules/**",
426
+ "**/rules/**/index.js",
427
427
  ],
428
428
  languageOptions: {
429
429
  parserOptions: {
@@ -684,7 +684,7 @@ const config = [
684
684
  },
685
685
  },
686
686
 
687
- // Disable function size limits for test and spec files
687
+ // Disable function and file size limits for test and spec files
688
688
  {
689
689
  files: [
690
690
  "**/*.test.{js,jsx,ts,tsx}",
@@ -700,6 +700,7 @@ const config = [
700
700
  ],
701
701
  rules: {
702
702
  "max-lines-per-function": "off",
703
+ "max-lines": "off", // Ignore file length limits in test and spec files
703
704
  // Allow multiple exports in test files for testing import/export patterns
704
705
  "no-restricted-syntax": [
705
706
  "warn",
@@ -805,7 +806,7 @@ const config = [
805
806
  "**/test/**",
806
807
  "!**/test/export/**",
807
808
  "!**/test/required-exports/**",
808
- "**/rules/**",
809
+ "**/rules/**/index.js",
809
810
  ],
810
811
  rules: {
811
812
  "no-restricted-syntax": [
@@ -916,7 +917,14 @@ const config = [
916
917
  // Function and file length rules - strict error thresholds
917
918
  {
918
919
  files: ["**/*.{ts,tsx,js,jsx}"],
919
- ignores: ["**/*.stories.{js,jsx,ts,tsx}"],
920
+ ignores: [
921
+ "**/*.stories.{js,jsx,ts,tsx}",
922
+ "**/*.test.{js,jsx,ts,tsx}",
923
+ "**/*.spec.{js,jsx,ts,tsx}",
924
+ "**/test/**/*.{js,jsx,ts,tsx}",
925
+ "**/tests/**/*.{js,jsx,ts,tsx}",
926
+ "**/__tests__/**/*.{js,jsx,ts,tsx}",
927
+ ],
920
928
  rules: {
921
929
  // Function length: error at 70+ lines
922
930
  "max-lines-per-function": allRules.maxFunctionLinesError,
@@ -977,9 +985,10 @@ const config = [
977
985
  },
978
986
  },
979
987
 
980
- // Rules directory configuration - allow export specifiers for API definitions
988
+ // Rules directory configuration - allow export specifiers for API definitions (but not examples)
981
989
  {
982
990
  files: ["**/rules/**/*.{js,ts}"],
991
+ ignores: ["**/rules/**/examples/**"],
983
992
  languageOptions: {
984
993
  globals: {
985
994
  ...globals.node,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-config-agent",
3
- "version": "1.1.4",
3
+ "version": "1.2.1",
4
4
  "description": "ESLint configuration package with TypeScript support",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -28,7 +28,7 @@
28
28
  "test:edge": "eslint test/edge-cases.tsx",
29
29
  "test:performance": "eslint test/performance-test.tsx",
30
30
  "test:comprehensive": "node scripts/test-runner.js",
31
- "test:ci": "eslint . --ignore-pattern 'test/**' --ignore-pattern 'scripts/**' --max-warnings 0",
31
+ "test:ci": "eslint . --ignore-pattern 'test/**' --ignore-pattern 'scripts/**' --ignore-pattern 'rules/**/examples/**' --max-warnings 0",
32
32
  "validate": "node scripts/validate-config.js",
33
33
  "release": "dotenv -e .env -- release-it",
34
34
  "release:patch": "dotenv -e .env -- release-it patch",
@@ -0,0 +1,113 @@
1
+ // Invalid: File with more than 100 lines (should trigger max-lines error)
2
+ const line1 = 1;
3
+ const line2 = 2;
4
+ const line3 = 3;
5
+ const line4 = 4;
6
+ const line5 = 5;
7
+ const line6 = 6;
8
+ const line7 = 7;
9
+ const line8 = 8;
10
+ const line9 = 9;
11
+ const line10 = 10;
12
+ const line11 = 11;
13
+ const line12 = 12;
14
+ const line13 = 13;
15
+ const line14 = 14;
16
+ const line15 = 15;
17
+ const line16 = 16;
18
+ const line17 = 17;
19
+ const line18 = 18;
20
+ const line19 = 19;
21
+ const line20 = 20;
22
+ const line21 = 21;
23
+ const line22 = 22;
24
+ const line23 = 23;
25
+ const line24 = 24;
26
+ const line25 = 25;
27
+ const line26 = 26;
28
+ const line27 = 27;
29
+ const line28 = 28;
30
+ const line29 = 29;
31
+ const line30 = 30;
32
+ const line31 = 31;
33
+ const line32 = 32;
34
+ const line33 = 33;
35
+ const line34 = 34;
36
+ const line35 = 35;
37
+ const line36 = 36;
38
+ const line37 = 37;
39
+ const line38 = 38;
40
+ const line39 = 39;
41
+ const line40 = 40;
42
+ const line41 = 41;
43
+ const line42 = 42;
44
+ const line43 = 43;
45
+ const line44 = 44;
46
+ const line45 = 45;
47
+ const line46 = 46;
48
+ const line47 = 47;
49
+ const line48 = 48;
50
+ const line49 = 49;
51
+ const line50 = 50;
52
+ const line51 = 51;
53
+ const line52 = 52;
54
+ const line53 = 53;
55
+ const line54 = 54;
56
+ const line55 = 55;
57
+ const line56 = 56;
58
+ const line57 = 57;
59
+ const line58 = 58;
60
+ const line59 = 59;
61
+ const line60 = 60;
62
+ const line61 = 61;
63
+ const line62 = 62;
64
+ const line63 = 63;
65
+ const line64 = 64;
66
+ const line65 = 65;
67
+ const line66 = 66;
68
+ const line67 = 67;
69
+ const line68 = 68;
70
+ const line69 = 69;
71
+ const line70 = 70;
72
+ const line71 = 71;
73
+ const line72 = 72;
74
+ const line73 = 73;
75
+ const line74 = 74;
76
+ const line75 = 75;
77
+ const line76 = 76;
78
+ const line77 = 77;
79
+ const line78 = 78;
80
+ const line79 = 79;
81
+ const line80 = 80;
82
+ const line81 = 81;
83
+ const line82 = 82;
84
+ const line83 = 83;
85
+ const line84 = 84;
86
+ const line85 = 85;
87
+ const line86 = 86;
88
+ const line87 = 87;
89
+ const line88 = 88;
90
+ const line89 = 89;
91
+ const line90 = 90;
92
+ const line91 = 91;
93
+ const line92 = 92;
94
+ const line93 = 93;
95
+ const line94 = 94;
96
+ const line95 = 95;
97
+ const line96 = 96;
98
+ const line97 = 97;
99
+ const line98 = 98;
100
+ const line99 = 99;
101
+ const line100 = 100;
102
+ const line101 = 101;
103
+ const line102 = 102;
104
+ const line103 = 103;
105
+ const line104 = 104;
106
+ const line105 = 105;
107
+ const line106 = 106;
108
+ const line107 = 107;
109
+ const line108 = 108;
110
+ const line109 = 109;
111
+ const line110 = 110;
112
+
113
+ export default line1;
@@ -0,0 +1,127 @@
1
+ // Invalid: File with 70-100 lines (should trigger max-lines warning)
2
+ // This file demonstrates what happens when a file grows beyond the recommended size
3
+
4
+ class ExtendedNumberUtils {
5
+ constructor() {
6
+ this.cache = new Map();
7
+ }
8
+
9
+ calculateSum(numbers) {
10
+ const key = numbers.join(',');
11
+ if (this.cache.has(key)) {
12
+ return this.cache.get(key);
13
+ }
14
+ const sum = numbers.reduce((acc, num) => acc + num, 0);
15
+ this.cache.set(key, sum);
16
+ return sum;
17
+ }
18
+
19
+ calculateAverage(numbers) {
20
+ if (numbers.length === 0) return 0;
21
+ return this.calculateSum(numbers) / numbers.length;
22
+ }
23
+
24
+ findMaxValue(numbers) {
25
+ if (numbers.length === 0) return null;
26
+ return Math.max(...numbers);
27
+ }
28
+
29
+ findMinValue(numbers) {
30
+ if (numbers.length === 0) return null;
31
+ return Math.min(...numbers);
32
+ }
33
+
34
+ sortNumbers(numbers) {
35
+ return [...numbers].sort((a, b) => a - b);
36
+ }
37
+
38
+ filterEvenNumbers(numbers) {
39
+ return numbers.filter(num => num % 2 === 0);
40
+ }
41
+
42
+ filterOddNumbers(numbers) {
43
+ return numbers.filter(num => num % 2 !== 0);
44
+ }
45
+
46
+ multiplyByTwo(numbers) {
47
+ return numbers.map(num => num * 2);
48
+ }
49
+
50
+ containsNumber(numbers, target) {
51
+ return numbers.includes(target);
52
+ }
53
+
54
+ removeNumber(numbers, target) {
55
+ return numbers.filter(num => num !== target);
56
+ }
57
+
58
+ addNumber(numbers, newNumber) {
59
+ return [...numbers, newNumber];
60
+ }
61
+
62
+ getNumberAtIndex(numbers, index) {
63
+ if (index < 0 || index >= numbers.length) {
64
+ return undefined;
65
+ }
66
+ return numbers[index];
67
+ }
68
+
69
+ getLastNumber(numbers) {
70
+ return numbers.length > 0 ? numbers[numbers.length - 1] : undefined;
71
+ }
72
+
73
+ getFirstNumber(numbers) {
74
+ return numbers.length > 0 ? numbers[0] : undefined;
75
+ }
76
+
77
+ reverseNumbers(numbers) {
78
+ return [...numbers].reverse();
79
+ }
80
+
81
+ shuffleNumbers(numbers) {
82
+ const shuffled = [...numbers];
83
+ for (let i = shuffled.length - 1; i > 0; i--) {
84
+ const j = Math.floor(Math.random() * (i + 1));
85
+ const temp = shuffled[i];
86
+ shuffled[i] = shuffled[j];
87
+ shuffled[j] = temp;
88
+ }
89
+ return shuffled;
90
+ }
91
+
92
+ getUniqueNumbers(numbers) {
93
+ return [...new Set(numbers)];
94
+ }
95
+
96
+ getDuplicateNumbers(numbers) {
97
+ const seen = new Set();
98
+ const duplicates = new Set();
99
+ for (const num of numbers) {
100
+ if (seen.has(num)) {
101
+ duplicates.add(num);
102
+ } else {
103
+ seen.add(num);
104
+ }
105
+ }
106
+ return [...duplicates];
107
+ }
108
+
109
+ countOccurrences(numbers, target) {
110
+ return numbers.filter(num => num === target).length;
111
+ }
112
+
113
+ getNumbersInRange(numbers, min, max) {
114
+ return numbers.filter(num => num >= min && num <= max);
115
+ }
116
+
117
+ clearCache() {
118
+ this.cache.clear();
119
+ }
120
+
121
+ getCacheSize() {
122
+ return this.cache.size;
123
+ }
124
+ }
125
+
126
+ // This file has between 70-100 lines and should trigger a max-lines warning
127
+ export default ExtendedNumberUtils;
@@ -0,0 +1,144 @@
1
+ // Invalid: TypeScript file with way more than 100 lines (should trigger max-lines error)
2
+ interface User { id: number; name: string; email: string; }
3
+ interface Product { id: number; title: string; price: number; }
4
+ interface Order { id: number; userId: number; products: Product[]; }
5
+
6
+ const user1: User = { id: 1, name: 'John', email: 'john@example.com' };
7
+ const user2: User = { id: 2, name: 'Jane', email: 'jane@example.com' };
8
+ const user3: User = { id: 3, name: 'Bob', email: 'bob@example.com' };
9
+ const user4: User = { id: 4, name: 'Alice', email: 'alice@example.com' };
10
+ const user5: User = { id: 5, name: 'Charlie', email: 'charlie@example.com' };
11
+ const user6: User = { id: 6, name: 'Diana', email: 'diana@example.com' };
12
+ const user7: User = { id: 7, name: 'Eve', email: 'eve@example.com' };
13
+ const user8: User = { id: 8, name: 'Frank', email: 'frank@example.com' };
14
+ const user9: User = { id: 9, name: 'Grace', email: 'grace@example.com' };
15
+ const user10: User = { id: 10, name: 'Henry', email: 'henry@example.com' };
16
+
17
+ const product1: Product = { id: 1, title: 'Laptop', price: 999 };
18
+ const product2: Product = { id: 2, title: 'Mouse', price: 25 };
19
+ const product3: Product = { id: 3, title: 'Keyboard', price: 75 };
20
+ const product4: Product = { id: 4, title: 'Monitor', price: 300 };
21
+ const product5: Product = { id: 5, title: 'Headphones', price: 150 };
22
+ const product6: Product = { id: 6, title: 'Webcam', price: 80 };
23
+ const product7: Product = { id: 7, title: 'Speakers', price: 120 };
24
+ const product8: Product = { id: 8, title: 'Microphone', price: 200 };
25
+ const product9: Product = { id: 9, title: 'Tablet', price: 500 };
26
+ const product10: Product = { id: 10, title: 'Phone', price: 700 };
27
+ const product11: Product = { id: 11, title: 'Charger', price: 30 };
28
+ const product12: Product = { id: 12, title: 'Cable', price: 15 };
29
+ const product13: Product = { id: 13, title: 'Case', price: 40 };
30
+ const product14: Product = { id: 14, title: 'Stand', price: 60 };
31
+ const product15: Product = { id: 15, title: 'Bag', price: 90 };
32
+
33
+ const order1: Order = { id: 1, userId: 1, products: [product1, product2] };
34
+ const order2: Order = { id: 2, userId: 2, products: [product3] };
35
+ const order3: Order = { id: 3, userId: 3, products: [product4, product5, product6] };
36
+ const order4: Order = { id: 4, userId: 4, products: [product7, product8] };
37
+ const order5: Order = { id: 5, userId: 5, products: [product9] };
38
+ const order6: Order = { id: 6, userId: 6, products: [product10, product11] };
39
+ const order7: Order = { id: 7, userId: 7, products: [product12, product13, product14] };
40
+ const order8: Order = { id: 8, userId: 8, products: [product15] };
41
+ const order9: Order = { id: 9, userId: 9, products: [product1, product3, product5] };
42
+ const order10: Order = { id: 10, userId: 10, products: [product2, product4] };
43
+
44
+ export class OrderManagementService {
45
+ private users = [user1, user2, user3, user4, user5, user6, user7, user8, user9, user10];
46
+ private products = [product1, product2, product3, product4, product5, product6, product7, product8, product9, product10, product11, product12, product13, product14, product15];
47
+ private orders = [order1, order2, order3, order4, order5, order6, order7, order8, order9, order10];
48
+
49
+ getUserById(id: number): User | undefined {
50
+ return this.users.find(user => user.id === id);
51
+ }
52
+
53
+ getProductById(id: number): Product | undefined {
54
+ return this.products.find(product => product.id === id);
55
+ }
56
+
57
+ getOrderById(id: number): Order | undefined {
58
+ return this.orders.find(order => order.id === id);
59
+ }
60
+
61
+ getUserOrders(userId: number): Order[] {
62
+ return this.orders.filter(order => order.userId === userId);
63
+ }
64
+
65
+ calculateOrderTotal(order: Order): number {
66
+ return order.products.reduce((total, product) => total + product.price, 0);
67
+ }
68
+
69
+ getExpensiveProducts(minPrice: number): Product[] {
70
+ return this.products.filter(product => product.price >= minPrice);
71
+ }
72
+
73
+ getCheapProducts(maxPrice: number): Product[] {
74
+ return this.products.filter(product => product.price <= maxPrice);
75
+ }
76
+
77
+ searchProductsByTitle(query: string): Product[] {
78
+ return this.products.filter(product => product.title.toLowerCase().includes(query.toLowerCase()));
79
+ }
80
+
81
+ getUsersWithOrders(): User[] {
82
+ const userIdsWithOrders = new Set(this.orders.map(order => order.userId));
83
+ return this.users.filter(user => userIdsWithOrders.has(user.id));
84
+ }
85
+
86
+ getMostExpensiveOrder(): Order | undefined {
87
+ let maxOrder: Order | undefined;
88
+ let maxTotal = 0;
89
+
90
+ for (const order of this.orders) {
91
+ const total = this.calculateOrderTotal(order);
92
+ if (total > maxTotal) {
93
+ maxTotal = total;
94
+ maxOrder = order;
95
+ }
96
+ }
97
+
98
+ return maxOrder;
99
+ }
100
+
101
+ getOrdersByPriceRange(minPrice: number, maxPrice: number): Order[] {
102
+ return this.orders.filter(order => {
103
+ const total = this.calculateOrderTotal(order);
104
+ return total >= minPrice && total <= maxPrice;
105
+ });
106
+ }
107
+
108
+ getPopularProducts(): Product[] {
109
+ const productCount = new Map<number, number>();
110
+
111
+ for (const order of this.orders) {
112
+ for (const product of order.products) {
113
+ productCount.set(product.id, (productCount.get(product.id) || 0) + 1);
114
+ }
115
+ }
116
+
117
+ return this.products.filter(product => (productCount.get(product.id) || 0) > 1).sort((a, b) => (productCount.get(b.id) || 0) - (productCount.get(a.id) || 0));
118
+ }
119
+
120
+ getAverageOrderValue(): number {
121
+ const totalValue = this.orders.reduce((sum, order) => sum + this.calculateOrderTotal(order), 0);
122
+ return totalValue / this.orders.length;
123
+ }
124
+
125
+ getUserSpending(userId: number): number {
126
+ const userOrders = this.getUserOrders(userId);
127
+ return userOrders.reduce((total, order) => total + this.calculateOrderTotal(order), 0);
128
+ }
129
+
130
+ getTopSpendingUsers(limit: number = 5): Array<{ user: User; totalSpent: number }> {
131
+ const userSpending = this.users.map(user => ({
132
+ user,
133
+ totalSpent: this.getUserSpending(user.id)
134
+ }));
135
+
136
+ return userSpending.sort((a, b) => b.totalSpent - a.totalSpent).slice(0, limit);
137
+ }
138
+ }
139
+
140
+ const data1 = 'extra'; const data2 = 'data'; const data3 = 'to'; const data4 = 'make'; const data5 = 'file';
141
+ const data6 = 'longer'; const data7 = 'than'; const data8 = '100'; const data9 = 'lines'; const data10 = 'for';
142
+ const data11 = 'testing'; const data12 = 'max'; const data13 = 'lines'; const data14 = 'rule'; const data15 = 'error';
143
+
144
+ // This file has way more than 100 lines and should trigger a max-lines error
@@ -0,0 +1,66 @@
1
+ // Valid: Short file under 70 lines (no warnings or errors)
2
+ // This file demonstrates proper file length management
3
+
4
+ export class NumberUtils {
5
+ static calculateSum(numbers) {
6
+ return numbers.reduce((sum, num) => sum + num, 0);
7
+ }
8
+
9
+ static calculateAverage(numbers) {
10
+ if (numbers.length === 0) return 0;
11
+ return this.calculateSum(numbers) / numbers.length;
12
+ }
13
+
14
+ static findMaxValue(numbers) {
15
+ if (numbers.length === 0) return null;
16
+ return Math.max(...numbers);
17
+ }
18
+
19
+ static findMinValue(numbers) {
20
+ if (numbers.length === 0) return null;
21
+ return Math.min(...numbers);
22
+ }
23
+
24
+ static sortNumbers(numbers) {
25
+ return [...numbers].sort((a, b) => a - b);
26
+ }
27
+
28
+ static filterEvenNumbers(numbers) {
29
+ return numbers.filter((num) => num % 2 === 0);
30
+ }
31
+
32
+ static filterOddNumbers(numbers) {
33
+ return numbers.filter((num) => num % 2 !== 0);
34
+ }
35
+
36
+ static multiplyByTwo(numbers) {
37
+ return numbers.map((num) => num * 2);
38
+ }
39
+
40
+ static containsNumber(numbers, target) {
41
+ return numbers.includes(target);
42
+ }
43
+
44
+ static removeNumber(numbers, target) {
45
+ return numbers.filter((num) => num !== target);
46
+ }
47
+
48
+ static addNumber(numbers, newNumber) {
49
+ return [...numbers, newNumber];
50
+ }
51
+
52
+ static getUniqueNumbers(numbers) {
53
+ return [...new Set(numbers)];
54
+ }
55
+
56
+ static reverseNumbers(numbers) {
57
+ return [...numbers].reverse();
58
+ }
59
+
60
+ static getNumbersInRange(numbers, min, max) {
61
+ return numbers.filter((num) => num >= min && num <= max);
62
+ }
63
+ }
64
+
65
+ // This file has less than 70 lines and should not trigger any max-lines warnings or errors
66
+ export default NumberUtils;
@@ -0,0 +1,44 @@
1
+ // Valid: Spec file that ignores max-lines rule regardless of length
2
+ describe('Long spec file test', () => {
3
+ const testData1 = 'data1'; const testData2 = 'data2'; const testData3 = 'data3';
4
+ const testData4 = 'data4'; const testData5 = 'data5'; const testData6 = 'data6';
5
+ const testData7 = 'data7'; const testData8 = 'data8'; const testData9 = 'data9';
6
+ const testData10 = 'data10'; const testData11 = 'data11'; const testData12 = 'data12';
7
+ const testData13 = 'data13'; const testData14 = 'data14'; const testData15 = 'data15';
8
+ const testData16 = 'data16'; const testData17 = 'data17'; const testData18 = 'data18';
9
+ const testData19 = 'data19'; const testData20 = 'data20'; const testData21 = 'data21';
10
+ const testData22 = 'data22'; const testData23 = 'data23'; const testData24 = 'data24';
11
+ const testData25 = 'data25'; const testData26 = 'data26'; const testData27 = 'data27';
12
+ const testData28 = 'data28'; const testData29 = 'data29'; const testData30 = 'data30';
13
+ const testData31 = 'data31'; const testData32 = 'data32'; const testData33 = 'data33';
14
+ const testData34 = 'data34'; const testData35 = 'data35'; const testData36 = 'data36';
15
+ const testData37 = 'data37'; const testData38 = 'data38'; const testData39 = 'data39';
16
+ const testData40 = 'data40'; const testData41 = 'data41'; const testData42 = 'data42';
17
+ const testData43 = 'data43'; const testData44 = 'data44'; const testData45 = 'data45';
18
+ const testData46 = 'data46'; const testData47 = 'data47'; const testData48 = 'data48';
19
+ const testData49 = 'data49'; const testData50 = 'data50'; const testData51 = 'data51';
20
+ const testData52 = 'data52'; const testData53 = 'data53'; const testData54 = 'data54';
21
+ const testData55 = 'data55'; const testData56 = 'data56'; const testData57 = 'data57';
22
+ const testData58 = 'data58'; const testData59 = 'data59'; const testData60 = 'data60';
23
+ const testData61 = 'data61'; const testData62 = 'data62'; const testData63 = 'data63';
24
+ const testData64 = 'data64'; const testData65 = 'data65'; const testData66 = 'data66';
25
+ const testData67 = 'data67'; const testData68 = 'data68'; const testData69 = 'data69';
26
+ const testData70 = 'data70'; const testData71 = 'data71'; const testData72 = 'data72';
27
+ const testData73 = 'data73'; const testData74 = 'data74'; const testData75 = 'data75';
28
+ const testData76 = 'data76'; const testData77 = 'data77'; const testData78 = 'data78';
29
+ const testData79 = 'data79'; const testData80 = 'data80'; const testData81 = 'data81';
30
+ const testData82 = 'data82'; const testData83 = 'data83'; const testData84 = 'data84';
31
+ const testData85 = 'data85'; const testData86 = 'data86'; const testData87 = 'data87';
32
+ const testData88 = 'data88'; const testData89 = 'data89'; const testData90 = 'data90';
33
+ const testData91 = 'data91'; const testData92 = 'data92'; const testData93 = 'data93';
34
+ const testData94 = 'data94'; const testData95 = 'data95'; const testData96 = 'data96';
35
+ const testData97 = 'data97'; const testData98 = 'data98'; const testData99 = 'data99';
36
+ const testData100 = 'data100'; const testData101 = 'data101'; const testData102 = 'data102';
37
+ const testData103 = 'data103'; const testData104 = 'data104'; const testData105 = 'data105';
38
+ const testData106 = 'data106'; const testData107 = 'data107'; const testData108 = 'data108';
39
+ const testData109 = 'data109'; const testData110 = 'data110'; const testData111 = 'data111';
40
+
41
+ it('should handle long spec files without max-lines errors', () => {
42
+ expect(testData1).toBe('data1');
43
+ });
44
+ });
@@ -0,0 +1,25 @@
1
+ // Valid: Test file that ignores max-lines rule regardless of length
2
+ const line1 = 1; const line2 = 2; const line3 = 3; const line4 = 4; const line5 = 5;
3
+ const line6 = 6; const line7 = 7; const line8 = 8; const line9 = 9; const line10 = 10;
4
+ const line11 = 11; const line12 = 12; const line13 = 13; const line14 = 14; const line15 = 15;
5
+ const line16 = 16; const line17 = 17; const line18 = 18; const line19 = 19; const line20 = 20;
6
+ const line21 = 21; const line22 = 22; const line23 = 23; const line24 = 24; const line25 = 25;
7
+ const line26 = 26; const line27 = 27; const line28 = 28; const line29 = 29; const line30 = 30;
8
+ const line31 = 31; const line32 = 32; const line33 = 33; const line34 = 34; const line35 = 35;
9
+ const line36 = 36; const line37 = 37; const line38 = 38; const line39 = 39; const line40 = 40;
10
+ const line41 = 41; const line42 = 42; const line43 = 43; const line44 = 44; const line45 = 45;
11
+ const line46 = 46; const line47 = 47; const line48 = 48; const line49 = 49; const line50 = 50;
12
+ const line51 = 51; const line52 = 52; const line53 = 53; const line54 = 54; const line55 = 55;
13
+ const line56 = 56; const line57 = 57; const line58 = 58; const line59 = 59; const line60 = 60;
14
+ const line61 = 61; const line62 = 62; const line63 = 63; const line64 = 64; const line65 = 65;
15
+ const line66 = 66; const line67 = 67; const line68 = 68; const line69 = 69; const line70 = 70;
16
+ const line71 = 71; const line72 = 72; const line73 = 73; const line74 = 74; const line75 = 75;
17
+ const line76 = 76; const line77 = 77; const line78 = 78; const line79 = 79; const line80 = 80;
18
+ const line81 = 81; const line82 = 82; const line83 = 83; const line84 = 84; const line85 = 85;
19
+ const line86 = 86; const line87 = 87; const line88 = 88; const line89 = 89; const line90 = 90;
20
+ const line91 = 91; const line92 = 92; const line93 = 93; const line94 = 94; const line95 = 95;
21
+ const line96 = 96; const line97 = 97; const line98 = 98; const line99 = 99; const line100 = 100;
22
+ const line101 = 101; const line102 = 102; const line103 = 103; const line104 = 104; const line105 = 105;
23
+ const line106 = 106; const line107 = 107; const line108 = 108; const line109 = 109; const line110 = 110;
24
+
25
+ export default line1;
@@ -0,0 +1,8 @@
1
+ // Invalid: Mix of direct exports and separate export statements
2
+ export const first = 'direct export';
3
+
4
+ const second = 'separate export';
5
+ const third = 'another separate export';
6
+
7
+ export { second };
8
+ export { third };
@@ -0,0 +1,8 @@
1
+ // Invalid: Multiple separate export statements should be consolidated
2
+ const apiUrl = "https://api.example.com";
3
+ const timeout = 5000;
4
+ const retries = 3;
5
+
6
+ export { apiUrl };
7
+ export { timeout };
8
+ export { retries };
@@ -0,0 +1,10 @@
1
+ // Invalid: Named exports scattered across multiple statements
2
+ const config = { api: 'v1' };
3
+ const utils = { format: () => {} };
4
+
5
+ export { config };
6
+
7
+ const helpers = { validate: () => {} };
8
+
9
+ export { utils };
10
+ export { helpers };
@@ -0,0 +1,2 @@
1
+ // Valid: Single direct export at declaration point
2
+ export const apiUrl = "https://api.example.com";
@@ -0,0 +1,7 @@
1
+ // Valid: Default export with grouped named exports
2
+ const mainConfig = { theme: 'dark' };
3
+ const helper1 = () => 'helper';
4
+ const helper2 = () => 'another helper';
5
+
6
+ export default mainConfig;
7
+ export { helper1, helper2 };
@@ -0,0 +1,6 @@
1
+ // Valid: All named exports consolidated into a single export statement
2
+ const apiUrl = "https://api.example.com";
3
+ const timeout = 5000;
4
+ const retries = 3;
5
+
6
+ export { apiUrl, timeout, retries };
@@ -0,0 +1,3 @@
1
+ export const groupExportsConfig = {
2
+ "import/group-exports": "error", // Enforce consolidating exports into single statements
3
+ };
@@ -1,8 +1,12 @@
1
+ import { noUnusedModulesConfig } from "./no-unused-modules/index.js";
2
+ import { groupExportsConfig } from "./group-exports/index.js";
3
+
1
4
  export const importRules = {
2
5
  // Import/export organization and restrictions
3
- "import/group-exports": "error", // Enforce consolidating exports into single statements
6
+ ...groupExportsConfig,
4
7
  "import/no-default-export": "off", // Allow default exports
5
8
  "import/no-namespace": "error",
9
+ ...noUnusedModulesConfig,
6
10
  // Disabled import rules (keep existing behavior)
7
11
  "import/extensions": ["off"],
8
12
  "import/no-extraneous-dependencies": ["off"],
@@ -0,0 +1,2 @@
1
+ // Invalid: This export is never imported (while others in the project might be used)
2
+ export const unusedExport = "This is never imported";
@@ -0,0 +1,6 @@
1
+ // Invalid: This class export is never imported by any other module
2
+ export class UnusedClass {
3
+ method() {
4
+ return "This class is never instantiated";
5
+ }
6
+ }
@@ -0,0 +1,2 @@
1
+ // Invalid: This export is never imported by any other module
2
+ export const unusedConstant = "This value is never used";
@@ -0,0 +1,4 @@
1
+ // Invalid: This function export is never imported by any other module
2
+ export function unusedFunction() {
3
+ return "This function is never called";
4
+ }
@@ -0,0 +1,4 @@
1
+ // Invalid: This entire module is never imported anywhere
2
+ const internalData = [1, 2, 3];
3
+
4
+ export const result = internalData.map(x => x * 2);
@@ -0,0 +1,8 @@
1
+ // Valid: This module consumes exports from other modules
2
+ import { apiUrl } from './used-export.js';
3
+ import { fetchData } from './used-function.js';
4
+ import { DataProcessor } from './used-class.js';
5
+
6
+ const processor = new DataProcessor();
7
+ fetchData().then(data => processor.process(data));
8
+ console.log(`API URL: ${apiUrl}`);
@@ -0,0 +1,5 @@
1
+ // Valid: Entry point that imports and uses modules
2
+ import './consumed-import.js';
3
+
4
+ // Entry points are typically excluded from unused module detection
5
+ console.log('Application started');
@@ -0,0 +1,6 @@
1
+ // Valid: Class export is used by other modules
2
+ export class DataProcessor {
3
+ process(data) {
4
+ return data.map(item => item.value);
5
+ }
6
+ }
@@ -0,0 +1,2 @@
1
+ // Valid: Export is used by other modules
2
+ export const apiUrl = "https://api.example.com";
@@ -0,0 +1,4 @@
1
+ // Valid: Function export is used by other modules
2
+ export function fetchData() {
3
+ return fetch("https://api.example.com");
4
+ }
@@ -0,0 +1,6 @@
1
+ // Note: import/no-unused-modules has compatibility issues with ESLint flat config
2
+ // It requires an .eslintrc file and has complex setup requirements
3
+ // For now, this rule is disabled to avoid configuration conflicts
4
+ export const noUnusedModulesConfig = {
5
+ // "import/no-unused-modules": "off", // Disabled due to flat config compatibility issues
6
+ };
@@ -1,4 +1,6 @@
1
+ import { noExplicitAnyConfig } from "./no-explicit-any/index.js";
2
+
1
3
  export const typescriptEslintRules = {
2
- "@typescript-eslint/no-explicit-any": "error",
4
+ ...noExplicitAnyConfig,
3
5
  "@typescript-eslint/consistent-type-assertions": "off",
4
6
  };
@@ -0,0 +1,2 @@
1
+ // Invalid: Using any in array type
2
+ export const items: any[] = ["hello", 42, true, { name: "test" }];
@@ -0,0 +1,4 @@
1
+ // Invalid: Using any type for function parameter
2
+ export function processData(data: any): number {
3
+ return data.length;
4
+ }
@@ -0,0 +1,4 @@
1
+ // Invalid: Using any as return type
2
+ export function getData(): any {
3
+ return { message: "Hello world" };
4
+ }
@@ -0,0 +1,5 @@
1
+ // Invalid: Using any type for variable declaration
2
+ export const config: any = {
3
+ apiUrl: "https://api.example.com",
4
+ timeout: 5000,
5
+ };
@@ -0,0 +1,16 @@
1
+ // Valid: Using generics instead of any
2
+ export function identity<T>(value: T): T {
3
+ return value;
4
+ }
5
+
6
+ export class Container<T> {
7
+ private value: T;
8
+
9
+ constructor(value: T) {
10
+ this.value = value;
11
+ }
12
+
13
+ getValue(): T {
14
+ return this.value;
15
+ }
16
+ }
@@ -0,0 +1,9 @@
1
+ // Valid: Using specific types instead of any
2
+ export function processData(data: string[]): number {
3
+ return data.length;
4
+ }
5
+
6
+ export const config: { apiUrl: string; timeout: number } = {
7
+ apiUrl: "https://api.example.com",
8
+ timeout: 5000,
9
+ };
@@ -0,0 +1,8 @@
1
+ // Valid: Using unknown instead of any for safer type handling
2
+ export function parseJson(json: string): unknown {
3
+ return JSON.parse(json);
4
+ }
5
+
6
+ export function isString(value: unknown): value is string {
7
+ return typeof value === "string";
8
+ }
@@ -0,0 +1,3 @@
1
+ export const noExplicitAnyConfig = {
2
+ "@typescript-eslint/no-explicit-any": "error", // Disallow usage of the any type
3
+ };
package/rules/README.md DELETED
@@ -1,42 +0,0 @@
1
- # ESLint Rules Configuration
2
-
3
- This directory contains custom rule configurations and their tests using ESLint's RuleTester.
4
-
5
- ## Structure
6
-
7
- Each rule has its own directory with:
8
- - `index.js` - The rule configuration (severity and options)
9
- - `[rule-name].spec.js` - RuleTester test cases
10
-
11
- ## Available Rules
12
-
13
- ### no-trailing-spaces
14
- - **Severity**: error
15
- - **Description**: Disallows trailing whitespace at the end of lines
16
- - **Options**:
17
- - `skipBlankLines: false` - Check empty lines for trailing spaces
18
- - `ignoreComments: false` - Check comments for trailing spaces
19
-
20
- ## Testing
21
-
22
- Run individual rule tests:
23
- ```bash
24
- cd rules/no-trailing-spaces
25
- node no-trailing-spaces.spec.js
26
- ```
27
-
28
- ## Adding New Rules
29
-
30
- 1. Create a new directory: `rules/[rule-name]/`
31
- 2. Add `index.js` with the rule configuration
32
- 3. Add `[rule-name].spec.js` with RuleTester tests
33
- 4. Import the configuration in the main `index.js` file
34
-
35
- Example structure:
36
- ```javascript
37
- // rules/[rule-name]/index.js
38
- export const rule = "error"; // or "warn"
39
- export const options = { /* rule options */ };
40
- export const [ruleName]Config = [rule, options];
41
- export default [ruleName]Config;
42
- ```