eslint-plugin-functype 1.4.0 → 2.0.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 (55) hide show
  1. package/dist/cli/list-rules.d.ts +1 -1
  2. package/dist/cli/list-rules.js +15 -239
  3. package/dist/cli/list-rules.js.map +1 -1
  4. package/dist/configs/recommended.d.ts +15 -0
  5. package/dist/configs/recommended.js +2 -0
  6. package/dist/configs/recommended.js.map +1 -0
  7. package/dist/configs/strict.d.ts +15 -0
  8. package/dist/configs/strict.js +2 -0
  9. package/dist/configs/strict.js.map +1 -0
  10. package/dist/dependency-validator-BBxa9-7D.js +4 -0
  11. package/dist/dependency-validator-BBxa9-7D.js.map +1 -0
  12. package/dist/index.d.ts +20 -17
  13. package/dist/index.js +1 -1364
  14. package/dist/index.js.map +1 -1
  15. package/dist/rules/index.d.ts +24 -32
  16. package/dist/rules/index.js +1 -1357
  17. package/dist/rules/index.js.map +1 -1
  18. package/dist/rules/no-get-unsafe.d.ts +7 -0
  19. package/dist/rules/no-get-unsafe.js +2 -0
  20. package/dist/rules/no-get-unsafe.js.map +1 -0
  21. package/dist/rules/no-imperative-loops.d.ts +7 -0
  22. package/dist/rules/no-imperative-loops.js +2 -0
  23. package/dist/rules/no-imperative-loops.js.map +1 -0
  24. package/dist/rules/prefer-do-notation.d.ts +7 -0
  25. package/dist/rules/prefer-do-notation.js +5 -0
  26. package/dist/rules/prefer-do-notation.js.map +1 -0
  27. package/dist/rules/prefer-either.d.ts +7 -0
  28. package/dist/rules/prefer-either.js +2 -0
  29. package/dist/rules/prefer-either.js.map +1 -0
  30. package/dist/rules/prefer-flatmap.d.ts +7 -0
  31. package/dist/rules/prefer-flatmap.js +2 -0
  32. package/dist/rules/prefer-flatmap.js.map +1 -0
  33. package/dist/rules/prefer-fold.d.ts +7 -0
  34. package/dist/rules/prefer-fold.js +2 -0
  35. package/dist/rules/prefer-fold.js.map +1 -0
  36. package/dist/rules/prefer-list.d.ts +7 -0
  37. package/dist/rules/prefer-list.js +2 -0
  38. package/dist/rules/prefer-list.js.map +1 -0
  39. package/dist/rules/prefer-map.d.ts +7 -0
  40. package/dist/rules/prefer-map.js +2 -0
  41. package/dist/rules/prefer-map.js.map +1 -0
  42. package/dist/rules/prefer-option.d.ts +7 -0
  43. package/dist/rules/prefer-option.js +2 -0
  44. package/dist/rules/prefer-option.js.map +1 -0
  45. package/dist/types/ast.d.ts +12 -0
  46. package/dist/types/ast.js +1 -0
  47. package/dist/utils/dependency-validator.d.ts +13 -11
  48. package/dist/utils/dependency-validator.js +1 -109
  49. package/dist/utils/functype-detection.d.ts +69 -0
  50. package/dist/utils/functype-detection.js +2 -0
  51. package/dist/utils/functype-detection.js.map +1 -0
  52. package/package.json +48 -52
  53. package/LICENSE +0 -21
  54. package/README.md +0 -325
  55. package/dist/utils/dependency-validator.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,1365 +1,2 @@
