eslint-plugin-ts-data-forge 0.9.0 → 0.10.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 (60) hide show
  1. package/README.md +1 -0
  2. package/dist/plugin.d.mts +3 -1
  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 +16 -1
  7. package/dist/rule-types.d.mts.map +1 -1
  8. package/dist/rule-types.mjs.map +1 -1
  9. package/dist/rules/ast-utils.d.mts +0 -16
  10. package/dist/rules/ast-utils.d.mts.map +1 -1
  11. package/dist/rules/ast-utils.mjs +0 -36
  12. package/dist/rules/ast-utils.mjs.map +1 -1
  13. package/dist/rules/no-side-effect-import.d.mts +1 -8
  14. package/dist/rules/no-side-effect-import.d.mts.map +1 -1
  15. package/dist/rules/no-side-effect-import.mjs +7 -43
  16. package/dist/rules/no-side-effect-import.mjs.map +1 -1
  17. package/dist/rules/prefer-arr-is-array.d.mts +1 -5
  18. package/dist/rules/prefer-arr-is-array.d.mts.map +1 -1
  19. package/dist/rules/prefer-arr-is-array.mjs +5 -83
  20. package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
  21. package/dist/rules/prefer-is-non-null-object.d.mts +1 -5
  22. package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
  23. package/dist/rules/prefer-is-non-null-object.mjs +4 -184
  24. package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
  25. package/dist/rules/prefer-is-record-and-has-key.d.mts +1 -5
  26. package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
  27. package/dist/rules/prefer-is-record-and-has-key.mjs +4 -219
  28. package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
  29. package/dist/rules/prefer-num-safe-parse-float.d.mts +1 -15
  30. package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
  31. package/dist/rules/prefer-num-safe-parse-float.mjs +6 -123
  32. package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
  33. package/dist/rules/prefer-num-safe-parse-int.d.mts +1 -13
  34. package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
  35. package/dist/rules/prefer-num-safe-parse-int.mjs +6 -121
  36. package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
  37. package/dist/rules/prefer-pipe-map-nullable.d.mts +24 -0
  38. package/dist/rules/prefer-pipe-map-nullable.d.mts.map +1 -0
  39. package/dist/rules/prefer-pipe-map-nullable.mjs +266 -0
  40. package/dist/rules/prefer-pipe-map-nullable.mjs.map +1 -0
  41. package/dist/rules/rules.d.mts +2 -1
  42. package/dist/rules/rules.d.mts.map +1 -1
  43. package/dist/rules/rules.mjs +2 -0
  44. package/dist/rules/rules.mjs.map +1 -1
  45. package/package.json +6 -6
  46. package/src/plugin.mts +1 -0
  47. package/src/rule-types.mts +17 -1
  48. package/src/rules/ast-utils.mts +0 -51
  49. package/src/rules/no-side-effect-import.mts +7 -61
  50. package/src/rules/prefer-arr-is-array.mts +5 -122
  51. package/src/rules/prefer-arr-is-array.test.mts +50 -7
  52. package/src/rules/prefer-is-non-null-object.mts +4 -274
  53. package/src/rules/prefer-is-record-and-has-key.mts +4 -318
  54. package/src/rules/prefer-num-safe-parse-float.mts +6 -193
  55. package/src/rules/prefer-num-safe-parse-float.test.mts +18 -0
  56. package/src/rules/prefer-num-safe-parse-int.mts +6 -192
  57. package/src/rules/prefer-num-safe-parse-int.test.mts +18 -0
  58. package/src/rules/prefer-pipe-map-nullable.mts +391 -0
  59. package/src/rules/prefer-pipe-map-nullable.test.mts +470 -0
  60. package/src/rules/rules.mts +2 -0
