eslint-plugin-ts-data-forge 0.8.4 → 0.8.6

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 (97) hide show
  1. package/dist/rules/ast-utils.d.mts +0 -32
  2. package/dist/rules/ast-utils.d.mts.map +1 -1
  3. package/dist/rules/ast-utils.mjs +4 -56
  4. package/dist/rules/ast-utils.mjs.map +1 -1
  5. package/dist/rules/no-unnecessary-type-guard.d.mts.map +1 -1
  6. package/dist/rules/no-unnecessary-type-guard.mjs +6 -6
  7. package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
  8. package/dist/rules/prefer-arr-is-array.d.mts.map +1 -1
  9. package/dist/rules/prefer-arr-is-array.mjs +7 -5
  10. package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
  11. package/dist/rules/prefer-arr-is-bounded-length-array.d.mts.map +1 -1
  12. package/dist/rules/prefer-arr-is-bounded-length-array.mjs +9 -6
  13. package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
  14. package/dist/rules/prefer-arr-is-fixed-length-array.d.mts.map +1 -1
  15. package/dist/rules/prefer-arr-is-fixed-length-array.mjs +9 -6
  16. package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
  17. package/dist/rules/prefer-arr-is-max-length-array.d.mts.map +1 -1
  18. package/dist/rules/prefer-arr-is-max-length-array.mjs +9 -6
  19. package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
  20. package/dist/rules/prefer-arr-is-min-length-array.d.mts.map +1 -1
  21. package/dist/rules/prefer-arr-is-min-length-array.mjs +9 -6
  22. package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
  23. package/dist/rules/prefer-arr-is-non-empty.d.mts.map +1 -1
  24. package/dist/rules/prefer-arr-is-non-empty.mjs +9 -6
  25. package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
  26. package/dist/rules/prefer-arr-scan.d.mts.map +1 -1
  27. package/dist/rules/prefer-arr-scan.mjs +8 -11
  28. package/dist/rules/prefer-arr-scan.mjs.map +1 -1
  29. package/dist/rules/prefer-arr-sum.d.mts.map +1 -1
  30. package/dist/rules/prefer-arr-sum.mjs +8 -6
  31. package/dist/rules/prefer-arr-sum.mjs.map +1 -1
  32. package/dist/rules/prefer-arr-uniq.d.mts.map +1 -1
  33. package/dist/rules/prefer-arr-uniq.mjs +11 -18
  34. package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
  35. package/dist/rules/prefer-as-int.d.mts.map +1 -1
  36. package/dist/rules/prefer-as-int.mjs +6 -8
  37. package/dist/rules/prefer-as-int.mjs.map +1 -1
  38. package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
  39. package/dist/rules/prefer-canonical-array-slicing.mjs +10 -6
  40. package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
  41. package/dist/rules/prefer-canonical-length-cast.d.mts.map +1 -1
  42. package/dist/rules/prefer-canonical-length-cast.mjs +3 -3
  43. package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
  44. package/dist/rules/prefer-canonical-length-guard.d.mts.map +1 -1
  45. package/dist/rules/prefer-canonical-length-guard.mjs +4 -4
  46. package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -1
  47. package/dist/rules/prefer-comparison-over-nullish-guard.d.mts.map +1 -1
  48. package/dist/rules/prefer-comparison-over-nullish-guard.mjs +3 -2
  49. package/dist/rules/prefer-comparison-over-nullish-guard.mjs.map +1 -1
  50. package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
  51. package/dist/rules/prefer-is-non-null-object.mjs +5 -5
  52. package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
  53. package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
  54. package/dist/rules/prefer-is-record-and-has-key.mjs +5 -5
  55. package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
  56. package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
  57. package/dist/rules/prefer-num-safe-parse-float.mjs +6 -6
  58. package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
  59. package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
  60. package/dist/rules/prefer-num-safe-parse-int.mjs +6 -6
  61. package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
  62. package/dist/rules/prefer-obj-over-entries-round-trip.d.mts.map +1 -1
  63. package/dist/rules/prefer-obj-over-entries-round-trip.mjs +9 -12
  64. package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
  65. package/dist/rules/prefer-range-for-loop.d.mts.map +1 -1
  66. package/dist/rules/prefer-range-for-loop.mjs +8 -8
  67. package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
  68. package/package.json +10 -9
  69. package/src/rules/ast-utils.mts +8 -82
  70. package/src/rules/no-unnecessary-type-guard.mts +14 -10
  71. package/src/rules/prefer-arr-is-array.mts +14 -7
  72. package/src/rules/prefer-arr-is-array.test.mts +14 -0
  73. package/src/rules/prefer-arr-is-bounded-length-array.mts +16 -10
  74. package/src/rules/prefer-arr-is-fixed-length-array.mts +17 -11
  75. package/src/rules/prefer-arr-is-max-length-array.mts +17 -11
  76. package/src/rules/prefer-arr-is-min-length-array.mts +17 -11
  77. package/src/rules/prefer-arr-is-non-empty.mts +16 -13
  78. package/src/rules/prefer-arr-scan.mts +24 -19
  79. package/src/rules/prefer-arr-sum.mts +18 -14
  80. package/src/rules/prefer-arr-uniq.mts +24 -28
  81. package/src/rules/prefer-as-int.mts +19 -12
  82. package/src/rules/prefer-canonical-array-slicing.mts +22 -12
  83. package/src/rules/prefer-canonical-length-cast.mts +7 -3
  84. package/src/rules/prefer-canonical-length-guard.mts +9 -4
  85. package/src/rules/prefer-comparison-over-nullish-guard.mts +3 -2
  86. package/src/rules/prefer-comparison-over-nullish-guard.test.mts +16 -0
  87. package/src/rules/prefer-is-non-null-object.mts +11 -7
  88. package/src/rules/prefer-is-record-and-has-key.mts +14 -10
  89. package/src/rules/prefer-num-safe-parse-float.mts +15 -10
  90. package/src/rules/prefer-num-safe-parse-int.mts +15 -13
  91. package/src/rules/prefer-obj-over-entries-round-trip.mts +19 -22
  92. package/src/rules/prefer-range-for-loop.mts +16 -11
  93. package/dist/rules/import-utils.d.mts +0 -30
  94. package/dist/rules/import-utils.d.mts.map +0 -1
  95. package/dist/rules/import-utils.mjs +0 -98
  96. package/dist/rules/import-utils.mjs.map +0 -1
  97. package/src/rules/import-utils.mts +0 -175
