eslint-plugin-ts-data-forge 0.10.5 → 0.10.7

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 (107) hide show
  1. package/README.md +3 -3
  2. package/dist/plugin.d.mts +2 -2
  3. package/dist/plugin.d.mts.map +1 -1
  4. package/dist/plugin.mjs +1 -0
  5. package/dist/plugin.mjs.map +1 -1
  6. package/dist/rule-types.d.mts +5 -5
  7. package/dist/rules/ast-utils.d.mts +8 -9
  8. package/dist/rules/ast-utils.d.mts.map +1 -1
  9. package/dist/rules/ast-utils.mjs +3 -4
  10. package/dist/rules/ast-utils.mjs.map +1 -1
  11. package/dist/rules/import-binding.d.mts +38 -0
  12. package/dist/rules/import-binding.d.mts.map +1 -0
  13. package/dist/rules/import-binding.mjs +53 -0
  14. package/dist/rules/import-binding.mjs.map +1 -0
  15. package/dist/rules/mutable-result.mjs +37 -37
  16. package/dist/rules/mutable-result.mjs.map +1 -1
  17. package/dist/rules/no-unnecessary-type-guard.d.mts.map +1 -1
  18. package/dist/rules/no-unnecessary-type-guard.mjs +146 -143
  19. package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
  20. package/dist/rules/prefer-arr-generate.d.mts +2 -2
  21. package/dist/rules/prefer-arr-generate.d.mts.map +1 -1
  22. package/dist/rules/prefer-arr-generate.mjs +146 -136
  23. package/dist/rules/prefer-arr-generate.mjs.map +1 -1
  24. package/dist/rules/prefer-arr-is-bounded-length-array.d.mts.map +1 -1
  25. package/dist/rules/prefer-arr-is-bounded-length-array.mjs +14 -16
  26. package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
  27. package/dist/rules/prefer-arr-is-fixed-length-array.d.mts.map +1 -1
  28. package/dist/rules/prefer-arr-is-fixed-length-array.mjs +15 -17
  29. package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
  30. package/dist/rules/prefer-arr-is-max-length-array.d.mts.map +1 -1
  31. package/dist/rules/prefer-arr-is-max-length-array.mjs +14 -16
  32. package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
  33. package/dist/rules/prefer-arr-is-min-length-array.d.mts.map +1 -1
  34. package/dist/rules/prefer-arr-is-min-length-array.mjs +14 -16
  35. package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
  36. package/dist/rules/prefer-arr-is-non-empty.d.mts.map +1 -1
  37. package/dist/rules/prefer-arr-is-non-empty.mjs +14 -16
  38. package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
  39. package/dist/rules/prefer-arr-scan.d.mts.map +1 -1
  40. package/dist/rules/prefer-arr-scan.mjs +9 -18
  41. package/dist/rules/prefer-arr-scan.mjs.map +1 -1
  42. package/dist/rules/prefer-arr-sum.d.mts.map +1 -1
  43. package/dist/rules/prefer-arr-sum.mjs +16 -21
  44. package/dist/rules/prefer-arr-sum.mjs.map +1 -1
  45. package/dist/rules/prefer-arr-uniq.d.mts.map +1 -1
  46. package/dist/rules/prefer-arr-uniq.mjs +38 -43
  47. package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
  48. package/dist/rules/prefer-as-int.d.mts.map +1 -1
  49. package/dist/rules/prefer-as-int.mjs +19 -26
  50. package/dist/rules/prefer-as-int.mjs.map +1 -1
  51. package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
  52. package/dist/rules/prefer-canonical-array-slicing.mjs +49 -16
  53. package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
  54. package/dist/rules/prefer-canonical-length-cast.mjs +15 -15
  55. package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
  56. package/dist/rules/prefer-num-safe-parse-float.d.mts +1 -1
  57. package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
  58. package/dist/rules/prefer-num-safe-parse-int.d.mts +1 -1
  59. package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
  60. package/dist/rules/prefer-obj-over-entries-round-trip.d.mts.map +1 -1
  61. package/dist/rules/prefer-obj-over-entries-round-trip.mjs +100 -99
  62. package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
  63. package/dist/rules/prefer-pipe-map-nullable.d.mts +2 -2
  64. package/dist/rules/prefer-pipe-map-nullable.d.mts.map +1 -1
  65. package/dist/rules/prefer-pipe-map-nullable.mjs +2 -4
  66. package/dist/rules/prefer-pipe-map-nullable.mjs.map +1 -1
  67. package/dist/rules/prefer-range-for-loop.d.mts.map +1 -1
  68. package/dist/rules/prefer-range-for-loop.mjs +18 -24
  69. package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
  70. package/dist/rules/rules.d.mts +2 -2
  71. package/package.json +6 -6
  72. package/src/plugin.mts +4 -3
  73. package/src/rule-types.mts +5 -5
  74. package/src/rules/ast-utils.mts +14 -15
  75. package/src/rules/import-binding.mts +94 -0
  76. package/src/rules/mutable-result.mts +56 -56
  77. package/src/rules/no-unnecessary-type-guard.mts +203 -207
  78. package/src/rules/no-unnecessary-type-guard.test.mts +62 -2
  79. package/src/rules/prefer-arr-generate.mts +222 -214
  80. package/src/rules/prefer-arr-generate.test.mts +490 -133
  81. package/src/rules/prefer-arr-is-bounded-length-array.mts +22 -33
  82. package/src/rules/prefer-arr-is-fixed-length-array.mts +26 -38
  83. package/src/rules/prefer-arr-is-fixed-length-array.test.mts +16 -7
  84. package/src/rules/prefer-arr-is-max-length-array.mts +23 -36
  85. package/src/rules/prefer-arr-is-max-length-array.test.mts +16 -7
  86. package/src/rules/prefer-arr-is-min-length-array.mts +23 -36
  87. package/src/rules/prefer-arr-is-min-length-array.test.mts +16 -7
  88. package/src/rules/prefer-arr-is-non-empty.mts +17 -34
  89. package/src/rules/prefer-arr-is-non-empty.test.mts +110 -0
  90. package/src/rules/prefer-arr-scan.mts +17 -42
  91. package/src/rules/prefer-arr-scan.test.mts +55 -0
  92. package/src/rules/prefer-arr-sum.mts +22 -36
  93. package/src/rules/prefer-arr-uniq.mts +51 -62
  94. package/src/rules/prefer-arr-uniq.test.mts +46 -20
  95. package/src/rules/prefer-as-int.mts +27 -52
  96. package/src/rules/prefer-as-int.test.mts +79 -29
  97. package/src/rules/prefer-canonical-array-slicing.mts +101 -40
  98. package/src/rules/prefer-canonical-array-slicing.test.mts +133 -34
  99. package/src/rules/prefer-canonical-length-cast.mts +29 -29
  100. package/src/rules/prefer-canonical-length-guard.mts +2 -2
  101. package/src/rules/prefer-num-safe-parse-float.test.mts +255 -94
  102. package/src/rules/prefer-num-safe-parse-int.test.mts +212 -76
  103. package/src/rules/prefer-obj-over-entries-round-trip.mts +152 -170
  104. package/src/rules/prefer-obj-over-entries-round-trip.test.mts +48 -6
  105. package/src/rules/prefer-pipe-map-nullable.mts +17 -19
  106. package/src/rules/prefer-range-for-loop.mts +37 -61
  107. package/src/rules/prefer-range-for-loop.test.mts +2 -2