@@ -0,0 +1,266 @@
1
+ import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
2
+ import { buildCalleeResolver, getArgumentText, getImportDeclarations, hasForeignBinding, isTypeWrapper, skipTypeWrappers, } from 'eslint-rule-forge';
3
+ import { Arr } from 'ts-data-forge';
4
+ import * as ts from 'typescript';
5
+ import { TS_DATA_FORGE_MODULE } from './constants.mjs';
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+ export const preferPipeMapNullable = {
25
+ meta: {
26
+ type: 'suggestion',
27
+ docs: {
28
+ description: 'Replace `pipe(x).map(mapNullable(fn))` with `pipe(x).mapNullable(fn)`.',
29
+ },
30
+ fixable: 'code',
31
+ hasSuggestions: true,
32
+ schema: [],
33
+ messages: {
34
+ useMapNullable: 'Replace `.map({{mapNullableName}}(…))` with `.mapNullable(…)`.',
35
+ suggestMapNullable: 'Replace `.map({{mapNullableName}}(…))` with `.mapNullable(…)` (the inferred type may be widened, or a comment or type assertion dropped).',
36
+ },
37
+ },
38
+ create: (context) => {
39
+ const sourceCode = context.sourceCode;
40
+ const program = sourceCode.ast;
41
+ const resolveTsDataForge = buildCalleeResolver(getImportDeclarations(program, TS_DATA_FORGE_MODULE));
42
+ const pipeResolvers = PIPE_MODULES.map((moduleName) => [
43
+ moduleName,
44
+ buildCalleeResolver(getImportDeclarations(program, moduleName)),
45
+ ]);
46
+ const services = sourceCode.parserServices;
47
+ const checker = services?.program?.getTypeChecker();
48
+ const esTreeNodeToTSNodeMap = services?.esTreeNodeToTSNodeMap;
49
+
50
+
51
+
52
+
53
+ const calls = (resolve,
54
+
55
+ callee, name, moduleName) => {
56
+ if (resolve(callee)?.canonicalName !== name) {
57
+ return false;
58
+ }
59
+ const unwrapped = skipTypeWrappers(callee);
60
+
61
+ const binding = unwrapped.type === AST_NODE_TYPES.MemberExpression
62
+ ? skipTypeWrappers(unwrapped.object)
63
+ : unwrapped;
64
+ return (binding.type === AST_NODE_TYPES.Identifier &&
65
+ !hasForeignBinding(sourceCode, binding, binding.name, moduleName));
66
+ };
67
+
68
+
69
+
70
+
71
+ const isPipe = (
72
+
73
+ wrapped) => {
74
+ const node = skipTypeWrappers(wrapped);
75
+ if (node.type !== AST_NODE_TYPES.CallExpression || node.optional) {
76
+ return false;
77
+ }
78
+ if (pipeResolvers.some(([moduleName, resolve]) => calls(resolve, node.callee, 'pipe', moduleName))) {
79
+ return true;
80
+ }
81
+ const { callee } = node;
82
+ return (callee.type === AST_NODE_TYPES.MemberExpression &&
83
+ !callee.computed &&
84
+ !callee.optional &&
85
+ callee.property.type === AST_NODE_TYPES.Identifier &&
86
+ PIPE_CHAIN_METHODS.has(callee.property.name) &&
87
+ isPipe(callee.object));
88
+ };
89
+
90
+
91
+
92
+
93
+ const keepsResultType = (
94
+
95
+ mapNullableCall,
96
+
97
+ mapFn) => {
98
+
99
+ const fn = skipSatisfies(mapFn);
100
+ if ((fn.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
101
+ fn.type !== AST_NODE_TYPES.FunctionExpression) ||
102
+ fn.returnType !== undefined) {
103
+
104
+ return true;
105
+ }
106
+ if (checker === undefined || esTreeNodeToTSNodeMap === undefined) {
107
+ return false;
108
+ }
109
+
110
+
111
+
112
+
113
+ const signatures = checker
114
+ .getTypeAtLocation(esTreeNodeToTSNodeMap.get(mapNullableCall))
115
+ .getCallSignatures();
116
+ return (Arr.isFixedLengthTuple(1, signatures) &&
117
+ !hasConstInferredPart(checker, checker.getReturnTypeOfSignature(signatures[0]), 0));
118
+ };
119
+ return {
120
+ CallExpression: (node) => {
121
+
122
+ if (node.optional ||
123
+ node.typeArguments !== undefined ||
124
+ node.callee.type !== AST_NODE_TYPES.MemberExpression ||
125
+ node.callee.computed ||
126
+ node.callee.optional ||
127
+ node.callee.property.type !== AST_NODE_TYPES.Identifier ||
128
+ node.callee.property.name !== 'map' ||
129
+ !Arr.isFixedLengthTuple(1, node.arguments)) {
130
+ return;
131
+ }
132
+ const mapNullableCall = node.arguments[0];
133
+ if (mapNullableCall.type !== AST_NODE_TYPES.CallExpression ||
134
+ mapNullableCall.optional ||
135
+
136
+
137
+ mapNullableCall.typeArguments !== undefined ||
138
+
139
+
140
+ !Arr.isFixedLengthTuple(1, mapNullableCall.arguments)) {
141
+ return;
142
+ }
143
+ const mapFn = mapNullableCall.arguments[0];
144
+ if (mapFn.type === AST_NODE_TYPES.SpreadElement ||
145
+ !calls(resolveTsDataForge, mapNullableCall.callee, 'mapNullable', TS_DATA_FORGE_MODULE) ||
146
+ !isPipe(node.callee.object)) {
147
+ return;
148
+ }
149
+
150
+ const droppedRanges = [
151
+ [mapNullableCall.range[0], mapFn.range[0]],
152
+ [mapFn.range[1], mapNullableCall.range[1]],
153
+ ];
154
+ const dropsComment = sourceCode
155
+ .getCommentsInside(mapNullableCall)
156
+ .some((comment) => droppedRanges.some(([start, end]) => start <= comment.range[0] && comment.range[1] <= end));
157
+ const autofixable = !dropsComment &&
158
+ !isTypeWrapper(mapNullableCall.callee) &&
159
+ keepsResultType(mapNullableCall, mapFn);
160
+ const mapProperty = node.callee.property;
161
+ const fix = (fixer) => [
162
+ fixer.replaceText(mapProperty, 'mapNullable'),
163
+ fixer.replaceText(mapNullableCall, getArgumentText(sourceCode, mapFn)),
164
+ ];
165
+ const data = {
166
+ mapNullableName: sourceCode.getText(mapNullableCall.callee),
167
+ };
168
+ if (autofixable) {
169
+ context.report({
170
+ node,
171
+ messageId: 'useMapNullable',
172
+ data,
173
+ fix,
174
+ });
175
+ }
176
+ else {
177
+ context.report({
178
+ node,
179
+ messageId: 'useMapNullable',
180
+ data,
181
+ suggest: [{ messageId: 'suggestMapNullable', data, fix }],
182
+ });
183
+ }
184
+ },
185
+ };
186
+ },
187
+ defaultOptions: [],
188
+ };
189
+
190
+
191
+
192
+
193
+
194
+ const PIPE_MODULES = [TS_DATA_FORGE_MODULE, 'ts-std-forge'];
195
+
196
+ const PIPE_CHAIN_METHODS = new Set([
197
+ 'map',
198
+ 'mapNullable',
199
+ 'mapOptional',
200
+ ]);
201
+
202
+ const literalTypeFlags = ts.TypeFlags.StringLiteral |
203
+ ts.TypeFlags.NumberLiteral |
204
+ ts.TypeFlags.BigIntLiteral |
205
+ ts.TypeFlags.BooleanLiteral |
206
+ ts.TypeFlags.EnumLiteral |
207
+ ts.TypeFlags.TemplateLiteral |
208
+ ts.TypeFlags.UniqueESSymbol;
209
+
210
+ const MAX_DEPTH = 4;
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+ const hasConstInferredPart = (
220
+
221
+ checker,
222
+
223
+ type, depth) => {
224
+ if (depth > MAX_DEPTH) {
225
+ return true;
226
+ }
227
+
228
+ if ((type.flags & ts.TypeFlags.Boolean) !== 0) {
229
+ return false;
230
+ }
231
+ if (type.isUnion() || type.isIntersection()) {
232
+ return type.types.some((t) => hasConstInferredPart(checker, t, depth + 1));
233
+ }
234
+ if ((type.flags & literalTypeFlags) !== 0 || checker.isTupleType(type)) {
235
+ return true;
236
+ }
237
+ if ((type.flags & ts.TypeFlags.Object) === 0) {
238
+ return false;
239
+ }
240
+
241
+ const { objectFlags } = type;
242
+
243
+ if ((objectFlags & ts.ObjectFlags.Reference) !== 0 &&
244
+ checker
245
+
246
+ .getTypeArguments(type)
247
+ .some((t) => hasConstInferredPart(checker, t, depth + 1))) {
248
+ return true;
249
+ }
250
+ return (
251
+
252
+
253
+ (objectFlags &
254
+ (ts.ObjectFlags.ObjectLiteral | ts.ObjectFlags.Anonymous)) !==
255
+ 0 &&
256
+ type
257
+ .getProperties()
258
+ .some((property) => hasConstInferredPart(checker, checker.getTypeOfSymbol(property), depth + 1)));
259
+ };
260
+
261
+ const skipSatisfies = (
262
+
263
+ node) => node.type === AST_NODE_TYPES.TSSatisfiesExpression
264
+ ? skipSatisfies(node.expression)
265
+ : node;
266
+ //# sourceMappingURL=prefer-pipe-map-nullable.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer-pipe-map-nullable.mjs","sourceRoot":"","sources":["../../src/rules/prefer-pipe-map-nullable.mts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ export declare const tsDataForgeRules: {
3
3
  readonly 'prefer-canonical-length-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalGuard" | "useIsBoundedLengthArray" | "useIsFixedLengthArray" | "useIsMaxLengthArray" | "useIsMinLengthArray" | "useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