@@ -4,16 +4,14 @@ import {
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
6
  import {
7
- asLengthAccess,
8
- isArrayOrTupleExpression,
9
- skipTypeWrappers,
10
- toArgumentText,
11
- } from './ast-utils.mjs';
12
- import {
13
- buildImportFixes,
7
+ buildNamedImportFix,
8
+ getArgumentText,
9
+ getImportDeclarations,
14
10
  getNamedImports,
15
- getTsDataForgeImport,
16
- } from './import-utils.mjs';
11
+ skipTypeWrappers,
12
+ } from 'eslint-rule-forge';
13
+ import { asLengthAccess, isArrayOrTupleExpression } from './ast-utils.mjs';
14
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
17
15
 
18
16
  type Options = readonly [];
19
17
 
@@ -39,7 +37,10 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
39
37
 
40
38
  const program = sourceCode.ast;
41
39
 
42
- const tsDataForgeImport = getTsDataForgeImport(program);
40
+ const tsDataForgeImports = getImportDeclarations(
41
+ program,
42
+ TS_DATA_FORGE_MODULE,
43
+ );
43
44
 
44
45
  const services = context.sourceCode.parserServices;
45
46
 
@@ -88,7 +89,7 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
88
89
  });
89
90
  },
90
91
  'Program:exit': () => {
91
- const namedImports = getNamedImports(tsDataForgeImport);
92
+ const namedImports = getNamedImports(tsDataForgeImports);
92
93
 
93
94
  const hasArrImport = namedImports.includes('Arr');
94
95
 
@@ -96,7 +97,7 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
96
97
  index,
97
98
  { node, arrayExpression },
98
99
  ] of mut_nodesToFix.entries()) {
99
- const arrayText = toArgumentText(arrayExpression, sourceCode);
100
+ const arrayText = getArgumentText(sourceCode, arrayExpression);
100
101
 
101
102
  const originalText = sourceCode.getText(node);
102
103
 
@@ -112,7 +113,9 @@ export const preferArrIsNonEmpty: TSESLint.RuleModule<MessageIds, Options> = {
112
113
 
113
114
  const importFixes =
114
115
  index === 0 && !hasArrImport
115
- ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])
116
+ ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
117
+ 'Arr',
118
+ ])
116
119
  : [];
