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.
- package/README.md +3 -3
- package/dist/plugin.d.mts +2 -2
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +5 -5
- package/dist/rules/ast-utils.d.mts +8 -9
- package/dist/rules/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +3 -4
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/import-binding.d.mts +38 -0
- package/dist/rules/import-binding.d.mts.map +1 -0
- package/dist/rules/import-binding.mjs +53 -0
- package/dist/rules/import-binding.mjs.map +1 -0
- package/dist/rules/mutable-result.mjs +37 -37
- package/dist/rules/mutable-result.mjs.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.d.mts.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.mjs +146 -143
- package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
- package/dist/rules/prefer-arr-generate.d.mts +2 -2
- package/dist/rules/prefer-arr-generate.d.mts.map +1 -1
- package/dist/rules/prefer-arr-generate.mjs +146 -136
- package/dist/rules/prefer-arr-generate.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs +14 -16
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs +15 -17
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs +14 -16
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs +14 -16
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.mjs +14 -16
- package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
- package/dist/rules/prefer-arr-scan.d.mts.map +1 -1
- package/dist/rules/prefer-arr-scan.mjs +9 -18
- package/dist/rules/prefer-arr-scan.mjs.map +1 -1
- package/dist/rules/prefer-arr-sum.d.mts.map +1 -1
- package/dist/rules/prefer-arr-sum.mjs +16 -21
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-arr-uniq.d.mts.map +1 -1
- package/dist/rules/prefer-arr-uniq.mjs +38 -43
- package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
- package/dist/rules/prefer-as-int.d.mts.map +1 -1
- package/dist/rules/prefer-as-int.mjs +19 -26
- package/dist/rules/prefer-as-int.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +49 -16
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.mjs +15 -15
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.d.mts +1 -1
- package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.d.mts +1 -1
- package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.d.mts.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs +100 -99
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
- package/dist/rules/prefer-pipe-map-nullable.d.mts +2 -2
- package/dist/rules/prefer-pipe-map-nullable.d.mts.map +1 -1
- package/dist/rules/prefer-pipe-map-nullable.mjs +2 -4
- package/dist/rules/prefer-pipe-map-nullable.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.d.mts.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs +18 -24
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/dist/rules/rules.d.mts +2 -2
- package/package.json +6 -6
- package/src/plugin.mts +4 -3
- package/src/rule-types.mts +5 -5
- package/src/rules/ast-utils.mts +14 -15
- package/src/rules/import-binding.mts +94 -0
- package/src/rules/mutable-result.mts +56 -56
- package/src/rules/no-unnecessary-type-guard.mts +203 -207
- package/src/rules/no-unnecessary-type-guard.test.mts +62 -2
- package/src/rules/prefer-arr-generate.mts +222 -214
- package/src/rules/prefer-arr-generate.test.mts +490 -133
- package/src/rules/prefer-arr-is-bounded-length-array.mts +22 -33
- package/src/rules/prefer-arr-is-fixed-length-array.mts +26 -38
- package/src/rules/prefer-arr-is-fixed-length-array.test.mts +16 -7
- package/src/rules/prefer-arr-is-max-length-array.mts +23 -36
- package/src/rules/prefer-arr-is-max-length-array.test.mts +16 -7
- package/src/rules/prefer-arr-is-min-length-array.mts +23 -36
- package/src/rules/prefer-arr-is-min-length-array.test.mts +16 -7
- package/src/rules/prefer-arr-is-non-empty.mts +17 -34
- package/src/rules/prefer-arr-is-non-empty.test.mts +110 -0
- package/src/rules/prefer-arr-scan.mts +17 -42
- package/src/rules/prefer-arr-scan.test.mts +55 -0
- package/src/rules/prefer-arr-sum.mts +22 -36
- package/src/rules/prefer-arr-uniq.mts +51 -62
- package/src/rules/prefer-arr-uniq.test.mts +46 -20
- package/src/rules/prefer-as-int.mts +27 -52
- package/src/rules/prefer-as-int.test.mts +79 -29
- package/src/rules/prefer-canonical-array-slicing.mts +101 -40
- package/src/rules/prefer-canonical-array-slicing.test.mts +133 -34
- package/src/rules/prefer-canonical-length-cast.mts +29 -29
- package/src/rules/prefer-canonical-length-guard.mts +2 -2
- package/src/rules/prefer-num-safe-parse-float.test.mts +255 -94
- package/src/rules/prefer-num-safe-parse-int.test.mts +212 -76
- package/src/rules/prefer-obj-over-entries-round-trip.mts +152 -170
- package/src/rules/prefer-obj-over-entries-round-trip.test.mts +48 -6
- package/src/rules/prefer-pipe-map-nullable.mts +17 -19
- package/src/rules/prefer-range-for-loop.mts +37 -61
- package/src/rules/prefer-range-for-loop.test.mts +2 -2
|
@@ -572,12 +572,20 @@ describe('prefer-canonical-array-slicing', () => {
|
|
|
572
572
|
const ys = xs.slice(1);
|
|
573
573
|
const zs = xs.slice(0, -1);
|
|
574
574
|
`,
|
|
575
|
-
output:
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
575
|
+
output: [
|
|
576
|
+
dedent`
|
|
577
|
+
import { Arr } from 'ts-data-forge';
|
|
578
|
+
declare const xs: readonly number[];
|
|
579
|
+
const ys = Arr.tail(xs);
|
|
580
|
+
const zs = xs.slice(0, -1);
|
|
581
|
+
`,
|
|
582
|
+
dedent`
|
|
583
|
+
import { Arr } from 'ts-data-forge';
|
|
584
|
+
declare const xs: readonly number[];
|
|
585
|
+
const ys = Arr.tail(xs);
|
|
586
|
+
const zs = Arr.butLast(xs);
|
|
587
|
+
`,
|
|
588
|
+
],
|
|
581
589
|
errors: [
|
|
582
590
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
583
591
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
@@ -629,13 +637,22 @@ describe('prefer-canonical-array-slicing through type wrappers', () => {
|
|
|
629
637
|
const b = xs.slice(0 satisfies number, -1 as number);
|
|
630
638
|
const c = xs.toSpliced(2 satisfies number);
|
|
631
639
|
`,
|
|
632
|
-
output:
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
640
|
+
output: [
|
|
641
|
+
dedent`
|
|
642
|
+
import { Arr } from 'ts-data-forge';
|
|
643
|
+
declare const xs: readonly number[];
|
|
644
|
+
const a = Arr.tail(xs);
|
|
645
|
+
const b = xs.slice(0 satisfies number, -1 as number);
|
|
646
|
+
const c = xs.toSpliced(2 satisfies number);
|
|
647
|
+
`,
|
|
648
|
+
dedent`
|
|
649
|
+
import { Arr } from 'ts-data-forge';
|
|
650
|
+
declare const xs: readonly number[];
|
|
651
|
+
const a = Arr.tail(xs);
|
|
652
|
+
const b = Arr.butLast(xs);
|
|
653
|
+
const c = Arr.take(xs, 2);
|
|
654
|
+
`,
|
|
655
|
+
],
|
|
639
656
|
errors: [
|
|
640
657
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
641
658
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
@@ -663,13 +680,22 @@ describe('prefer-canonical-array-slicing through type wrappers', () => {
|
|
|
663
680
|
const ys = (xs satisfies readonly number[]).slice(1);
|
|
664
681
|
const zs = [...xs!, v!];
|
|
665
682
|
`,
|
|
666
|
-
output:
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
683
|
+
output: [
|
|
684
|
+
dedent`
|
|
685
|
+
import { Arr } from 'ts-data-forge';
|
|
686
|
+
declare const xs: readonly number[];
|
|
687
|
+
declare const v: number | undefined;
|
|
688
|
+
const ys = Arr.tail(xs satisfies readonly number[]);
|
|
689
|
+
const zs = [...xs!, v!];
|
|
690
|
+
`,
|
|
691
|
+
dedent`
|
|
692
|
+
import { Arr } from 'ts-data-forge';
|
|
693
|
+
declare const xs: readonly number[];
|
|
694
|
+
declare const v: number | undefined;
|
|
695
|
+
const ys = Arr.tail(xs satisfies readonly number[]);
|
|
696
|
+
const zs = Arr.toPushed(xs!, v!);
|
|
697
|
+
`,
|
|
698
|
+
],
|
|
673
699
|
errors: [
|
|
674
700
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
675
701
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
@@ -697,14 +723,24 @@ describe('prefer-canonical-array-slicing through type wrappers', () => {
|
|
|
697
723
|
const c = xs.filter((_, i) => i < (xs.length satisfies number) - 2);
|
|
698
724
|
const d = xs.toSpliced(0, xs!.length - 2);
|
|
699
725
|
`,
|
|
700
|
-
output:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
726
|
+
output: [
|
|
727
|
+
dedent`
|
|
728
|
+
import { Arr } from 'ts-data-forge';
|
|
729
|
+
declare const xs: readonly number[];
|
|
730
|
+
const a = Arr.butLast(xs);
|
|
731
|
+
const b = xs.slice(0, (xs as readonly number[]).length - 1);
|
|
732
|
+
const c = xs.filter((_, i) => i < (xs.length satisfies number) - 2);
|
|
733
|
+
const d = xs.toSpliced(0, xs!.length - 2);
|
|
734
|
+
`,
|
|
735
|
+
dedent`
|
|
736
|
+
import { Arr } from 'ts-data-forge';
|
|
737
|
+
declare const xs: readonly number[];
|
|
738
|
+
const a = Arr.butLast(xs);
|
|
739
|
+
const b = Arr.butLast(xs);
|
|
740
|
+
const c = Arr.skipLast(xs, 2);
|
|
741
|
+
const d = Arr.takeLast(xs, 2);
|
|
742
|
+
`,
|
|
743
|
+
],
|
|
708
744
|
errors: [
|
|
709
745
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
710
746
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
@@ -719,12 +755,20 @@ describe('prefer-canonical-array-slicing through type wrappers', () => {
|
|
|
719
755
|
const a = (xs satisfies readonly number[]).slice(0, xs.length - 1);
|
|
720
756
|
const b = xs.toSpliced((xs.length as number), 0, 1);
|
|
721
757
|
`,
|
|
722
|
-
output:
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
758
|
+
output: [
|
|
759
|
+
dedent`
|
|
760
|
+
import { Arr } from 'ts-data-forge';
|
|
761
|
+
declare const xs: readonly number[];
|
|
762
|
+
const a = Arr.butLast(xs satisfies readonly number[]);
|
|
763
|
+
const b = xs.toSpliced((xs.length as number), 0, 1);
|
|
764
|
+
`,
|
|
765
|
+
dedent`
|
|
766
|
+
import { Arr } from 'ts-data-forge';
|
|
767
|
+
declare const xs: readonly number[];
|
|
768
|
+
const a = Arr.butLast(xs satisfies readonly number[]);
|
|
769
|
+
const b = Arr.toPushed(xs, 1);
|
|
770
|
+
`,
|
|
771
|
+
],
|
|
728
772
|
errors: [
|
|
729
773
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
730
774
|
{ messageId: 'preferCanonicalArraySlicing' },
|
|
@@ -733,3 +777,58 @@ describe('prefer-canonical-array-slicing through type wrappers', () => {
|
|
|
733
777
|
],
|
|
734
778
|
});
|
|
735
779
|
});
|
|
780
|
+
|
|
781
|
+
describe('prefer-canonical-array-slicing and the import it adds', () => {
|
|
782
|
+
tester.run('prefer-canonical-array-slicing', preferCanonicalArraySlicing, {
|
|
783
|
+
valid: [],
|
|
784
|
+
invalid: [
|
|
785
|
+
{
|
|
786
|
+
name: 'writes through a namespace import, which needs no edit',
|
|
787
|
+
code: dedent`
|
|
788
|
+
import * as tdf from 'ts-data-forge';
|
|
789
|
+
declare const xs: readonly number[];
|
|
790
|
+
const ys = xs.slice(1);
|
|
791
|
+
`,
|
|
792
|
+
output: dedent`
|
|
793
|
+
import * as tdf from 'ts-data-forge';
|
|
794
|
+
declare const xs: readonly number[];
|
|
795
|
+
const ys = tdf.Arr.tail(xs);
|
|
796
|
+
`,
|
|
797
|
+
errors: [{ messageId: 'preferCanonicalArraySlicing' }],
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
name: 'joins an existing named import from the module',
|
|
801
|
+
code: dedent`
|
|
802
|
+
import { asInt } from 'ts-data-forge';
|
|
803
|
+
declare const xs: readonly number[];
|
|
804
|
+
const ys = xs.slice(1);
|
|
805
|
+
`,
|
|
806
|
+
output: dedent`
|
|
807
|
+
import { asInt, Arr } from 'ts-data-forge';
|
|
808
|
+
declare const xs: readonly number[];
|
|
809
|
+
const ys = Arr.tail(xs);
|
|
810
|
+
`,
|
|
811
|
+
errors: [{ messageId: 'preferCanonicalArraySlicing' }],
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
name: 'no fix when an import type binds the name',
|
|
815
|
+
code: dedent`
|
|
816
|
+
import type { Arr } from 'ts-data-forge';
|
|
817
|
+
declare const xs: readonly number[];
|
|
818
|
+
const ys = xs.slice(1);
|
|
819
|
+
`,
|
|
820
|
+
output: null,
|
|
821
|
+
errors: [{ messageId: 'preferCanonicalArraySlicing' }],
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
name: 'no fix where a local binding takes the name',
|
|
825
|
+
code: dedent`
|
|
826
|
+
declare const xs: readonly number[];
|
|
827
|
+
const f = (Arr: unknown) => xs.slice(1);
|
|
828
|
+
`,
|
|
829
|
+
output: null,
|
|
830
|
+
errors: [{ messageId: 'preferCanonicalArraySlicing' }],
|
|
831
|
+
},
|
|
832
|
+
],
|
|
833
|
+
});
|
|
834
|
+
});
|
|
@@ -218,6 +218,24 @@ const getCastName = (
|
|
|
218
218
|
: undefined;
|
|
219
219
|
};
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* A non-negative integer literal's value, or `undefined` for anything else.
|
|
223
|
+
* Read through type wrappers: `1 as const` is still the bound `1`.
|
|
224
|
+
*/
|
|
225
|
+
const getLengthLiteral = (
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
227
|
+
wrapped: TSESTree.Node,
|
|
228
|
+
): number | undefined => {
|
|
229
|
+
const node = skipTypeWrappers(wrapped);
|
|
230
|
+
|
|
231
|
+
return node.type === AST_NODE_TYPES.Literal &&
|
|
232
|
+
typeof node.value === 'number' &&
|
|
233
|
+
Number.isSafeInteger(node.value) &&
|
|
234
|
+
node.value >= 0
|
|
235
|
+
? node.value
|
|
236
|
+
: undefined;
|
|
237
|
+
};
|
|
238
|
+
|
|
221
239
|
/**
|
|
222
240
|
* Whether the call is `cast(...bounds, array)` with every bound written as the
|
|
223
241
|
* exact numeric literal the rewrite requires.
|
|
@@ -270,10 +288,10 @@ const matchesBounds = (
|
|
|
270
288
|
/**
|
|
271
289
|
* Removes the length arguments the rewrite drops. Each removal runs from the
|
|
272
290
|
* first token after the delimiter before the dropped argument (the call's `(`
|
|
273
|
-
* or the previous comma) up to the first token after the comma that follows
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
291
|
+
* or the previous comma) up to the first token after the comma that follows it.
|
|
292
|
+
* Parentheses the source put around the dropped argument, the comma, whitespace
|
|
293
|
+
* and any comments in between go with it; parentheses around the next argument
|
|
294
|
+
* stay. The ranges never overlap.
|
|
277
295
|
*/
|
|
278
296
|
const dropBoundsFix = (
|
|
279
297
|
fixer: TSESLint.RuleFixer,
|
|
@@ -285,13 +303,13 @@ const dropBoundsFix = (
|
|
|
285
303
|
rewrite: CastRewrite,
|
|
286
304
|
): readonly TSESLint.RuleFix[] =>
|
|
287
305
|
rewrite.bounds.flatMap((_, index) => {
|
|
288
|
-
// Read before the `includes` below, not after. The strict standard
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
// `
|
|
293
|
-
//
|
|
294
|
-
//
|
|
306
|
+
// Read before the `includes` below, not after. The strict standard library
|
|
307
|
+
// types `includes` as `searchElement is T` — that is what lets a literal
|
|
308
|
+
// union narrow a wider value — but the predicate applies to the false
|
|
309
|
+
// branch too, and here `keep` is `readonly number[]` against a `number`, so
|
|
310
|
+
// `index` narrows to `never` past the guard and `index + 1` stops
|
|
311
|
+
// type-checking. A false `includes` means "not in this array", not "not of
|
|
312
|
+
// this type".
|
|
295
313
|
const dropped = node.arguments[index];
|
|
296
314
|
|
|
297
315
|
const previous = node.arguments[index - 1];
|
|
@@ -319,21 +337,3 @@ const dropBoundsFix = (
|
|
|
319
337
|
? []
|
|
320
338
|
: [fixer.removeRange([start.range[0], end.range[0]])];
|
|
321
339
|
});
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* A non-negative integer literal's value, or `undefined` for anything else.
|
|
325
|
-
* Read through type wrappers: `1 as const` is still the bound `1`.
|
|
326
|
-
*/
|
|
327
|
-
const getLengthLiteral = (
|
|
328
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
329
|
-
wrapped: TSESTree.Node,
|
|
330
|
-
): number | undefined => {
|
|
331
|
-
const node = skipTypeWrappers(wrapped);
|
|
332
|
-
|
|
333
|
-
return node.type === AST_NODE_TYPES.Literal &&
|
|
334
|
-
typeof node.value === 'number' &&
|
|
335
|
-
Number.isSafeInteger(node.value) &&
|
|
336
|
-
node.value >= 0
|
|
337
|
-
? node.value
|
|
338
|
-
: undefined;
|
|
339
|
-
};
|
|
@@ -59,8 +59,8 @@ const MESSAGES = {
|
|
|
59
59
|
} as const satisfies ReadonlyRecord<MessageIds, string>;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Length guards whose bound makes them redundant with a named degenerate
|
|
63
|
-
*
|
|
62
|
+
* Length guards whose bound makes them redundant with a named degenerate guard,
|
|
63
|
+
* keyed by guard name.
|
|
64
64
|
*
|
|
65
65
|
* Every entry stays inside its own family rather than crossing the
|
|
66
66
|
* branded/structural divide:
|