4
4
  readonly 'prefer-canonical-length-cast': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalCast", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
5
5
  readonly 'prefer-arr-generate': import("@typescript-eslint/utils/ts-eslint").RuleModule<"suggestArrGenerate" | "useArrGenerate", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
6
- readonly 'prefer-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
6
+ readonly 'prefer-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
7
  readonly 'prefer-arr-scan': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferArrScanAccumulate" | "preferArrScanPrefix", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
8
  readonly 'prefer-arr-sum': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrSum" | "useArrSumBy", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
9
9
  readonly 'prefer-arr-uniq': import("@typescript-eslint/utils/ts-eslint").RuleModule<"suggestArrUniq" | "useArrUniq", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
@@ -19,5 +19,6 @@ export declare const tsDataForgeRules: {
19
19
  }> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
20
20
  readonly 'prefer-comparison-over-nullish-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferComparison", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
21
21
  readonly 'prefer-obj-over-entries-round-trip': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useObjFilter" | "useObjFilterMap" | "useObjMap", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
22
+ readonly 'prefer-pipe-map-nullable': import("@typescript-eslint/utils/ts-eslint").RuleModule<"suggestMapNullable" | "useMapNullable", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
22
23
  };
23
24
  //# sourceMappingURL=rules.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;CAmBa,CAAC"}