117
120
 
118
121
  return [...importFixes, fixer.replaceText(node, replacement)];
@@ -3,19 +3,18 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import { Arr } from 'ts-data-forge';
7
- import type { FixedLengthTuple } from 'ts-type-forge';
8
- import * as ts from 'typescript';
9
6
  import {
7
+ buildNamedImportFix,
8
+ getArgumentText,
9
+ getImportDeclarations,
10
+ getNamedImports,
10
11
  skipTypeWrappers,
11
- toArgumentText,
12
12
  typeWrapperLayers,
13
- } from './ast-utils.mjs';
14
- import {
15
- buildImportFixes,
16
- getNamedImports,
17
- getTsDataForgeImport,
18
- } from './import-utils.mjs';
13
+ } from 'eslint-rule-forge';
14
+ import { Arr } from 'ts-data-forge';
15
+ import type { FixedLengthTuple } from 'ts-type-forge';
16
+ import * as ts from 'typescript';
17
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
19
18
 
20
19
  type Options = readonly [];
21
20
 
@@ -179,7 +178,10 @@ export const preferArrScan: TSESLint.RuleModule<MessageIds, Options> = {
179
178
 
180
179
  const program = sourceCode.ast;
181
180
 
182
- const tsDataForgeImport = getTsDataForgeImport(program);
181
+ const tsDataForgeImports = getImportDeclarations(
182
+ program,
183
+ TS_DATA_FORGE_MODULE,
184
+ );
183
185
 
184
186
  const services = sourceCode.parserServices;
185
187
 
@@ -343,10 +345,9 @@ export const preferArrScan: TSESLint.RuleModule<MessageIds, Options> = {
343
345
  mut_lastReads,
344
346
  );
345
347
 
346
- const arrayText = toArgumentText(
347
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion
348
+ const arrayText = getArgumentText(
349
+ sourceCode, // eslint-disable-next-line total-functions/no-unsafe-type-assertion
348
350
  (call.callee as TSESTree.MemberExpression).object,
349
- sourceCode,
350
351
  );
351
352
 
352
353
  // A node's text never includes the parentheses the source put around it:
@@ -358,7 +359,7 @@ export const preferArrScan: TSESLint.RuleModule<MessageIds, Options> = {
358
359
  ? `(${elementText})`
359
360
  : elementText;
360
361
 
361
- return `Arr.scan(${arrayText}, (${parameterText}) => ${bodyText}, ${toArgumentText(seed, sourceCode)})`;
362
+ return `Arr.scan(${arrayText}, (${parameterText}) => ${bodyText}, ${getArgumentText(sourceCode, seed)})`;
362
363
  };
363
364
 
364
365
  /**
@@ -493,7 +494,8 @@ export const preferArrScan: TSESLint.RuleModule<MessageIds, Options> = {
493
494
  // been seen by the time its `map` is judged, whichever order the traversal
494
495
  // reached them in.
495
496
  'Program:exit': () => {
496
- const hasArrImport = getNamedImports(tsDataForgeImport).includes('Arr');
497
+ const hasArrImport =
498
+ getNamedImports(tsDataForgeImports).includes('Arr');
497
499
 
498
500
  // The import is inserted once per pass: two fixes writing the same
499
501
  // insertion point would otherwise produce it twice.
@@ -514,9 +516,12 @@ export const preferArrScan: TSESLint.RuleModule<MessageIds, Options> = {
514
516
  ? undefined
515
517
  : (fixer) => [
516
518
  ...(call === mut_importNeededBy
517
- ? buildImportFixes(fixer, program, tsDataForgeImport, [
518
- 'Arr',
519
- ])
519
+ ? buildNamedImportFix(
520
+ fixer,
521
+ program,
522
+ TS_DATA_FORGE_MODULE,
523
+ ['Arr'],
524
+ )
520
525
  : []),
521
526
  fixer.replaceText(call, replacement),
522
527
  ],
@@ -3,18 +3,17 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import { Arr, pipe } from 'ts-data-forge';
7
- import type { TypeReference } from 'typescript';
8
6
  import {
7
+ buildNamedImportFix,
8
+ getArgumentText,
9
+ getImportDeclarations,
10
+ getNamedImports,
9
11
  skipTypeWrappers,
10
- toArgumentText,
11
12
  typeWrapperLayers,
12
- } from './ast-utils.mjs';
13
- import {
14
- buildImportFixes,
15
- getNamedImports,
16
- getTsDataForgeImport,
17
- } from './import-utils.mjs';
13
+ } from 'eslint-rule-forge';
14
+ import { Arr, pipe } from 'ts-data-forge';
15
+ import type { TypeReference } from 'typescript';
16
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
18
17
 
19
18
  type Options = readonly [];
20
19
 
@@ -41,7 +40,10 @@ export const preferArrSum: TSESLint.RuleModule<MessageIds, Options> = {
41
40
 
42
41
  const program = sourceCode.ast;
43
42
 
44
- const tsDataForgeImport = getTsDataForgeImport(program);
43
+ const tsDataForgeImports = getImportDeclarations(
44
+ program,
45
+ TS_DATA_FORGE_MODULE,
46
+ );
45
47
 
46
48
  const services = context.sourceCode.parserServices;
47
49
 
@@ -264,14 +266,14 @@ export const preferArrSum: TSESLint.RuleModule<MessageIds, Options> = {
264
266
  }
265
267
  },
266
268
  'Program:exit': () => {
267
- const namedImports = getNamedImports(tsDataForgeImport);
269
+ const namedImports = getNamedImports(tsDataForgeImports);
268
270
 
269
271
  const hasArrImport = namedImports.includes('Arr');
270
272
 
271
273
  for (const [index, nodeInfo] of mut_nodesToFix.entries()) {
272
- const arrayText = toArgumentText(
273
- nodeInfo.arrayExpression,
274
+ const arrayText = getArgumentText(
274
275
  sourceCode,
276
+ nodeInfo.arrayExpression,
275
277
  );
276
278
 
277
279
  context.report({
@@ -289,7 +291,9 @@ export const preferArrSum: TSESLint.RuleModule<MessageIds, Options> = {
289
291
 
290
292
  const importFixes =
291
293
  index === 0 && !hasArrImport
292
- ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])
294
+ ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
295
+ 'Arr',
296
+ ])
293
297
  : [];
294
298
 
295
299
  return [
@@ -1,16 +1,20 @@
1
1
  import {
2
2
  AST_NODE_TYPES,
3
+ ASTUtils,
3
4
  type TSESLint,
4
5
  type TSESTree,
5
6
  } from '@typescript-eslint/utils';
6
- import { Arr } from 'ts-data-forge';
7
- import * as ts from 'typescript';
8
- import { skipTypeWrappers, typeWrapperLayers } from './ast-utils.mjs';
9
7
  import {
10
- buildImportFixes,
8
+ buildNamedImportFix,
9
+ getArgumentText,
10
+ getImportDeclarations,
11
11
  getImportedLocalName,
12
- getTsDataForgeImport,
13
- } from './import-utils.mjs';
12
+ skipTypeWrappers,
13
+ typeWrapperLayers,
14
+ } from 'eslint-rule-forge';
15
+ import { Arr } from 'ts-data-forge';
16
+ import * as ts from 'typescript';
17
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
14
18
 
15
19
  type Options = readonly [];
16
20
 
@@ -47,7 +51,10 @@ export const preferArrUniq: TSESLint.RuleModule<MessageIds, Options> = {
47
51
 
48
52
  const program = sourceCode.ast;
49
53
 
50
- const tsDataForgeImport = getTsDataForgeImport(program);
54
+ const tsDataForgeImports = getImportDeclarations(
55
+ program,
56
+ TS_DATA_FORGE_MODULE,
57
+ );
51
58
 
52
59
  const services = sourceCode.parserServices;
53
60
 
@@ -150,7 +157,7 @@ export const preferArrUniq: TSESLint.RuleModule<MessageIds, Options> = {
150
157
  check(node, element.argument);
151
158
  },
152
159
  'Program:exit': () => {
153
- const arrLocalName = getImportedLocalName(tsDataForgeImport, 'Arr');
160
+ const arrLocalName = getImportedLocalName(tsDataForgeImports, 'Arr');
154
161
 
155
162
  const arrName = arrLocalName ?? 'Arr';
156
163
 
@@ -162,16 +169,18 @@ export const preferArrUniq: TSESLint.RuleModule<MessageIds, Options> = {
162
169
  for (const match of mut_matches) {
163
170
  const arrayText = sourceCode.getText(match.arrayExpression);
164
171
 
165
- const argumentText =
166
- match.arrayExpression.type === AST_NODE_TYPES.SequenceExpression
167
- ? `(${arrayText})`
168
- : arrayText;
172
+ const argumentText = getArgumentText(
173
+ sourceCode,
174
+ match.arrayExpression,
175
+ );
169
176
 
170
177
  const buildFix =
171
178
  (withImport: boolean): TSESLint.ReportFixFunction =>
172
179
  (fixer) => [
173
180
  ...(withImport && arrLocalName === undefined
174
- ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])
181
+ ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
182
+ 'Arr',
183
+ ])
175
184
  : []),
176
185
  fixer.replaceText(match.node, `${arrName}.uniq(${argumentText})`),
177
186
  ];
@@ -254,23 +263,10 @@ const isGlobal = (
254
263
  node: TSESTree.Node,
255
264
  name: string,
256
265
  ): boolean => {
257
- const variable = findVariable(sourceCode.getScope(node), name);
266
+ const variable = ASTUtils.findVariable(sourceCode.getScope(node), name);
258
267
 
259
268
  // Globals from the TypeScript lib are implicit variables with no definition.
260
- return variable === undefined || variable.defs.length === 0;
261
- };
262
-
263
- const findVariable = (
264
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
265
- scope: TSESLint.Scope.Scope,
266
- name: string,
267
- ): TSESLint.Scope.Variable | undefined => {
268
- const variable = scope.set.get(name);
269
-
270
- return (
271
- variable ??
272
- (scope.upper === null ? undefined : findVariable(scope.upper, name))
273
- );
269
+ return variable === null || variable.defs.length === 0;
274
270
  };
275
271
 
276
272
  /**
@@ -3,14 +3,15 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import type { DeepReadonly } from 'ts-type-forge';
7
- import { toArgumentText } from './ast-utils.mjs';
8
- import { brandedNumberTypeNameToFunctionName } from './branded-number-types.mjs';
9
6
  import {
10
- buildImportFixes,
7
+ buildNamedImportFix,
8
+ getArgumentText,
9
+ getImportDeclarations,
11
10
  getNamedImports,
12
- getTsDataForgeImport,
13
- } from './import-utils.mjs';
11
+ } from 'eslint-rule-forge';
12
+ import type { DeepReadonly } from 'ts-type-forge';
13
+ import { brandedNumberTypeNameToFunctionName } from './branded-number-types.mjs';
14
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
14
15
 
15
16
  type Options = readonly [];
16
17
 
@@ -36,7 +37,10 @@ export const preferAsInt: TSESLint.RuleModule<MessageIds, Options> = {
36
37
 
37
38
  const program = sourceCode.ast;
38
39
 
39
- const tsDataForgeImport = getTsDataForgeImport(program);
40
+ const tsDataForgeImports = getImportDeclarations(
41
+ program,
42
+ TS_DATA_FORGE_MODULE,
43
+ );
40
44
 
41
45
  const mut_nodesToFix: {
42
46
  node: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion;
@@ -66,7 +70,7 @@ export const preferAsInt: TSESLint.RuleModule<MessageIds, Options> = {
66
70
  TSAsExpression: collect,
67
71
  TSTypeAssertion: collect,
68
72
  'Program:exit': () => {
69
- const namedImports = getNamedImports(tsDataForgeImport);
73
+ const namedImports = getNamedImports(tsDataForgeImports);
70
74
 
71
75
  // Group nodes by function name to handle imports efficiently
72
76
  const functionNameToNodes = Map.groupBy(
@@ -87,14 +91,17 @@ export const preferAsInt: TSESLint.RuleModule<MessageIds, Options> = {
87
91
  functionName,
88
92
  },
89
93
  fix: (fixer) => {
90
- const replacement = `${functionName}(${toArgumentText(node.expression, sourceCode)})`;
94
+ const replacement = `${functionName}(${getArgumentText(sourceCode, node.expression)})`;
91
95
 
92
96
  // Add import only for the first node of this function and only if not already imported
93
97
  const importFixes =
94
98
  index === 0 && !hasImport
95
- ? buildImportFixes(fixer, program, tsDataForgeImport, [
96
- functionName,
97
- ])
99
+ ? buildNamedImportFix(
100
+ fixer,
101
+ program,
102
+ TS_DATA_FORGE_MODULE,
103
+ [functionName],
104
+ )
98
105
  : [];
99
106
 
100
107
  return [...importFixes, fixer.replaceText(node, replacement)];
@@ -4,14 +4,16 @@ import {
4
4
  type TSESLint,
5
5
  type TSESTree,
6
6
  } from '@typescript-eslint/utils';
7
- import type { ReadonlyRecord } from 'ts-type-forge';
8
- import * as ts from 'typescript';
9
- import { skipTypeWrappers, typeWrapperLayers } from './ast-utils.mjs';
10
7
  import {
11
- buildImportFixes,
8
+ buildNamedImportFix,
9
+ getImportDeclarations,
12
10
  getImportedLocalName,
13
- getTsDataForgeImport,
14
- } from './import-utils.mjs';
11
+ skipTypeWrappers,
12
+ typeWrapperLayers,
13
+ } from 'eslint-rule-forge';
14
+ import type { ReadonlyRecord } from 'ts-type-forge';
15
+ import * as ts from 'typescript';
16
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
15
17
 
16
18
  /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
17
19
 
@@ -188,7 +190,10 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
188
190
 
189
191
  const program = sourceCode.ast;
190
192
 
191
- const tsDataForgeImport = getTsDataForgeImport(program);
193
+ const tsDataForgeImports = getImportDeclarations(
194
+ program,
195
+ TS_DATA_FORGE_MODULE,
196
+ );
192
197
 
193
198
  const parserServices = ESLintUtils.getParserServices(context);
194
199
 
@@ -199,11 +204,14 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
199
204
  * (possibly aliased), a namespace import member, or the canonical `Arr`
200
205
  * (added by the import fix when nothing is imported yet).
201
206
  */
202
- const arrLocalName = getImportedLocalName(tsDataForgeImport, 'Arr');
207
+ const arrLocalName = getImportedLocalName(tsDataForgeImports, 'Arr');
203
208
 
204
- const namespaceLocalName = tsDataForgeImport?.specifiers.find(
205
- (specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,
206
- )?.local.name;
209
+ const namespaceLocalName = tsDataForgeImports
210
+ .flatMap((declaration) => declaration.specifiers)
211
+ .find(
212
+ (specifier) =>
213
+ specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,
214
+ )?.local.name;
207
215
 
208
216
  const arrRef =
209
217
  arrLocalName ??
@@ -837,7 +845,9 @@ export const preferCanonicalArraySlicing: TSESLint.RuleModule<
837
845
  fix: (fixer) => {
838
846
  const importFixes =
839
847
  index === 0 && needsArrImport
840
- ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])
848
+ ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
849
+ 'Arr',
850
+ ])
841
851
  : [];