1
- 'use strict';
2
-
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
- };
12
- var __commonJS = (cb, mod) => function __require() {
13
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
14
- };
15
- var __copyProps = (to, from, except, desc) => {
16
- if (from && typeof from === "object" || typeof from === "function") {
17
- for (let key of __getOwnPropNames(from))
18
- if (!__hasOwnProp.call(to, key) && key !== except)
19
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
- }
21
- return to;
22
- };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
- // If the importer is in node compatibility mode or this is not an ESM
25
- // file that has been converted to a CommonJS file using a Babel-
26
- // compatible transform (i.e. "__esModule" has not been set), then set
27
- // "default" to the CommonJS "module.exports" for node compatibility.
28
- !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
- mod
30
- ));
31
-
32
- // src/utils/functype-detection.ts
33
- function getFunctypeImports(context) {
34
- const types = /* @__PURE__ */ new Set();
35
- const functions = /* @__PURE__ */ new Set();
36
- const all = /* @__PURE__ */ new Set();
37
- const sourceCode = context.sourceCode;
38
- const program = sourceCode.ast;
39
- for (const node of program.body) {
40
- if (node.type === "ImportDeclaration" && node.source.type === "Literal" && node.source.value === "functype") {
41
- if (node.specifiers) {
42
- for (const spec of node.specifiers) {
43
- if (spec.type === "ImportSpecifier" && spec.imported.type === "Identifier") {
44
- const name = spec.imported.name;
45
- all.add(name);
46
- if (["Option", "Either", "List", "LazyList", "Task", "Try"].includes(name)) {
47
- types.add(name);
48
- } else if (["Do", "DoAsync", "$"].includes(name)) {
49
- functions.add(name);
50
- } else {
51
- functions.add(name);
52
- }
53
- } else if (spec.type === "ImportDefaultSpecifier") {
54
- all.add("default");
55
- functions.add("default");
56
- } else if (spec.type === "ImportNamespaceSpecifier") {
57
- all.add("*");
58
- types.add("*");
59
- functions.add("*");
60
- }
61
- }
62
- }
63
- }
64
- }
65
- return { types, functions, all };
66
- }
67
- function getFunctypeImportsLegacy(context) {
68
- return getFunctypeImports(context).all;
69
- }
70
- function isFunctypeType(node, functypeImports) {
71
- if (!node) return false;
72
- const types = functypeImports instanceof Set ? functypeImports : functypeImports.types || functypeImports.all;
73
- if (node.type === "TSTypeReference" && node.typeName?.type === "Identifier") {
74
- const typeName = node.typeName.name;
75
- return types.has(typeName) || ["Option", "Either", "List", "LazyList", "Task", "Try"].includes(typeName);
76
- }
77
- return false;
78
- }
79
- function isFunctypeCall(node, functypeImports) {
80
- if (!node || node.type !== "CallExpression") return false;
81
- const callee = node.callee;
82
- if (callee.type === "MemberExpression" && callee.object.type === "Identifier") {
83
- const objectName = callee.object.name;
84
- const methodName = callee.property?.name;
85
- if (functypeImports.has(objectName)) return true;
86
- if (objectName === "Option" && ["some", "none", "of"].includes(methodName) || objectName === "Either" && ["left", "right", "of"].includes(methodName) || objectName === "List" && ["of", "from", "empty"].includes(methodName)) {
87
- return true;
88
- }
89
- }
90
- if (callee.type === "MemberExpression") {
91
- const methodName = callee.property?.name;
92
- if ([
93
- "map",
94
- "flatMap",
95
- "filter",
96
- "fold",
97
- "foldLeft",
98
- "foldRight",
99
- "getOrElse",
100
- "orElse",
101
- "isEmpty",
102
- "nonEmpty",
103
- "isDefined",
104
- "isSome",
105
- "isNone",
106
- "isLeft",
107
- "isRight",
108
- "toArray"
109
- ].includes(methodName)) {
110
- return true;
111
- }
112
- }
113
- return false;
114
- }
115
- function isAlreadyUsingFunctype(node, functypeImports) {
116
- let parent = node.parent;
117
- while (parent) {
118
- if (isFunctypeCall(parent, functypeImports) || isFunctypeType(parent, functypeImports)) {
119
- return true;
120
- }
121
- parent = parent.parent;
122
- }
123
- return false;
124
- }
125
- function isChainedMethodCall(node, methodName) {
126
- if (!node || node.type !== "CallExpression") return false;
127
- const callee = node.callee;
128
- if (callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === methodName) {
129
- return true;
130
- }
131
- return false;
132
- }
133
- var init_functype_detection = __esm({
134
- "src/utils/functype-detection.ts"() {
135
- }
136
- });
137
-
138
- // src/rules/prefer-option.ts
139
- var require_prefer_option = __commonJS({
140
- "src/rules/prefer-option.ts"(exports, module) {
141
- init_functype_detection();
142
- var rule2 = {
143
- meta: {
144
- type: "suggestion",
145
- docs: {
146
- description: "Prefer Option<T> over nullable types (T | null | undefined)",
147
- category: "Stylistic Issues",
148
- recommended: true
149
- },
150
- schema: [
151
- {
152
- type: "object",
153
- properties: {
154
- allowNullableIntersections: {
155
- type: "boolean",
156
- default: false
157
- }
158
- },
159
- additionalProperties: false
160
- }
161
- ],
162
- messages: {
163
- preferOption: "Prefer Option<{{type}}> over nullable type '{{nullable}}'",
164
- preferOptionReturn: "Prefer Option<{{type}}> as return type over nullable '{{nullable}}'"
165
- }
166
- },
167
- create(context) {
168
- const functypeImports = getFunctypeImportsLegacy(context);
169
- return {
170
- TSUnionType(node) {
171
- if (!node.types || node.types.length < 2) return;
172
- const hasNull = node.types.some(
173
- (type) => type.type === "TSNullKeyword" || type.type === "TSUndefinedKeyword"
174
- );
175
- if (!hasNull) return;
176
- const nonNullTypes = node.types.filter(
177
- (type) => type.type !== "TSNullKeyword" && type.type !== "TSUndefinedKeyword"
178
- );
179
- if (nonNullTypes.length === 1) {
180
- const nonNullType = nonNullTypes[0];
181
- if (isFunctypeType(nonNullType, functypeImports)) return;
182
- if (isAlreadyUsingFunctype(node, functypeImports)) return;
183
- const sourceCode = context.sourceCode;
184
- const nonNullTypeText = sourceCode.getText(nonNullType);
185
- const fullType = sourceCode.getText(node);
186
- if (nonNullTypeText.startsWith("Option<")) return;
187
- context.report({
188
- node,
189
- messageId: "preferOption",
190
- data: {
191
- type: nonNullTypeText,
192
- nullable: fullType
193
- }
194
- });
195
- }
196
- }
197
- };
198
- }
199
- };
200
- module.exports = rule2;
201
- }
202
- });
203
-
204
- // src/rules/prefer-either.ts
205
- var require_prefer_either = __commonJS({
206
- "src/rules/prefer-either.ts"(exports, module) {
207
- var rule2 = {
208
- meta: {
209
- type: "suggestion",
210
- docs: {
211
- description: "Prefer Either<E, T> over try/catch blocks and throw statements",
212
- category: "Best Practices",
213
- recommended: true
214
- },
215
- schema: [
216
- {
217
- type: "object",
218
- properties: {
219
- allowThrowInTests: {
220
- type: "boolean",
221
- default: true
222
- }
223
- },
224
- additionalProperties: false
225
- }
226
- ],
227
- messages: {
228
- preferEitherOverTryCatch: "Prefer Either<Error, T> over try/catch block",
229
- preferEitherOverThrow: "Prefer Either.left(error) over throw statement",
230
- preferEitherReturn: "Consider returning Either<Error, {{type}}> instead of throwing"
231
- }
232
- },
233
- create(context) {
234
- const options = context.options[0] || {};
235
- const allowThrowInTests = options.allowThrowInTests !== false;
236
- function isInTestFile() {
237
- const filename = context.getFilename();
238
- return /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(filename) || filename.includes("__tests__") || filename.includes("/test/") || filename.includes("/tests/");
239
- }
240
- function hasThrowStatementsOutsideCatch(node) {
241
- if (!node) return false;
242
- if (node.type === "ThrowStatement") {
243
- let parent = node.parent;
244
- while (parent) {
245
- if (parent.type === "CatchClause") return false;
246
- parent = parent.parent;
247
- }
248
- return true;
249
- }
250
- if (node.type === "CatchClause") return false;
251
- for (const key in node) {
252
- if (key === "parent") continue;
253
- const child = node[key];
254
- if (Array.isArray(child)) {
255
- for (const item of child) {
256
- if (item && typeof item === "object" && hasThrowStatementsOutsideCatch(item)) {
257
- return true;
258
- }
259
- }
260
- } else if (child && typeof child === "object" && hasThrowStatementsOutsideCatch(child)) {
261
- return true;
262
- }
263
- }
264
- return false;
265
- }
266
- return {
267
- TryStatement(node) {
268
- if (allowThrowInTests && isInTestFile()) return;
269
- if (node.handler && node.handler.body) {
270
- const catchBody = node.handler.body.body;
271
- const hasRethrow = catchBody.some((stmt) => stmt.type === "ThrowStatement");
272
- if (hasRethrow) return;
273
- }
274
- context.report({
275
- node,
276
- messageId: "preferEitherOverTryCatch"
277
- });
278
- },
279
- ThrowStatement(node) {
280
- if (allowThrowInTests && isInTestFile()) return;
281
- let parent = node.parent;
282
- while (parent) {
283
- if (parent.type === "CatchClause") return;
284
- parent = parent.parent;
285
- }
286
- context.report({
287
- node,
288
- messageId: "preferEitherOverThrow"
289
- });
290
- },
291
- FunctionDeclaration(node) {
292
- if (allowThrowInTests && isInTestFile()) return;
293
- if (!node.body) return;
294
- const hasThrowsNotInCatch = hasThrowStatementsOutsideCatch(node.body);
295
- if (hasThrowsNotInCatch) {
296
- const returnType = node.returnType?.typeAnnotation;
297
- if (returnType) {
298
- const sourceCode = context.sourceCode;
299
- const returnTypeText = sourceCode.getText(returnType);
300
- if (!returnTypeText.includes("Either")) {
301
- context.report({
302
- node: node.id || node,
303
- messageId: "preferEitherReturn",
304
- data: { type: returnTypeText }
305
- });
306
- }
307
- }
308
- }
309
- }
310
- };
311
- }
312
- };
313
- module.exports = rule2;
314
- }
315
- });
316
-
317
- // src/rules/prefer-list.ts
318
- var require_prefer_list = __commonJS({
319
- "src/rules/prefer-list.ts"(exports, module) {
320
- init_functype_detection();
321
- var rule2 = {
322
- meta: {
323
- type: "suggestion",
324
- docs: {
325
- description: "Prefer List<T> over native arrays for immutable collections",
326
- category: "Stylistic Issues",
327
- recommended: true
328
- },
329
- schema: [
330
- {
331
- type: "object",
332
- properties: {
333
- allowArraysInTests: {
334
- type: "boolean",
335
- default: true
336
- },
337
- allowReadonlyArrays: {
338
- type: "boolean",
339
- default: true
340
- }
341
- },
342
- additionalProperties: false
343
- }
344
- ],
345
- messages: {
346
- preferList: "Prefer List<{{type}}> over array type {{arrayType}}",
347
- preferListLiteral: "Prefer List.of(...) or List.from([...]) over array literal"
348
- }
349
- },
350
- create(context) {
351
- const options = context.options[0] || {};
352
- const allowArraysInTests = options.allowArraysInTests !== false;
353
- const allowReadonlyArrays = options.allowReadonlyArrays !== false;
354
- const functypeImports = getFunctypeImportsLegacy(context);
355
- function isInTestFile() {
356
- const filename = context.getFilename();
357
- return /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(filename) || filename.includes("__tests__") || filename.includes("/test/") || filename.includes("/tests/");
358
- }
359
- function findTypeParameter(node, sourceCode) {
360
- function findInNode(n) {
361
- if (n.type === "TSTypeParameterInstantiation" && n.params && n.params[0]) {
362
- return sourceCode.getText(n.params[0]);
363
- }
364
- for (const key in n) {
365
- if (key === "parent") continue;
366
- const child = n[key];
367
- if (Array.isArray(child)) {
368
- for (const item of child) {
369
- if (item && typeof item === "object" && item.type) {
370
- const result = findInNode(item);
371
- if (result) return result;
372
- }
373
- }
374
- } else if (child && typeof child === "object" && child.type) {
375
- const result = findInNode(child);
376
- if (result) return result;
377
- }
378
- }
379
- return null;
380
- }
381
- return findInNode(node);
382
- }
383
- return {
384
- TSArrayType(node) {
385
- if (allowArraysInTests && isInTestFile()) return;
386
- const sourceCode = context.sourceCode;
387
- const elementType = sourceCode.getText(node.elementType);
388
- const fullType = sourceCode.getText(node);
389
- context.report({
390
- node,
391
- messageId: "preferList",
392
- data: {
393
- type: elementType,
394
- arrayType: fullType
395
- }
396
- });
397
- },
398
- TSTypeReference(node) {
399
- if (allowArraysInTests && isInTestFile()) return;
400
- const sourceCode = context.sourceCode;
401
- let typeName = "";
402
- if (node.typeName && node.typeName.type === "Identifier") {
403
- typeName = node.typeName.name;
404
- } else if (node.typeName) {
405
- typeName = sourceCode.getText(node.typeName);
406
- } else {
407
- return;
408
- }
409
- if (typeName === "Array") {
410
- const typeParam = findTypeParameter(node, sourceCode);
411
- const fullType = sourceCode.getText(node);
412
- if (allowReadonlyArrays && fullType.startsWith("readonly")) return;
413
- context.report({
414
- node,
415
- messageId: "preferList",
416
- data: {
417
- type: typeParam || "T",
418
- arrayType: fullType
419
- }
420
- });
421
- }
422
- if (typeName === "ReadonlyArray") {
423
- const typeParam = findTypeParameter(node, sourceCode);
424
- const fullType = sourceCode.getText(node);
425
- context.report({
426
- node,
427
- messageId: "preferList",
428
- data: {
429
- type: typeParam || "T",
430
- arrayType: fullType
431
- }
432
- });
433
- }
434
- },
435
- ArrayExpression(node) {
436
- if (allowArraysInTests && isInTestFile()) return;
437
- if (node.elements.length === 0) return;
438
- let parent = node.parent;
439
- if (parent && isFunctypeCall(parent, functypeImports)) {
440
- return;
441
- }
442
- if (parent && parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && parent.callee.object.type === "Identifier" && parent.callee.object.name === "List" && ["from", "of"].includes(parent.callee.property.name)) {
443
- return;
444
- }
445
- parent = node.parent;
446
- while (parent) {
447
- if (parent.type === "ArrayExpression") {
448
- return;
449
- }
450
- parent = parent.parent;
451
- }
452
- let hasTypeAnnotation = false;
453
- parent = node.parent;
454
- while (parent) {
455
- if (parent.type === "VariableDeclarator" && parent.id?.typeAnnotation) {
456
- hasTypeAnnotation = true;
457
- break;
458
- }
459
- if (parent.type === "TSTypeAnnotation") {
460
- hasTypeAnnotation = true;
461
- break;
462
- }
463
- parent = parent.parent;
464
- }
465
- if (hasTypeAnnotation) return;
466
- context.report({
467
- node,
468
- messageId: "preferListLiteral"
469
- });
470
- }
471
- };
472
- }
473
- };
474
- module.exports = rule2;
475
- }
476
- });
477
-
478
- // src/rules/no-get-unsafe.ts
479
- var require_no_get_unsafe = __commonJS({
480
- "src/rules/no-get-unsafe.ts"(exports, module) {
481
- var rule2 = {
482
- meta: {
483
- type: "problem",
484
- docs: {
485
- description: "Avoid unsafe .get() calls on Option, Either, and other monadic types",
486
- category: "Possible Errors",
487
- recommended: true
488
- },
489
- fixable: "code",
490
- schema: [
491
- {
492
- type: "object",
493
- properties: {
494
- allowInTests: {
495
- type: "boolean",
496
- default: true
497
- },
498
- unsafeMethods: {
499
- type: "array",
500
- items: { type: "string" },
501
- default: ["get", "getOrThrow", "unwrap", "expect"]
502
- }
503
- },
504
- additionalProperties: false
505
- }
506
- ],
507
- messages: {
508
- noUnsafeGet: "Avoid unsafe .{{method}}() call. Use .fold(), .map(), or .getOrElse() instead",
509
- noUnsafeGetSuggestion: "Consider using .getOrElse(defaultValue) or .fold() for safe access"
510
- }
511
- },
512
- create(context) {
513
- const options = context.options[0] || {};
514
- const allowInTests = options.allowInTests !== false;
515
- const unsafeMethods = options.unsafeMethods || ["get", "getOrThrow", "unwrap", "expect"];
516
- function isInTestFile() {
517
- const filename = context.getFilename();
518
- return /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(filename) || filename.includes("__tests__") || filename.includes("/test/") || filename.includes("/tests/");
519
- }
520
- function isMonadicType(node) {
521
- if (!node) return false;
522
- const sourceCode = context.sourceCode;
523
- const text = sourceCode.getText(node);
524
- if (/\b(Option|Either|Maybe|Result|Some|None|Left|Right)\b/.test(text)) {
525
- return true;
526
- }
527
- if (/\.(map|flatMap|filter|fold)\s*\(/.test(text)) {
528
- return true;
529
- }
530
- if (/\b(option|either|maybe|result|some|none|left|right)\w*\b/i.test(text)) {
531
- return true;
532
- }
533
- if (node.type === "Identifier") {
534
- const varName = node.name.toLowerCase();
535
- if (/(option|either|maybe|result|some|none|left|right|opt)/.test(varName)) {
536
- return true;
537
- }
538
- }
539
- if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.object) {
540
- return isMonadicType(node.callee.object);
541
- }
542
- return false;
543
- }
544
- return {
545
- CallExpression(node) {
546
- if (allowInTests && isInTestFile()) return;
547
- if (node.callee.type !== "MemberExpression") return;
548
- const property = node.callee.property;
549
- if (!property || property.type !== "Identifier") return;
550
- const methodName = property.name;
551
- if (!unsafeMethods.includes(methodName)) return;
552
- if (isMonadicType(node.callee.object)) {
553
- context.report({
554
- node,
555
- messageId: "noUnsafeGet",
556
- data: { method: methodName },
557
- fix(fixer) {
558
- const sourceCode = context.sourceCode;
559
- const objectText = sourceCode.getText(node.callee.object);
560
- let replacement;
561
- if (methodName === "get") {
562
- replacement = `${objectText}.getOrElse(/* TODO: provide default value */)`;
563
- } else if (methodName === "getOrThrow") {
564
- replacement = `${objectText}.getOrElse(/* TODO: provide default value */)`;
565
- } else if (methodName === "unwrap") {
566
- replacement = `${objectText}.getOrElse(/* TODO: provide default value */)`;
567
- } else if (methodName === "expect") {
568
- replacement = `${objectText}.getOrElse(/* TODO: provide default value */)`;
569
- } else {
570
- replacement = `${objectText}.getOrElse(/* TODO: provide default value */)`;
571
- }
572
- return fixer.replaceText(node, replacement);
573
- }
574
- });
575
- }
576
- }
577
- };
578
- }
579
- };
580
- module.exports = rule2;
581
- }
582
- });
583
-
584
- // src/rules/prefer-fold.ts
585
- var require_prefer_fold = __commonJS({
586
- "src/rules/prefer-fold.ts"(exports, module) {
587
- var rule2 = {
588
- meta: {
589
- type: "suggestion",
590
- docs: {
591
- description: "Prefer .fold() over if/else chains when working with monadic types",
592
- category: "Best Practices",
593
- recommended: true
594
- },
595
- fixable: "code",
596
- schema: [
597
- {
598
- type: "object",
599
- properties: {
600
- minComplexity: {
601
- type: "integer",
602
- minimum: 1,
603
- default: 2
604
- }
605
- },
606
- additionalProperties: false
607
- }
608
- ],
609
- messages: {
610
- preferFold: "Prefer .fold() over if/else when working with {{type}} types",
611
- preferFoldTernary: "Consider using .fold() instead of ternary operator for {{type}}"
612
- }
613
- },
614
- create(context) {
615
- const options = context.options[0] || {};
616
- const minComplexity = options.minComplexity || 2;
617
- function extractBodyForFold(node, sourceCode) {
618
- if (node.type === "BlockStatement") {
619
- const statements = node.body;
620
- if (statements.length === 1 && statements[0].type === "ReturnStatement") {
621
- return sourceCode.getText(statements[0].argument);
622
- } else {
623
- return sourceCode.getText(node).slice(1, -1).trim();
624
- }
625
- } else {
626
- return sourceCode.getText(node);
627
- }
628
- }
629
- function generateFoldFromIf(node) {
630
- const sourceCode = context.sourceCode;
631
- if (node.type !== "IfStatement") return null;
632
- const test = node.test;
633
- const consequent = node.consequent;
634
- const alternate = node.alternate;
635
- if (!consequent || !alternate) return null;
636
- let monadicObj = null;
637
- let isNegated = false;
638
- if (test.type === "CallExpression" && test.callee.type === "MemberExpression") {
639
- const methodName = test.callee.property.name;
640
- monadicObj = sourceCode.getText(test.callee.object);
641
- if (methodName === "isSome" || methodName === "isRight" || methodName === "isSuccess") {
642
- isNegated = false;
643
- } else if (methodName === "isNone" || methodName === "isEmpty" || methodName === "isLeft" || methodName === "isFailure") {
644
- isNegated = true;
645
- }
646
- }
647
- if (!monadicObj) return null;
648
- if (consequent.type === "BlockStatement") {
649
- if (consequent.body.length !== 1 || consequent.body[0].type !== "ReturnStatement") {
650
- return null;
651
- }
652
- }
653
- if (alternate.type === "BlockStatement") {
654
- if (alternate.body.length !== 1 || alternate.body[0].type !== "ReturnStatement") {
655
- return null;
656
- }
657
- } else if (alternate.type === "IfStatement") {
658
- return null;
659
- }
660
- const thenBody = extractBodyForFold(consequent, sourceCode);
661
- const elseBody = extractBodyForFold(alternate, sourceCode);
662
- if (isNegated) {
663
- return `${monadicObj}.fold(() => ${thenBody}, () => ${elseBody})`;
664
- } else {
665
- return `${monadicObj}.fold(() => ${elseBody}, (value) => ${thenBody})`;
666
- }
667
- }
668
- function generateFoldFromTernary(node) {
669
- const sourceCode = context.sourceCode;
670
- if (node.type !== "ConditionalExpression") return null;
671
- const test = node.test;
672
- const consequent = node.consequent;
673
- const alternate = node.alternate;
674
- let monadicObj = null;
675
- let isNegated = false;
676
- if (test.type === "CallExpression" && test.callee.type === "MemberExpression") {
677
- const methodName = test.callee.property.name;
678
- monadicObj = sourceCode.getText(test.callee.object);
679
- if (methodName === "isSome" || methodName === "isRight" || methodName === "isSuccess") {
680
- isNegated = false;
681
- } else if (methodName === "isNone" || methodName === "isEmpty" || methodName === "isLeft" || methodName === "isFailure") {
682
- isNegated = true;
683
- }
684
- }
685
- if (!monadicObj) return null;
686
- const thenExpr = sourceCode.getText(consequent);
687
- const elseExpr = sourceCode.getText(alternate);
688
- if (isNegated) {
689
- return `${monadicObj}.fold(() => ${thenExpr}, () => ${elseExpr})`;
690
- } else {
691
- return `${monadicObj}.fold(() => ${elseExpr}, (value) => ${thenExpr})`;
692
- }
693
- }
694
- function shouldAutoFix(node) {
695
- if (node.type === "CallExpression" && node.callee.type === "MemberExpression") {
696
- const methodName = node.callee.property.name;
697
- return ["isSome", "isNone", "isEmpty", "isRight", "isLeft", "isSuccess", "isFailure"].includes(methodName);
698
- }
699
- return false;
700
- }
701
- function isMonadicCheck(node) {
702
- const sourceCode = context.sourceCode;
703
- const text = sourceCode.getText(node);
704
- if (/\.(isSome|isNone|isEmpty|isDefined)\s*\(\s*\)/.test(text)) {
705
- return { isMonadic: true, type: "Option" };
706
- }
707
- if (/\.(isLeft|isRight)\s*\(\s*\)/.test(text)) {
708
- return { isMonadic: true, type: "Either" };
709
- }
710
- if (/\.(isSuccess|isFailure)\s*\(\s*\)/.test(text)) {
711
- return { isMonadic: true, type: "Result" };
712
- }
713
- if (node.type === "BinaryExpression") {
714
- if ((node.operator === "===" || node.operator === "!==") && (node.left.type === "Literal" && (node.left.value === null || node.left.value === void 0) || node.right.type === "Literal" && (node.right.value === null || node.right.value === void 0))) {
715
- return { isMonadic: true, type: "Option" };
716
- }
717
- if (node.operator === "==" || node.operator === "!=" || node.operator === "===" || node.operator === "!==") {
718
- const leftIsUndefined = node.left.type === "Identifier" && node.left.name === "undefined" || node.left.type === "Literal" && node.left.value === void 0;
719
- const rightIsUndefined = node.right.type === "Identifier" && node.right.name === "undefined" || node.right.type === "Literal" && node.right.value === void 0;
720
- if (leftIsUndefined || rightIsUndefined) {
721
- return { isMonadic: true, type: "Option" };
722
- }
723
- }
724
- }
725
- return { isMonadic: false, type: "" };
726
- }
727
- function analyzeIfStatement(node) {
728
- const test = node.test;
729
- const monadicInfo = isMonadicCheck(test);
730
- if (!monadicInfo.isMonadic) return;
731
- if (node.parent && node.parent.type === "IfStatement") return;
732
- let complexity = 1;
733
- let current = node;
734
- while (current.alternate) {
735
- complexity++;
736
- if (current.alternate.type === "IfStatement") {
737
- current = current.alternate;
738
- } else {
739
- break;
740
- }
741
- }
742
- if (complexity >= minComplexity) {
743
- context.report({
744
- node,
745
- messageId: "preferFold",
746
- data: { type: monadicInfo.type },
747
- fix(fixer) {
748
- if (!shouldAutoFix(node.test)) {
749
- return null;
750
- }
751
- const replacement = generateFoldFromIf(node);
752
- if (replacement) {
753
- return fixer.replaceText(node, replacement);
754
- }
755
- return null;
756
- }
757
- });
758
- }
759
- }
760
- return {
761
- IfStatement(node) {
762
- analyzeIfStatement(node);
763
- },
764
- ConditionalExpression(node) {
765
- const monadicInfo = isMonadicCheck(node.test);
766
- if (monadicInfo.isMonadic) {
767
- context.report({
768
- node,
769
- messageId: "preferFoldTernary",
770
- data: { type: monadicInfo.type },
771
- fix(fixer) {
772
- if (!shouldAutoFix(node.test)) {
773
- return null;
774
- }
775
- const replacement = generateFoldFromTernary(node);
776
- if (replacement) {
777
- return fixer.replaceText(node, replacement);
778
- }
779
- return null;
780
- }
781
- });
782
- }
783
- }
784
- };
785
- }
786
- };
787
- module.exports = rule2;
788
- }
789
- });
790
-
791
- // src/rules/prefer-map.ts
792
- var require_prefer_map = __commonJS({
793
- "src/rules/prefer-map.ts"(exports, module) {
794
- var rule2 = {
795
- meta: {
796
- type: "suggestion",
797
- docs: {
798
- description: "Prefer .map() over manual transformations and imperative patterns",
799
- category: "Best Practices",
800
- recommended: true
801
- },
802
- fixable: "code",
803
- schema: [
804
- {
805
- type: "object",
806
- properties: {
807
- checkArrayMethods: {
808
- type: "boolean",
809
- default: true
810
- },
811
- checkForLoops: {
812
- type: "boolean",
813
- default: true
814
- }
815
- },
816
- additionalProperties: false
817
- }
818
- ],
819
- messages: {
820
- preferMapOverLoop: "Prefer .map() over for loop for transforming {{collection}}",
821
- preferMapOverPush: "Prefer .map() over manual .push() in loop",
822
- preferMapChain: "Consider using .map() for transformation instead of manual property access"
823
- }
824
- },
825
- create(context) {
826
- const options = context.options[0] || {};
827
- const checkArrayMethods = options.checkArrayMethods !== false;
828
- const checkForLoops = options.checkForLoops !== false;
829
- function isForEachToMapSafe(node) {
830
- if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression" || node.callee.property.name !== "forEach") {
831
- return false;
832
- }
833
- const callback = node.arguments[0];
834
- if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
835
- const body = callback.body;
836
- return body.type === "MemberExpression";
837
- }
838
- return false;
839
- }
840
- function isTransformationLoop(node) {
841
- if (!node.body || node.body.type !== "BlockStatement") return false;
842
- const statements = node.body.body;
843
- if (statements.length === 0) return false;
844
- return statements.some((stmt) => {
845
- if (stmt.type === "ExpressionStatement" && stmt.expression.type === "CallExpression") {
846
- const call = stmt.expression;
847
- return call.callee.type === "MemberExpression" && call.callee.property.name === "push";
848
- }
849
- return false;
850
- });
851
- }
852
- function isSimplePropertyAccess(node) {
853
- if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.property.name === "forEach") {
854
- const callback = node.arguments[0];
855
- if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
856
- const body = callback.body;
857
- if (body.type === "MemberExpression") {
858
- return true;
859
- }
860
- }
861
- }
862
- return false;
863
- }
864
- return {
865
- ForStatement(node) {
866
- if (!checkForLoops) return;
867
- if (isTransformationLoop(node)) {
868
- context.report({
869
- node,
870
- messageId: "preferMapOverLoop",
871
- data: { collection: "array" }
872
- });
873
- }
874
- },
875
- ForInStatement(node) {
876
- if (!checkForLoops) return;
877
- if (isTransformationLoop(node)) {
878
- context.report({
879
- node,
880
- messageId: "preferMapOverLoop",
881
- data: { collection: "object" }
882
- });
883
- }
884
- },
885
- ForOfStatement(node) {
886
- if (!checkForLoops) return;
887
- if (isTransformationLoop(node)) {
888
- context.report({
889
- node,
890
- messageId: "preferMapOverLoop",
891
- data: { collection: "iterable" }
892
- });
893
- }
894
- },
895
- CallExpression(node) {
896
- if (!checkArrayMethods) return;
897
- if (node.callee.type === "MemberExpression" && node.callee.property.name === "forEach") {
898
- if (isSimplePropertyAccess(node)) {
899
- context.report({
900
- node,
901
- messageId: "preferMapChain",
902
- fix(fixer) {
903
- if (!isForEachToMapSafe(node)) {
904
- return null;
905
- }
906
- const sourceCode = context.sourceCode;
907
- const text = sourceCode.getText(node);
908
- const fixedText = text.replace(/\.forEach\s*\(/g, ".map(");
909
- return fixer.replaceText(node, fixedText);
910
- }
911
- });
912
- }
913
- }
914
- if (node.callee.type === "MemberExpression" && node.callee.property.name === "push") {
915
- let parent = node.parent;
916
- while (parent) {
917
- if (parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && (parent.callee.property.name === "forEach" || parent.callee.property.name === "for")) {
918
- context.report({
919
- node,
920
- messageId: "preferMapOverPush"
921
- });
922
- break;
923
- }
924
- parent = parent.parent;
925
- }
926
- }
927
- }
928
- };
929
- }
930
- };
931
- module.exports = rule2;
932
- }
933
- });
934
-
935
- // src/rules/prefer-flatmap.ts
936
- var require_prefer_flatmap = __commonJS({
937
- "src/rules/prefer-flatmap.ts"(exports, module) {
938
- var rule2 = {
939
- meta: {
940
- type: "suggestion",
941
- docs: {
942
- description: "Prefer .flatMap() over .map().flat() and nested transformations",
943
- category: "Best Practices",
944
- recommended: true
945
- },
946
- fixable: "code",
947
- schema: [
948
- {
949
- type: "object",
950
- properties: {
951
- checkNestedMaps: {
952
- type: "boolean",
953
- default: true
954
- }
955
- },
956
- additionalProperties: false
957
- }
958
- ],
959
- messages: {
960
- preferFlatMapOverMapFlat: "Use .flatMap() instead of .map().flat()",
961
- preferFlatMapNested: "Consider .flatMap() for nested transformations that return arrays",
962
- preferFlatMapChain: "Use .flatMap() instead of chained .map() operations that flatten results"
963
- }
964
- },
965
- create(context) {
966
- const options = context.options[0] || {};
967
- const checkNestedMaps = options.checkNestedMaps !== false;
968
- function isMapFollowedByFlat(node) {
969
- if (node.type !== "CallExpression") return false;
970
- const callee = node.callee;
971
- if (callee.type !== "MemberExpression") return false;
972
- if (callee.property.name === "flat") {
973
- const object = callee.object;
974
- if (object.type === "CallExpression" && object.callee.type === "MemberExpression" && object.callee.property.name === "map") {
975
- return true;
976
- }
977
- }
978
- return false;
979
- }
980
- function returnsArray(functionNode) {
981
- if (!functionNode || !functionNode.body) return false;
982
- if (functionNode.body.type === "ArrayExpression") {
983
- return true;
984
- }
985
- if (functionNode.body.type === "CallExpression") {
986
- const call = functionNode.body;
987
- if (call.callee.type === "MemberExpression") {
988
- const methodName = call.callee.property.name;
989
- if (["map", "filter", "slice", "concat", "split"].includes(methodName)) {
990
- return true;
991
- }
992
- }
993
- }
994
- if (functionNode.body.type === "BlockStatement") {
995
- const statements = functionNode.body.body;
996
- for (const stmt of statements) {
997
- if (stmt.type === "ReturnStatement" && stmt.argument) {
998
- if (stmt.argument.type === "ArrayExpression") {
999
- return true;
1000
- }
1001
- if (stmt.argument.type === "CallExpression") {
1002
- const call = stmt.argument;
1003
- if (call.callee.type === "MemberExpression") {
1004
- const methodName = call.callee.property.name;
1005
- if (["map", "filter", "slice", "concat", "split"].includes(methodName)) {
1006
- return true;
1007
- }
1008
- }
1009
- }
1010
- }
1011
- }
1012
- }
1013
- return false;
1014
- }
1015
- function isNestedMapReturningArrays(node) {
1016
- if (node.type !== "CallExpression") return false;
1017
- const callee = node.callee;
1018
- if (callee.type !== "MemberExpression") return false;
1019
- if (callee.property.name === "map") {
1020
- const callback = node.arguments[0];
1021
- if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
1022
- return returnsArray(callback);
1023
- }
1024
- }
1025
- return false;
1026
- }
1027
- return {
1028
- CallExpression(node) {
1029
- if (isMapFollowedByFlat(node)) {
1030
- const sourceCode = context.sourceCode;
1031
- context.report({
1032
- node,
1033
- messageId: "preferFlatMapOverMapFlat",
1034
- fix(fixer) {
1035
- const mapCall = node.callee.object;
1036
- const mapCallText = sourceCode.getText(mapCall);
1037
- const flatMapText = mapCallText.replace(/\.map\s*\(/, ".flatMap(");
1038
- return fixer.replaceText(node, flatMapText);
1039
- }
1040
- });
1041
- return;
1042
- }
1043
- if (node.callee.type === "MemberExpression" && node.callee.property.name === "map") {
1044
- const object = node.callee.object;
1045
- if (object.type === "CallExpression" && object.callee.type === "MemberExpression" && object.callee.property.name === "map") {
1046
- const firstMapCallback = object.arguments[0];
1047
- if (firstMapCallback && returnsArray(firstMapCallback)) {
1048
- context.report({
1049
- node: object,
1050
- // Report on the first map call
1051
- messageId: "preferFlatMapChain"
1052
- });
1053
- return;
1054
- }
1055
- }
1056
- }
1057
- if (checkNestedMaps && isNestedMapReturningArrays(node)) {
1058
- if (node.parent && node.parent.type === "MemberExpression" && node.parent.parent && node.parent.parent.type === "CallExpression" && node.parent.property.name === "flat") {
1059
- return;
1060
- }
1061
- const object = node.callee?.object;
1062
- if (object?.type === "CallExpression" && object.callee?.type === "MemberExpression" && object.callee?.property?.name === "map") {
1063
- return;
1064
- }
1065
- if (node.parent?.type === "MemberExpression" && node.parent.parent?.type === "CallExpression" && node.parent.parent.callee?.property?.name === "map") {
1066
- return;
1067
- }
1068
- context.report({
1069
- node,
1070
- messageId: "preferFlatMapNested"
1071
- });
1072
- }
1073
- }
1074
- };
1075
- }
1076
- };
1077
- module.exports = rule2;
1078
- }
1079
- });
1080
-
1081
- // src/rules/no-imperative-loops.ts
1082
- var require_no_imperative_loops = __commonJS({
1083
- "src/rules/no-imperative-loops.ts"(exports, module) {
1084
- var rule2 = {
1085
- meta: {
1086
- type: "suggestion",
1087
- docs: {
1088
- description: "Prefer functional iteration methods over imperative for loops",
1089
- category: "Best Practices",
1090
- recommended: true
1091
- },
1092
- schema: [
1093
- {
1094
- type: "object",
1095
- properties: {
1096
- allowForIndexAccess: {
1097
- type: "boolean",
1098
- default: false
1099
- },
1100
- allowWhileLoops: {
1101
- type: "boolean",
1102
- default: false
1103
- },
1104
- allowInTests: {
1105
- type: "boolean",
1106
- default: true
1107
- }
1108
- },
1109
- additionalProperties: false
1110
- }
1111
- ],
1112
- messages: {
1113
- noForLoop: "Prefer functional methods (.map, .filter, .forEach, .reduce) over for loops",
1114
- noForInLoop: "Prefer Object.keys().forEach() or functional methods over for..in loops",
1115
- noForOfLoop: "Prefer .forEach() or .map() over for..of loops",
1116
- noWhileLoop: "Prefer functional iteration or recursion over while loops",
1117
- noDoWhileLoop: "Prefer functional iteration or recursion over do..while loops",
1118
- suggestFunctional: "Consider: {{suggestion}}"
1119
- }
1120
- },
1121
- create(context) {
1122
- const options = context.options[0] || {};
1123
- const allowForIndexAccess = options.allowForIndexAccess || false;
1124
- const allowWhileLoops = options.allowWhileLoops || false;
1125
- const allowInTests = options.allowInTests !== false;
1126
- function isInTestFile() {
1127
- const filename = context.getFilename();
1128
- return /\.(test|spec)\.(ts|js|tsx|jsx)$/.test(filename) || filename.includes("__tests__") || filename.includes("/test/") || filename.includes("/tests/");
1129
- }
1130
- function needsIndexAccess(node) {
1131
- if (!node.body || node.body.type !== "BlockStatement") return false;
1132
- const sourceCode = context.sourceCode;
1133
- const bodyText = sourceCode.getText(node.body);
1134
- return /\[\s*\w+\s*\]/.test(bodyText) && node.init && node.init.type === "VariableDeclaration";
1135
- }
1136
- return {
1137
- ForStatement(node) {
1138
- if (allowInTests && isInTestFile()) return;
1139
- if (allowForIndexAccess && needsIndexAccess(node)) return;
1140
- context.report({
1141
- node,
1142
- messageId: "noForLoop"
1143
- });
1144
- },
1145
- ForInStatement(node) {
1146
- if (allowInTests && isInTestFile()) return;
1147
- context.report({
1148
- node,
1149
- messageId: "noForInLoop"
1150
- });
1151
- },
1152
- ForOfStatement(node) {
1153
- if (allowInTests && isInTestFile()) return;
1154
- context.report({
1155
- node,
1156
- messageId: "noForOfLoop"
1157
- });
1158
- },
1159
- WhileStatement(node) {
1160
- if (allowWhileLoops) return;
1161
- if (allowInTests && isInTestFile()) return;
1162
- context.report({
1163
- node,
1164
- messageId: "noWhileLoop"
1165
- });
1166
- },
1167
- DoWhileStatement(node) {
1168
- if (allowWhileLoops) return;
1169
- if (allowInTests && isInTestFile()) return;
1170
- context.report({
1171
- node,
1172
- messageId: "noDoWhileLoop"
1173
- });
1174
- }
1175
- };
1176
- }
1177
- };
1178
- module.exports = rule2;
1179
- }
1180
- });
1181
-
1182
- // src/rules/prefer-do-notation.ts
1183
- var rule, prefer_do_notation_default;
1184
- var init_prefer_do_notation = __esm({
1185
- "src/rules/prefer-do-notation.ts"() {
1186
- init_functype_detection();
1187
- rule = {
1188
- meta: {
1189
- type: "suggestion",
1190
- docs: {
1191
- description: "Prefer Do notation for complex monadic compositions and nested operations",
1192
- category: "Best Practices",
1193
- recommended: true
1194
- },
1195
- fixable: "code",
1196
- messages: {
1197
- preferDoForNestedChecks: "Prefer Do notation for nested null/undefined checks instead of logical AND chains",
1198
- preferDoForChainedMethods: "Prefer Do notation for complex flatMap chains ({{count}} levels deep)",
1199
- preferDoForMixedMonads: "Consider Do notation when mixing Option, Either, and Try operations",
1200
- preferDoAsyncForChainedTasks: "Prefer DoAsync notation for chained async operations"
1201
- },
1202
- schema: [
1203
- {
1204
- type: "object",
1205
- properties: {
1206
- minChainDepth: {
1207
- type: "integer",
1208
- minimum: 2,
1209
- default: 3
1210
- },
1211
- detectMixedMonads: {
1212
- type: "boolean",
1213
- default: true
1214
- }
1215
- },
1216
- additionalProperties: false
1217
- }
1218
- ]
1219
- },
1220
- create(context) {
1221
- const options = context.options[0] || {};
1222
- const minChainDepth = options.minChainDepth || 3;
1223
- const detectMixedMonads = options.detectMixedMonads !== false;
1224
- const functypeImports = getFunctypeImports(context);
1225
- const hasDoImport = functypeImports.functions.has("Do") || functypeImports.functions.has("DoAsync");
1226
- function checkNestedNullChecks(node) {
1227
- if (node.operator !== "&&") return;
1228
- let depth = 0;
1229
- let current = node;
1230
- while (current.type === "LogicalExpression" && current.operator === "&&") {
1231
- if (current.right.type === "MemberExpression" || current.right.type === "LogicalExpression" && current.right.operator === "&&") {
1232
- depth++;
1233
- }
1234
- current = current.left;
1235
- }
1236
- if (depth >= 2 && hasNestedPropertyAccess(node)) {
1237
- context.report({
1238
- node,
1239
- messageId: "preferDoForNestedChecks",
1240
- fix: hasDoImport ? (fixer) => fixNestedChecks(fixer, node) : void 0
1241
- });
1242
- }
1243
- }
1244
- function hasNestedPropertyAccess(node) {
1245
- const text = context.sourceCode.getText(node);
1246
- const nestedPattern = /\w+(\.\w+){2,}/g;
1247
- const matches = text.match(nestedPattern);
1248
- return matches !== null && matches.length > 0;
1249
- }
1250
- function checkChainedMethods(node) {
1251
- if (!isChainedMethodCall(node, "flatMap")) return;
1252
- const chainDepth = getChainDepth(node);
1253
- if (chainDepth >= minChainDepth) {
1254
- context.report({
1255
- node,
1256
- messageId: "preferDoForChainedMethods",
1257
- data: { count: chainDepth.toString() },
1258
- fix: hasDoImport ? (fixer) => fixChainedMethods(fixer, node) : void 0
1259
- });
1260
- }
1261
- }
1262
- function getChainDepth(node) {
1263
- let depth = 1;
1264
- let current = node;
1265
- while (current.callee.type === "MemberExpression" && current.callee.object.type === "CallExpression") {
1266
- const method = current.callee.property;
1267
- if (method.type === "Identifier" && ["flatMap", "map", "filter", "fold"].includes(method.name)) {
1268
- depth++;
1269
- }
1270
- current = current.callee.object;
1271
- }
1272
- return depth;
1273
- }
1274
- function fixNestedChecks(fixer, node) {
1275
- const text = context.sourceCode.getText(node);
1276
- const match = text.match(/(\w+)(\.\w+)+/);
1277
- if (match) {
1278
- const baseVar = match[1];
1279
- const chain = match[0];
1280
- const doNotation = `Do(function* () {
1281
- const obj = yield* $(Option(${baseVar}))
1282
- return yield* $(Option(obj${chain.substring(baseVar.length)}))
1283
- })`;
1284
- return fixer.replaceText(node, doNotation);
1285
- }
1286
- return fixer.replaceText(node, `/* TODO: Convert to Do notation */ ${text}`);
1287
- }
1288
- function fixChainedMethods(fixer, node) {
1289
- const text = context.sourceCode.getText(node);
1290
- return fixer.replaceText(node, `/* TODO: Consider Do notation for complex chains */ ${text}`);
1291
- }
1292
- function checkMixedMonads(node) {
1293
- if (!detectMixedMonads) return;
1294
- const text = context.sourceCode.getText(node);
1295
- const monadTypes = ["Option", "Either", "Try", "Task"];
1296
- const foundMonads = monadTypes.filter((type) => text.includes(type));
1297
- if (foundMonads.length >= 2) {
1298
- context.report({
1299
- node,
1300
- messageId: "preferDoForMixedMonads"
1301
- });
1302
- }
1303
- }
1304
- return {
1305
- LogicalExpression(node) {
1306
- checkNestedNullChecks(node);
1307
- },
1308
- CallExpression(node) {
1309
- checkChainedMethods(node);
1310
- checkMixedMonads(node);
1311
- }
1312
- };
1313
- }
1314
- };
1315
- prefer_do_notation_default = rule;
1316
- }
1317
- });
1318
-
1319
- // src/rules/index.ts
1320
- var import_prefer_option, import_prefer_either, import_prefer_list, import_no_get_unsafe, import_prefer_fold, import_prefer_map, import_prefer_flatmap, import_no_imperative_loops, rules_default;
1321
- var init_rules = __esm({
1322
- "src/rules/index.ts"() {
1323
- import_prefer_option = __toESM(require_prefer_option());
1324
- import_prefer_either = __toESM(require_prefer_either());
1325
- import_prefer_list = __toESM(require_prefer_list());
1326
- import_no_get_unsafe = __toESM(require_no_get_unsafe());
1327
- import_prefer_fold = __toESM(require_prefer_fold());
1328
- import_prefer_map = __toESM(require_prefer_map());
1329
- import_prefer_flatmap = __toESM(require_prefer_flatmap());
1330
- import_no_imperative_loops = __toESM(require_no_imperative_loops());
1331
- init_prefer_do_notation();
1332
- rules_default = {
1333
- "prefer-option": import_prefer_option.default,
1334
- "prefer-either": import_prefer_either.default,
1335
- "prefer-list": import_prefer_list.default,
1336
- "no-get-unsafe": import_no_get_unsafe.default,
1337
- "prefer-fold": import_prefer_fold.default,
1338
- "prefer-map": import_prefer_map.default,
1339
- "prefer-flatmap": import_prefer_flatmap.default,
1340
- "no-imperative-loops": import_no_imperative_loops.default,
1341
- "prefer-do-notation": prefer_do_notation_default
1342
- };
1343
- }
1344
- });
1345
-
1346
- // src/index.ts
1347
- var require_src = __commonJS({
1348
- "src/index.ts"(exports, module) {
1349
- init_rules();
1350
- var plugin = {
1351
- rules: rules_default,
1352
- // Meta information
1353
- meta: {
1354
- name: "eslint-plugin-functype",
1355
- version: "2.0.0"
1356
- }
1357
- };
1358
- module.exports = plugin;
1359
- }
1360
- });
1361
- var index = require_src();
1362
-
1363
- module.exports = index;
1364
- //# sourceMappingURL=index.js.map
1
+ import e from"./configs/recommended.js";import t from"./configs/strict.js";import n from"./rules/index.js";const r={rules:n,meta:{name:`eslint-plugin-functype`,version:`2.0.0`},configs:{}};r.configs={recommended:{name:`functype-plugin/recommended`,plugins:{functype:r},rules:e},strict:{name:`functype-plugin/strict`,plugins:{functype:r},rules:t}};export{r as default};
1365
2
  //# sourceMappingURL=index.js.map