@@ -1,16 +1,11 @@
1
1
  import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
- import {
3
- buildNamedImportFix,
4
- getArgumentText,
5
- getImportDeclarations,
6
- getNamedImports,
7
- } from 'eslint-rule-forge';
2
+ import { getArgumentText } from 'eslint-rule-forge';
8
3
  import {
9
4
  isArrayOrTupleExpression,
10
5
  isSameArrayText,
11
6
  parseLengthComparison,
12
7
  } from './ast-utils.mjs';
13
- import { TS_DATA_FORGE_MODULE } from './constants.mjs';
8
+ import { tsDataForgeValueAt } from './import-binding.mjs';
14
9
 
15
10
  type Options = readonly [];
16
11
 
@@ -38,13 +33,6 @@ export const preferArrIsBoundedLengthArray: TSESLint.RuleModule<
38
33
  create: (context) => {
39
34
  const sourceCode = context.sourceCode;
40
35
 
41
- const program = sourceCode.ast;
42
-
43
- const tsDataForgeImports = getImportDeclarations(
44
- program,
45
- TS_DATA_FORGE_MODULE,
46
- );
47
-
48
36
  const services = context.sourceCode.parserServices;
49
37
 
50
38
  const mut_nodesToFix: {
@@ -100,14 +88,14 @@ export const preferArrIsBoundedLengthArray: TSESLint.RuleModule<
100
88
  });