1
+ {"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;CAoBa,CAAC"}
@@ -15,6 +15,7 @@ import { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';
15
15
  import { preferNumSafeParseFloat } from './prefer-num-safe-parse-float.mjs';
16
16
  import { preferNumSafeParseInt } from './prefer-num-safe-parse-int.mjs';
17
17
  import { preferObjOverEntriesRoundTrip } from './prefer-obj-over-entries-round-trip.mjs';
18
+ import { preferPipeMapNullable } from './prefer-pipe-map-nullable.mjs';
18
19
  import { preferRangeForLoop } from './prefer-range-for-loop.mjs';
19
20
  export const tsDataForgeRules = {
20
21
  'prefer-canonical-array-slicing': preferCanonicalArraySlicing,
@@ -35,5 +36,6 @@ export const tsDataForgeRules = {
35
36
  'no-unnecessary-type-guard': noUnnecessaryTypeGuard,
36
37
  'prefer-comparison-over-nullish-guard': preferComparisonOverNullishGuard,
37
38
  'prefer-obj-over-entries-round-trip': preferObjOverEntriesRoundTrip,
39
+ 'prefer-pipe-map-nullable': preferPipeMapNullable,
38
40
  };
39
41
  //# sourceMappingURL=rules.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"rules.mjs","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4CAA4C,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gCAAgC,EAAE,2BAA2B;IAC7D,+BAA+B,EAAE,0BAA0B;IAC3D,8BAA8B,EAAE,yBAAyB;IACzD,qBAAqB,EAAE,iBAAiB;IACxC,qBAAqB,EAAE,gBAAgB;IACvC,iBAAiB,EAAE,aAAa;IAChC,gBAAgB,EAAE,YAAY;IAC9B,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,WAAW;IAC5B,2BAA2B,EAAE,qBAAqB;IAClD,uBAAuB,EAAE,kBAAkB;IAC3C,8BAA8B,EAAE,uBAAuB;IACvD,2BAA2B,EAAE,qBAAqB;IAClD,6BAA6B,EAAE,uBAAuB;IACtD,uBAAuB,EAAE,kBAAkB;IAC3C,2BAA2B,EAAE,sBAAsB;IACnD,sCAAsC,EAAE,gCAAgC;IACxE,oCAAoC,EAAE,6BAA6B;CAC3B,CAAC"}
1
+ {"version":3,"file":"rules.mjs","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,gCAAgC,EAAE,MAAM,4CAA4C,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gCAAgC,EAAE,2BAA2B;IAC7D,+BAA+B,EAAE,0BAA0B;IAC3D,8BAA8B,EAAE,yBAAyB;IACzD,qBAAqB,EAAE,iBAAiB;IACxC,qBAAqB,EAAE,gBAAgB;IACvC,iBAAiB,EAAE,aAAa;IAChC,gBAAgB,EAAE,YAAY;IAC9B,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,WAAW;IAC5B,2BAA2B,EAAE,qBAAqB;IAClD,uBAAuB,EAAE,kBAAkB;IAC3C,8BAA8B,EAAE,uBAAuB;IACvD,2BAA2B,EAAE,qBAAqB;IAClD,6BAA6B,EAAE,uBAAuB;IACtD,uBAAuB,EAAE,kBAAkB;IAC3C,2BAA2B,EAAE,sBAAsB;IACnD,sCAAsC,EAAE,gCAAgC;IACxE,oCAAoC,EAAE,6BAA6B;IACnE,0BAA0B,EAAE,qBAAqB;CACT,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-ts-data-forge",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "private": false,
5
5
  "description": "ESLint rules that steer code toward ts-data-forge idioms.",
6
6
  "keywords": [
@@ -35,17 +35,17 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@typescript-eslint/utils": "^8.71.1",
38
- "eslint-rule-forge": "^0.2.0",
39
- "ts-data-forge": "14.8.0",
38
+ "eslint-rule-forge": "^0.3.0",
39
+ "ts-data-forge": "14.9.0",
40
40
  "ts-type-forge": "~9.2.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@typescript-eslint/parser": "8.71.1",
44
44
  "@typescript-eslint/rule-tester": "8.71.1",
45
45
  "dedent": "1.7.2",
46
- "eslint-config-typed": "5.19.0",
47
- "eslint-plugin-ts-fortress": "1.3.4",
48
- "eslint-plugin-ts-type-forge": "0.8.9",
46
+ "eslint-config-typed": "5.19.1",
47
+ "eslint-plugin-ts-fortress": "1.3.6",
48
+ "eslint-plugin-ts-type-forge": "0.8.11",
49
49
  "prettier": "3.9.9",
50
50
  "prettier-plugin-organize-imports": "4.3.0",
51
51
  "prettier-plugin-packagejson": "3.0.2",
package/src/plugin.mts CHANGED
@@ -28,6 +28,7 @@ const recommendedRules = {
28
28
  'ts-data-forge/no-unnecessary-type-guard': 'error',
29
29
  'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',
30
30
  'ts-data-forge/prefer-obj-over-entries-round-trip': 'error',
31
+ 'ts-data-forge/prefer-pipe-map-nullable': 'error',
31
32
  } as const satisfies ReadonlyRecord<
32
33
  `ts-data-forge/${keyof typeof tsDataForgeRules}`,
33
34
  'error'
@@ -208,7 +208,7 @@ namespace PreferIsRecordAndHasKey {
208
208
  }
209
209
 
210
210
  /**
211
- * @description Replace `parseInt(x, 10)` with `Result.unwrapOkOr(Num.safeParseInt(x), Number.NaN)` from ts-data-forge.
211
+ * @description Replace `parseInt(x)` or `Number.parseInt(x, 10)` with `Result.unwrapOkOr(Num.safeParseInt(x), Number.NaN)` from ts-data-forge.
212
212
  *
213
213
  * ```md
214
214
  * | key | value |
@@ -325,6 +325,21 @@ namespace PreferObjOverEntriesRoundTrip {
325
325
  export type RuleEntry = Linter.StringSeverity;
326
326
  }
327
327
 
328
+ /**
329
+ * @description Replace `pipe(x).map(mapNullable(fn))` with `pipe(x).mapNullable(fn)`.
330
+ *
331
+ * ```md
332
+ * | key | value |
333
+ * | :--------- | :--------- |
334
+ * | type | suggestion |
335
+ * | deprecated | false |
336
+ * | fixable | code |
337
+ * ```
338
+ */
339
+ namespace PreferPipeMapNullable {
340
+ export type RuleEntry = Linter.StringSeverity;
341
+ }
342
+
328
343
  export type EslintTsDataForgeRules = Readonly<{
329
344
  'ts-data-forge/prefer-canonical-array-slicing': PreferCanonicalArraySlicing.RuleEntry;
330
345
  'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
@@ -344,6 +359,7 @@ export type EslintTsDataForgeRules = Readonly<{
344
359
  'ts-data-forge/no-unnecessary-type-guard': NoUnnecessaryTypeGuard.RuleEntry;
345
360
  'ts-data-forge/prefer-comparison-over-nullish-guard': PreferComparisonOverNullishGuard.RuleEntry;
346
361
  'ts-data-forge/prefer-obj-over-entries-round-trip': PreferObjOverEntriesRoundTrip.RuleEntry;
362
+ 'ts-data-forge/prefer-pipe-map-nullable': PreferPipeMapNullable.RuleEntry;
347
363
  }>;
348
364
 
349
365
  export type EslintTsDataForgeRulesOption = Readonly<{
@@ -10,7 +10,6 @@ import {
10
10
  typeWrapperLayers,
11
11
  } from 'eslint-rule-forge';
12
12
  import type { DeepReadonly } from 'ts-type-forge';
13
- import type * as ts from 'typescript';
14
13
 
15
14
  const isIntegerLiteral = (node: DeepReadonly<TSESTree.Expression>): boolean =>
16
15
  node.type === AST_NODE_TYPES.Literal &&
@@ -265,53 +264,3 @@ const isVariableDefinition = (
265
264
  def: TSESLint.Scope.Definition,
266
265
  ): def is TSESLint.Scope.Definition & VariableDefinition =>
267
266
  (def.type as string) === 'Variable';
268
-
269
- /**
270
- * Whether `node`, read through its type wrappers, is the identifier `name`:
271
- * `Number`, `(Number as NumberConstructor)` and `Number!` are the same object.
272
- */
273
- export const isIdentifierNamed = (
274
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
275
- node: TSESTree.Node,
276
- name: string,
277
- ): boolean => {
278
- const unwrapped = skipTypeWrappers(node);
279
-
280
- return (
281
- unwrapped.type === AST_NODE_TYPES.Identifier && unwrapped.name === name
282
- );
283
- };
284
-
285
- /**
286
- * Whether `holds` is true of the type of every layer of type wrappers around
287
- * `node` (see {@link typeWrapperLayers}), outermost to innermost; `false`
288
- * without type information. A widening cast (`s as string | number`) is the
289
- * author saying the value may be more than it is, and is respected; a
290
- * narrowing one (`s!`, `v as unknown as string`) cannot vouch for what the
291
- * value's own type does not, and the fix, which keeps the wrapper, has to
292
- * type-check at every layer too.
293
- */
294
- export const typeHoldsAtEveryLayer = (
295
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
296
- sourceCode: TSESLint.SourceCode,
297
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
298
- node: TSESTree.Node,
299
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
300
- holds: (type: ts.Type, typeChecker: ts.TypeChecker) => boolean,
301
- ): boolean => {
302
- const services = sourceCode.parserServices;
303
-
304
- const checker = services?.program?.getTypeChecker();
305
-
306
- if (checker === undefined) {
307
- return false;
308
- }
309
-
310
- return typeWrapperLayers(node).every((layer) => {
311
- const tsNode = services?.esTreeNodeToTSNodeMap?.get(layer);
312
-
313
- return (
314
- tsNode !== undefined && holds(checker.getTypeAtLocation(tsNode), checker)
315
- );
316
- });
317
- };
@@ -1,11 +1,6 @@
1
- import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
- import { Arr } from 'ts-data-forge';
1
+ import { createNoSideEffectImport } from 'eslint-rule-forge';
3
2
  import { TS_DATA_FORGE_MODULE } from './constants.mjs';
4
3
 
5
- type Options = readonly [];
6
-
7
- type MessageIds = 'removeSideEffectImport';
8
-
9
4
  /**
10
5
  * `import 'ts-data-forge';` binds nothing and runs nothing: every export is a
11
6
  * pure value or function, and the package declares `sideEffects: false`, which
@@ -13,59 +8,10 @@ type MessageIds = 'removeSideEffectImport';
13
8
  * a side-effect import in the emitted JavaScript — that is the one kind it
14
9
  * never elides — so what is left is a module loaded for no reason, and a
15
10
  * bundler that trusts the flag drops it anyway.
16
- *
17
- * The rule matches the bare specifier exactly; an import of some other module
18
- * is none of its business.
19
11
  */
20
- export const noSideEffectImport: TSESLint.RuleModule<MessageIds, Options> = {
21
- meta: {
22
- type: 'suggestion',
23
- docs: {
24
- description: `Remove a side-effect-only \`import '${TS_DATA_FORGE_MODULE}';\`, which binds no name and, the package being \`sideEffects: false\`, does nothing.`,
25
- },
26
- fixable: 'code',
27
- schema: [],
28
- messages: {
29
- removeSideEffectImport: `\`import '${TS_DATA_FORGE_MODULE}';\` binds no name, and \`${TS_DATA_FORGE_MODULE}\` declares \`sideEffects: false\`, so loading it does nothing. Remove it, and import what you need by name.`,
30
- },
31
- },
32
-
33
- create: (context) => ({
34
- ImportDeclaration: (node) => {
35
- if (
36
- node.source.value !== TS_DATA_FORGE_MODULE ||
37
- !Arr.isEmpty(node.specifiers)
38
- ) {
39
- return;
40
- }
41
-
42
- context.report({
43
- node,
44
- messageId: 'removeSideEffectImport',
45
- fix: (fixer) => removeStatement(fixer, context.sourceCode, node),
46
- });
47
- },
48
- }),
49
- defaultOptions: [],
50
- } as const;
51
-
52
- /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
53
-
54
- /**
55
- * Removes a statement along with the rest of its line, so that deleting it does
56
- * not leave a blank line where it was.
57
- */
58
- const removeStatement = (
59
- fixer: TSESLint.RuleFixer,
60
- sourceCode: TSESLint.SourceCode,
61
- node: TSESTree.Node,
62
- ): TSESLint.RuleFix => {
63
- const restOfLine = /^[^\S\n]*\n/u.exec(
64
- sourceCode.getText().slice(node.range[1]),
65
- );
66
-
67
- return fixer.removeRange([
68
- node.range[0],
69
- node.range[1] + (restOfLine?.[0].length ?? 0),
70
- ]);
71
- };
12
+ export const noSideEffectImport = createNoSideEffectImport({
13
+ moduleName: TS_DATA_FORGE_MODULE,
14
+ type: 'suggestion',
15
+ description: `Remove a side-effect-only \`import '${TS_DATA_FORGE_MODULE}';\`, which binds no name and, the package being \`sideEffects: false\`, does nothing.`,
16
+ message: `\`import '${TS_DATA_FORGE_MODULE}';\` binds no name, and \`${TS_DATA_FORGE_MODULE}\` declares \`sideEffects: false\`, so loading it does nothing. Remove it, and import what you need by name.`,
17
+ });