842
852
 
843
853
  return [
@@ -4,8 +4,12 @@ import {
4
4
  type TSESLint,
5
5
  type TSESTree,
6
6
  } from '@typescript-eslint/utils';
7
- import { skipTypeWrappers } from './ast-utils.mjs';
8
- import { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';
7
+ import {
8
+ getImportDeclarations,
9
+ getImportedLocalName,
10
+ skipTypeWrappers,
11
+ } from 'eslint-rule-forge';
12
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
9
13
 
10
14
  type Options = readonly [];
11
15
 
@@ -143,7 +147,7 @@ export const preferCanonicalLengthCast: TSESLint.RuleModule<
143
147
  const sourceCode = context.sourceCode;
144
148
 
145
149
  const arrLocalName = getImportedLocalName(
146
- getTsDataForgeImport(sourceCode.ast),
150
+ getImportDeclarations(sourceCode.ast, TS_DATA_FORGE_MODULE),
147
151
  'Arr',
148
152
  );
149
153
 
@@ -3,9 +3,14 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
+ import {
7
+ getArgumentText,
8
+ getImportDeclarations,
9
+ getImportedLocalName,
10
+ skipTypeWrappers,
11
+ } from 'eslint-rule-forge';
6
12
  import type { MutableRecord, ReadonlyRecord } from 'ts-type-forge';
7
- import { skipTypeWrappers, toArgumentText } from './ast-utils.mjs';
8
- import { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';
13
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
9
14
  import { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';
10
15
  import { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';
11
16
  import { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';
@@ -117,7 +122,7 @@ export const preferCanonicalLengthGuard: TSESLint.RuleModule<
117
122
  const sourceCode = context.sourceCode;
118
123
 
119
124
  const arrLocalName = getImportedLocalName(
120
- getTsDataForgeImport(sourceCode.ast),
125
+ getImportDeclarations(sourceCode.ast, TS_DATA_FORGE_MODULE),
121
126
  'Arr',
122
127
  );
123
128
 
@@ -154,7 +159,7 @@ export const preferCanonicalLengthGuard: TSESLint.RuleModule<
154
159
  return;
155
160
  }
156
161
 
157
- const arrayText = toArgumentText(array, sourceCode);
162
+ const arrayText = getArgumentText(sourceCode, array);
158
163
 
159
164
  context.report({
160
165
  node,
@@ -3,8 +3,9 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
+ import { buildCalleeResolver, getImportDeclarations } from 'eslint-rule-forge';
6
7
  import type { DeepReadonly, ReadonlyRecord } from 'ts-type-forge';
7
- import { buildCalleeResolver, getTsDataForgeImport } from './import-utils.mjs';
8
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
8
9
 
9
10
  type Options = readonly [];
10
11
 
@@ -121,7 +122,7 @@ export const preferComparisonOverNullishGuard: TSESLint.RuleModule<
121
122
  const sourceCode = context.sourceCode;
122
123
 
123
124
  const resolveGuard = buildCalleeResolver(
124
- getTsDataForgeImport(sourceCode.ast),
125
+ getImportDeclarations(sourceCode.ast, TS_DATA_FORGE_MODULE),
125
126
  );
126
127
 
127
128
  return {
@@ -49,6 +49,22 @@ describe('prefer-comparison-over-nullish-guard', () => {
49
49
  },
50
50
  ],
51
51
  invalid: [
52
+ {
53
+ name: 'the guard imported by a second ts-data-forge declaration',
54
+ code: dedent`
55
+ import type { Optional } from 'ts-data-forge';
56
+ import { isUndefined } from 'ts-data-forge';
57
+ declare const x: string | undefined;
58
+ const y = isUndefined(x);
59
+ `,
60
+ output: dedent`
61
+ import type { Optional } from 'ts-data-forge';
62
+ import { isUndefined } from 'ts-data-forge';
63
+ declare const x: string | undefined;
64
+ const y = x === undefined;
65
+ `,
66
+ errors: [{ messageId: 'preferComparison' }],
67
+ },
52
68
  {
53
69
  name: 'isNotUndefined with explicit argument',
54
70
  code: dedent`
@@ -3,12 +3,13 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import { skipTypeWrappers } from './ast-utils.mjs';
7
6
  import {
8
- buildImportFixes,
7
+ buildNamedImportFix,
8
+ getImportDeclarations,
9
9
  getNamedImports,
10
- getTsDataForgeImport,
11
- } from './import-utils.mjs';
10
+ skipTypeWrappers,
11
+ } from 'eslint-rule-forge';
12
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
12
13
 
13
14
  type Options = readonly [];
14
15
 
@@ -34,7 +35,10 @@ export const preferIsNonNullObject: TSESLint.RuleModule<MessageIds, Options> = {
34
35
 
35
36
  const program = sourceCode.ast;
36
37
 
37
- const tsDataForgeImport = getTsDataForgeImport(program);
38
+ const tsDataForgeImports = getImportDeclarations(
39
+ program,
40
+ TS_DATA_FORGE_MODULE,
41
+ );
38
42
 
39
43
  const mut_nodesToFix: {
40
44
  node: TSESTree.LogicalExpression;
@@ -54,7 +58,7 @@ export const preferIsNonNullObject: TSESLint.RuleModule<MessageIds, Options> = {
54
58
  'Program:exit': () => {
55
59
  // Check if isNonNullObject is already imported
56
60
  const hasIsNonNullObjectImport =
57
- getNamedImports(tsDataForgeImport).includes('isNonNullObject');
61
+ getNamedImports(tsDataForgeImports).includes('isNonNullObject');
58
62
 
59
63
  // Note: We add import only for the first node to avoid conflicts when
60
64
  // multiple fixes try to insert at the same position. This means if the
@@ -73,7 +77,7 @@ export const preferIsNonNullObject: TSESLint.RuleModule<MessageIds, Options> = {
73
77
  // Add import only for the first node and only if not already imported
74
78
  const importFixes =
75
79
  index === 0 && !hasIsNonNullObjectImport
76
- ? buildImportFixes(fixer, program, tsDataForgeImport, [
80
+ ? buildNamedImportFix(fixer, program, TS_DATA_FORGE_MODULE, [
77
81
  'isNonNullObject',
78
82
  ])
79
83
  : [];
@@ -3,14 +3,15 @@ import {
3
3
  type TSESLint,
4
4
  type TSESTree,
5
5
  } from '@typescript-eslint/utils';
6
- import { Arr } from 'ts-data-forge';
7
- import * as ts from 'typescript';
8
- import { typeWrapperLayers } from './ast-utils.mjs';
9
6
  import {
10
- buildImportFixes,
7
+ buildNamedImportFix,
8
+ getImportDeclarations,
11
9
  getNamedImports,
12
- getTsDataForgeImport,
13
- } from './import-utils.mjs';
10
+ typeWrapperLayers,
11
+ } from 'eslint-rule-forge';
12
+ import { Arr } from 'ts-data-forge';
13
+ import * as ts from 'typescript';
14
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
14
15
 
15
16
  type Options = readonly [];
16
17
 
@@ -39,7 +40,10 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
39
40
 
40
41
  const program = sourceCode.ast;
41
42
 
42
- const tsDataForgeImport = getTsDataForgeImport(program);
43
+ const tsDataForgeImports = getImportDeclarations(
44
+ program,
45
+ TS_DATA_FORGE_MODULE,
46
+ );
43
47
 
44
48
  const services = sourceCode.parserServices;
45
49
 
@@ -141,7 +145,7 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
141
145
  },
142
146
 
143
147
  'Program:exit': () => {
144
- const namedImports = getNamedImports(tsDataForgeImport);
148
+ const namedImports = getNamedImports(tsDataForgeImports);
145
149
 
146
150
  const rewrites = mut_nodesToFix.map((entry) => ({
147
151
  ...entry,
@@ -186,10 +190,10 @@ export const preferIsRecordAndHasKey: TSESLint.RuleModule<MessageIds, Options> =
186
190
  // otherwise and only one of them would survive a pass.
187
191
  const importFixes =
188
192
  index === 0 && importsToAdd.length > 0
189
- ? buildImportFixes(
193
+ ? buildNamedImportFix(
190
194
  fixer,
191
195
  program,
192
- tsDataForgeImport,
196
+ TS_DATA_FORGE_MODULE,
193
197
  importsToAdd,
194
198
  )
195
199
  : [];