101
89
  },
102
90
  'Program:exit': () => {
103
- const namedImports = getNamedImports(tsDataForgeImports);
91
+ const arrAt = tsDataForgeValueAt(sourceCode, 'Arr');
104
92
 
105
- const hasArrImport = namedImports.includes('Arr');
106
-
107
- for (const [
108
- index,
109
- { node, arrayExpression, minExpression, maxExpression },
110
- ] of mut_nodesToFix.entries()) {
93
+ for (const {
94
+ node,
95
+ arrayExpression,
96
+ minExpression,
97
+ maxExpression,
98
+ } of mut_nodesToFix) {
111
99
  const arrayText = getArgumentText(sourceCode, arrayExpression);
112
100
 
113
101
  const minText = sourceCode.getText(minExpression);
@@ -116,6 +104,8 @@ export const preferArrIsBoundedLengthArray: TSESLint.RuleModule<
116
104
 
117
105
  const originalText = sourceCode.getText(node);
118
106
 
107
+ const arr = arrAt(node);
108
+
119
109
  context.report({
120
110
  node,
121
111
  messageId: 'useIsBoundedLengthArray',
@@ -125,18 +115,17 @@ export const preferArrIsBoundedLengthArray: TSESLint.RuleModule<
125
115
  min: minText,
126
116
  max: maxText,
127
117
  },
128
- fix: (fixer) => {
129
- const replacement = `Arr.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;
130
-
131
- const importFixes =
132
- index === 0 && !hasArrImport
133
- ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
134
- 'Arr',
135
- ])
136
- : [];
137
-
138
- return [...importFixes, fixer.replaceText(node, replacement)];
139
- },
118
+ fix:
119
+ arr === undefined
120
+ ? null
121
+ : (fixer) => {
122
+ const replacement = `${arr.localName}.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;
123
+
124
+ return [
125
+ ...arr.edits(fixer),
126
+ fixer.replaceText(node, replacement),
127
+ ];
128
+ },
140
129
  });
141
130
  }
142
131
  },
@@ -1,17 +1,11 @@
1
1
  import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
- import {
3
- buildNamedImportFix,
4
- getArgumentText,
5
- getImportDeclarations,
6
- getNamedImports,
7
- skipTypeWrappers,
8
- } from 'eslint-rule-forge';
2
+ import { getArgumentText, skipTypeWrappers } from 'eslint-rule-forge';
9
3
  import {
10
4
  asLengthAccess,
11
5
  isArrayOrTupleExpression,
12
6
  isIntegerLiteralOrConstant,
13
7
  } from './ast-utils.mjs';
14
- import { TS_DATA_FORGE_MODULE } from './constants.mjs';
8
+ import { tsDataForgeValueAt } from './import-binding.mjs';
15
9
 
16
10
  type Options = readonly [];
17
11
 
@@ -39,13 +33,6 @@ export const preferArrIsFixedLengthArray: TSESLint.RuleModule<
39
33
  create: (context) => {
40
34
  const sourceCode = context.sourceCode;
41
35
 
42
- const program = sourceCode.ast;
43
-
44
- const tsDataForgeImports = getImportDeclarations(
45
- program,
46
- TS_DATA_FORGE_MODULE,
47
- );
48
-
49
36
  const services = context.sourceCode.parserServices;
50
37
 
51
38
  const mut_nodesToFix: {
@@ -92,27 +79,29 @@ export const preferArrIsFixedLengthArray: TSESLint.RuleModule<
92
79
  mut_nodesToFix.push({
93
80
  node,
94
81
  arrayExpression,
95
- // The literal or constant itself: a wrapper such as `as number`
96
- // would only widen the length the guard narrows to.
82
+ // The literal or constant itself: a wrapper such as `as number` would
83
+ // only widen the length the guard narrows to.
97
84
  lengthExpression: skipTypeWrappers(valueSide),
98
85
  isNegated,
99
86
  });
100
87
  },
101
88
  'Program:exit': () => {
102
- const namedImports = getNamedImports(tsDataForgeImports);
89
+ const arrAt = tsDataForgeValueAt(sourceCode, 'Arr');
103
90
 
104
- const hasArrImport = namedImports.includes('Arr');
105
-
106
- for (const [
107
- index,
108
- { node, arrayExpression, lengthExpression, isNegated },
109
- ] of mut_nodesToFix.entries()) {
91
+ for (const {
92
+ node,
93
+ arrayExpression,
94
+ lengthExpression,
95
+ isNegated,
96
+ } of mut_nodesToFix) {
110
97
  const arrayText = getArgumentText(sourceCode, arrayExpression);
111
98
 
112
99
  const lengthText = sourceCode.getText(lengthExpression);
113
100
 
114
101
  const originalText = sourceCode.getText(node);
115
102
 
103
+ const arr = arrAt(node);
104
+
116
105
  context.report({
117
106
  node,
118
107
  messageId: 'useIsFixedLengthArray',
@@ -121,20 +110,19 @@ export const preferArrIsFixedLengthArray: TSESLint.RuleModule<
121
110
  arrayName: arrayText,
122
111
  length: lengthText,
123
112
  },
124
- fix: (fixer) => {
125
- const baseCall = `Arr.isFixedLengthArray(${lengthText}, ${arrayText})`;
126
-
127
- const replacement = isNegated ? `!${baseCall}` : baseCall;
128
-
129
- const importFixes =
130
- index === 0 && !hasArrImport
131
- ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
132
- 'Arr',
133
- ])
134
- : [];
135
-
136
- return [...importFixes, fixer.replaceText(node, replacement)];
137
- },
113
+ fix:
114
+ arr === undefined
115
+ ? null
116
+ : (fixer) => {
117
+ const baseCall = `${arr.localName}.isFixedLengthArray(${lengthText}, ${arrayText})`;
118
+
119
+ const replacement = isNegated ? `!${baseCall}` : baseCall;
120
+
121
+ return [
122
+ ...arr.edits(fixer),
123
+ fixer.replaceText(node, replacement),
124
+ ];
125
+ },
138
126
  });
139
127
  }
140
128
  },
@@ -158,13 +158,22 @@ describe('prefer-arr-is-fixed-length-array', () => {
158
158
  const ok1 = xs.length === 3;
159
159
  const ok2 = ys.length === 2;
160
160
  `,
161
- output: dedent`
162
- import { Arr } from 'ts-data-forge';
163
- const xs = [1, 2, 3];
164
- const ys = [4, 5];
165
- const ok1 = Arr.isFixedLengthArray(3, xs);
166
- const ok2 = Arr.isFixedLengthArray(2, ys);
167
- `,
161
+ output: [
162
+ dedent`
163
+ import { Arr } from 'ts-data-forge';
164
+ const xs = [1, 2, 3];
165
+ const ys = [4, 5];
166
+ const ok1 = Arr.isFixedLengthArray(3, xs);
167
+ const ok2 = ys.length === 2;
168
+ `,
169
+ dedent`
170
+ import { Arr } from 'ts-data-forge';
171
+ const xs = [1, 2, 3];
172
+ const ys = [4, 5];
173
+ const ok1 = Arr.isFixedLengthArray(3, xs);
174
+ const ok2 = Arr.isFixedLengthArray(2, ys);
175
+ `,
176
+ ],
168
177
  errors: [
169
178
  { messageId: 'useIsFixedLengthArray' },
170
179
  { messageId: 'useIsFixedLengthArray' },
@@ -1,18 +1,12 @@
1
1
  import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
- import {
3
- buildNamedImportFix,
4
- getArgumentText,
5
- getImportDeclarations,
6
- getNamedImports,
7
- skipTypeWrappers,
8
- } from 'eslint-rule-forge';
2
+ import { getArgumentText, skipTypeWrappers } from 'eslint-rule-forge';
9
3
  import {
10
4
  asLengthAccess,
11
5
  isArrayOrTupleExpression,
12
6
  isIntegerLiteralOrConstant,
13
7
  isPartOfBoundedLengthCheck,
14
8
  } from './ast-utils.mjs';
15
- import { TS_DATA_FORGE_MODULE } from './constants.mjs';
9
+ import { tsDataForgeValueAt } from './import-binding.mjs';
16
10
 
17
11
  type Options = readonly [];
18
12
 
@@ -40,13 +34,6 @@ export const preferArrIsMaxLengthArray: TSESLint.RuleModule<
40
34
  create: (context) => {
41
35
  const sourceCode = context.sourceCode;
42
36
 
43
- const program = sourceCode.ast;
44
-
45
- const tsDataForgeImports = getImportDeclarations(
46
- program,
47
- TS_DATA_FORGE_MODULE,
48
- );
49
-
50
37
  const services = context.sourceCode.parserServices;
51
38
 
52
39
  const mut_nodesToFix: {
@@ -98,26 +85,27 @@ export const preferArrIsMaxLengthArray: TSESLint.RuleModule<
98
85
  mut_nodesToFix.push({
99
86
  node,
100
87
  arrayExpression,
101
- // The literal or constant itself: a wrapper such as `as number`
102
- // would only widen the length the guard narrows to.
88
+ // The literal or constant itself: a wrapper such as `as number` would
89
+ // only widen the length the guard narrows to.
103
90
  lengthExpression: skipTypeWrappers(valueSide),
104
91
  });
105
92
  },
106
93
  'Program:exit': () => {
107
- const namedImports = getNamedImports(tsDataForgeImports);
94
+ const arrAt = tsDataForgeValueAt(sourceCode, 'Arr');
108
95
 
109
- const hasArrImport = namedImports.includes('Arr');
110
-
111
- for (const [
112
- index,
113
- { node, arrayExpression, lengthExpression },
114
- ] of mut_nodesToFix.entries()) {
96
+ for (const {
97
+ node,
98
+ arrayExpression,
99
+ lengthExpression,
100
+ } of mut_nodesToFix) {
115
101
  const arrayText = getArgumentText(sourceCode, arrayExpression);
116
102
 
117
103
  const lengthText = sourceCode.getText(lengthExpression);
118
104
 
119
105
  const originalText = sourceCode.getText(node);
120
106
 
107
+ const arr = arrAt(node);
108
+
121
109
  context.report({
122
110
  node,
123
111
  messageId: 'useIsMaxLengthArray',
@@ -126,18 +114,17 @@ export const preferArrIsMaxLengthArray: TSESLint.RuleModule<
126
114
  arrayName: arrayText,
127
115
  length: lengthText,
128
116
  },
129
- fix: (fixer) => {
130
- const replacement = `Arr.isMaxLengthArray(${lengthText}, ${arrayText})`;
131
-
132
- const importFixes =
133
- index === 0 && !hasArrImport
134
- ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
135
- 'Arr',
136
- ])
137
- : [];
138
-
139
- return [...importFixes, fixer.replaceText(node, replacement)];
140
- },
117
+ fix:
118
+ arr === undefined
119
+ ? null
120
+ : (fixer) => {
121
+ const replacement = `${arr.localName}.isMaxLengthArray(${lengthText}, ${arrayText})`;
122
+
123
+ return [
124
+ ...arr.edits(fixer),
125
+ fixer.replaceText(node, replacement),
126
+ ];
127
+ },
141
128
  });
142
129
  }
143
130
  },
@@ -171,13 +171,22 @@ describe('prefer-arr-is-max-length-array', () => {
171
171
  const ok1 = xs.length <= 2;
172
172
  const ok2 = ys.length <= 1;
173
173
  `,
174
- output: dedent`
175
- import { Arr } from 'ts-data-forge';
176
- const xs = [1, 2, 3];
177
- const ys = [4, 5];
178
- const ok1 = Arr.isMaxLengthArray(2, xs);
179
- const ok2 = Arr.isMaxLengthArray(1, ys);
180
- `,
174
+ output: [
175
+ dedent`
176
+ import { Arr } from 'ts-data-forge';
177
+ const xs = [1, 2, 3];
178
+ const ys = [4, 5];
179
+ const ok1 = Arr.isMaxLengthArray(2, xs);
180
+ const ok2 = ys.length <= 1;
181
+ `,
182
+ dedent`
183
+ import { Arr } from 'ts-data-forge';
184
+ const xs = [1, 2, 3];
185
+ const ys = [4, 5];
186
+ const ok1 = Arr.isMaxLengthArray(2, xs);
187
+ const ok2 = Arr.isMaxLengthArray(1, ys);
188
+ `,
189
+ ],
181
190
  errors: [
182
191
  { messageId: 'useIsMaxLengthArray' },
183
192
  { messageId: 'useIsMaxLengthArray' },
@@ -1,18 +1,12 @@
1
1
  import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
- import {
3
- buildNamedImportFix,
4
- getArgumentText,
5
- getImportDeclarations,
6
- getNamedImports,
7
- skipTypeWrappers,
8
- } from 'eslint-rule-forge';
2
+ import { getArgumentText, skipTypeWrappers } from 'eslint-rule-forge';
9
3
  import {
10
4
  asLengthAccess,
11
5
  isArrayOrTupleExpression,
12
6
  isIntegerLiteralOrConstant,
13
7
  isPartOfBoundedLengthCheck,
14
8
  } from './ast-utils.mjs';
15
- import { TS_DATA_FORGE_MODULE } from './constants.mjs';
9
+ import { tsDataForgeValueAt } from './import-binding.mjs';
16
10
 
17
11
  type Options = readonly [];
18
12
 
@@ -40,13 +34,6 @@ export const preferArrIsMinLengthArray: TSESLint.RuleModule<
40
34
  create: (context) => {
41
35
  const sourceCode = context.sourceCode;
42
36
 
43
- const program = sourceCode.ast;
44
-
45
- const tsDataForgeImports = getImportDeclarations(
46
- program,
47
- TS_DATA_FORGE_MODULE,
48
- );
49
-
50
37
  const services = context.sourceCode.parserServices;
51
38
 
52
39
  const mut_nodesToFix: {
@@ -98,26 +85,27 @@ export const preferArrIsMinLengthArray: TSESLint.RuleModule<
98
85
  mut_nodesToFix.push({
99
86
  node,
100
87
  arrayExpression,
101
- // The literal or constant itself: a wrapper such as `as number`
102
- // would only widen the length the guard narrows to.
88
+ // The literal or constant itself: a wrapper such as `as number` would
89
+ // only widen the length the guard narrows to.
103
90
  lengthExpression: skipTypeWrappers(valueSide),
104
91
  });
105
92
  },
106
93
  'Program:exit': () => {
107
- const namedImports = getNamedImports(tsDataForgeImports);
94
+ const arrAt = tsDataForgeValueAt(sourceCode, 'Arr');
108
95
 
109
- const hasArrImport = namedImports.includes('Arr');
110
-
111
- for (const [
112
- index,
113
- { node, arrayExpression, lengthExpression },
114
- ] of mut_nodesToFix.entries()) {
96
+ for (const {
97
+ node,
98
+ arrayExpression,
99
+ lengthExpression,
100
+ } of mut_nodesToFix) {
115
101
  const arrayText = getArgumentText(sourceCode, arrayExpression);
116
102
 
117
103
  const lengthText = sourceCode.getText(lengthExpression);
118
104
 
119
105
  const originalText = sourceCode.getText(node);
120
106
 
107
+ const arr = arrAt(node);
108
+
121
109
  context.report({
122
110
  node,
123
111
  messageId: 'useIsMinLengthArray',
@@ -126,18 +114,17 @@ export const preferArrIsMinLengthArray: TSESLint.RuleModule<
126
114
  arrayName: arrayText,
127
115
  length: lengthText,
128
116
  },
129
- fix: (fixer) => {
130
- const replacement = `Arr.isMinLengthArray(${lengthText}, ${arrayText})`;
131
-
132
- const importFixes =
133
- index === 0 && !hasArrImport
134
- ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
135
- 'Arr',
136
- ])
137
- : [];
138
-
139
- return [...importFixes, fixer.replaceText(node, replacement)];
140
- },
117
+ fix:
118
+ arr === undefined
119
+ ? null
120
+ : (fixer) => {
121
+ const replacement = `${arr.localName}.isMinLengthArray(${lengthText}, ${arrayText})`;
122
+
123
+ return [
124
+ ...arr.edits(fixer),
125
+ fixer.replaceText(node, replacement),
126
+ ];
127
+ },
141
128
  });
142
129
  }
143
130
  },
@@ -172,13 +172,22 @@ describe('prefer-arr-is-min-length-array', () => {
172
172
  const ok1 = xs.length >= 2;
173
173
  const ok2 = ys.length >= 1;
174
174
  `,
175
- output: dedent`
176
- import { Arr } from 'ts-data-forge';
177
- const xs = [1, 2, 3];
178
- const ys = [4, 5];
179
- const ok1 = Arr.isMinLengthArray(2, xs);
180
- const ok2 = Arr.isMinLengthArray(1, ys);
181
- `,
175
+ output: [
176
+ dedent`
177
+ import { Arr } from 'ts-data-forge';
178
+ const xs = [1, 2, 3];
179
+ const ys = [4, 5];
180
+ const ok1 = Arr.isMinLengthArray(2, xs);
181
+ const ok2 = ys.length >= 1;
182
+ `,
183
+ dedent`
184
+ import { Arr } from 'ts-data-forge';
185
+ const xs = [1, 2, 3];
186
+ const ys = [4, 5];
187
+ const ok1 = Arr.isMinLengthArray(2, xs);
188
+ const ok2 = Arr.isMinLengthArray(1, ys);
189
+ `,
190
+ ],
182
191
  errors: [
183
192
  { messageId: 'useIsMinLengthArray' },
184
193
  { messageId: 'useIsMinLengthArray' },
@@ -3,15 +3,9 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import {
7
- buildNamedImportFix,
8
- getArgumentText,
9
- getImportDeclarations,
10
- getNamedImports,
11
- skipTypeWrappers,
12
- } from 'eslint-rule-forge';
6
+ import { getArgumentText, skipTypeWrappers } from 'eslint-rule-forge';
13
7
  import { asLengthAccess, isArrayOrTupleExpression } from './ast-utils.mjs';
14
- import { TS_DATA_FORGE_MODULE } from './constants.mjs';
8
+ import { tsDataForgeValueAt } from './import-binding.mjs';
15
9
 
16
10
  type Options = readonly [];
17
11
 
@@ -36,13 +30,6 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
36
30
  create: (context) => {
37
31
  const sourceCode = context.sourceCode;
38
32
 
39
- const program = sourceCode.ast;
40
-
41
- const tsDataForgeImports = getImportDeclarations(
42
- program,
43
- TS_DATA_FORGE_MODULE,
44
- );
45
-
46
33
  const services = context.sourceCode.parserServices;
47
34
 
48
35
  const mut_nodesToFix: {
@@ -90,18 +77,15 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
90
77
  });
91
78
  },
92
79
  'Program:exit': () => {
93
- const namedImports = getNamedImports(tsDataForgeImports);
94
-
95
- const hasArrImport = namedImports.includes('Arr');
80
+ const arrAt = tsDataForgeValueAt(sourceCode, 'Arr');
96
81
 
97
- for (const [
98
- index,
99
- { node, arrayExpression },
100
- ] of mut_nodesToFix.entries()) {
82
+ for (const { node, arrayExpression } of mut_nodesToFix) {
101
83
  const arrayText = getArgumentText(sourceCode, arrayExpression);
102
84
 
103
85
  const originalText = sourceCode.getText(node);
104
86
 
87
+ const arr = arrAt(node);
88
+
105
89
  context.report({
106
90
  node,
107
91
  messageId: 'useIsNonEmpty',
@@ -109,18 +93,17 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
109
93
  original: originalText,
110
94
  arrayName: arrayText,
111
95
  },
112
- fix: (fixer) => {
113
- const replacement = `Arr.isNonEmpty(${arrayText})`;
114
-
115
- const importFixes =
116
- index === 0 && !hasArrImport
117
- ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
118
- 'Arr',
119
- ])
120
- : [];
121
-
122
- return [...importFixes, fixer.replaceText(node, replacement)];
123
- },
96
+ fix:
97
+ arr === undefined
98
+ ? null
99
+ : (fixer) => {
100
+ const replacement = `${arr.localName}.isNonEmpty(${arrayText})`;
101
+
102
+ return [
103
+ ...arr.edits(fixer),
104
+ fixer.replaceText(node, replacement),
105
+ ];
106
+ },
124
107
  });
125
108
  }
126
109
  },