eslint-plugin-ts-data-forge 0.1.7 → 0.3.0

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 +49 -19
  2. package/dist/index.d.mts +1 -1
  3. package/dist/index.d.mts.map +1 -1
  4. package/dist/plugin.d.mts +46 -1
  5. package/dist/plugin.d.mts.map +1 -1
  6. package/dist/plugin.mjs +35 -1
  7. package/dist/plugin.mjs.map +1 -1
  8. package/dist/rule-types.d.mts +6 -51
  9. package/dist/rule-types.d.mts.map +1 -1
  10. package/dist/rules/import-utils.mjs.map +1 -1
  11. package/dist/rules/prefer-arr-is-bounded-length-array.mjs +1 -1
  12. package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
  13. package/dist/rules/prefer-arr-is-fixed-length-array.mjs +1 -1
  14. package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
  15. package/dist/rules/prefer-arr-is-max-length-array.mjs +1 -1
  16. package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
  17. package/dist/rules/prefer-arr-is-min-length-array.mjs +1 -1
  18. package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
  19. package/dist/rules/prefer-arr-sum.mjs +2 -2
  20. package/dist/rules/prefer-arr-sum.mjs.map +1 -1
  21. package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
  22. package/dist/rules/prefer-canonical-length-cast.d.mts +6 -0
  23. package/dist/rules/prefer-canonical-length-cast.d.mts.map +1 -0
  24. package/dist/rules/prefer-canonical-length-cast.mjs +155 -0
  25. package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -0
  26. package/dist/rules/prefer-canonical-length-guard.d.mts +6 -0
  27. package/dist/rules/prefer-canonical-length-guard.d.mts.map +1 -0
  28. package/dist/rules/prefer-canonical-length-guard.mjs +129 -0
  29. package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -0
  30. package/dist/rules/prefer-range-for-loop.mjs +1 -1
  31. package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
  32. package/dist/rules/rules.d.mts +2 -5
  33. package/dist/rules/rules.d.mts.map +1 -1
  34. package/dist/rules/rules.mjs +4 -10
  35. package/dist/rules/rules.mjs.map +1 -1
  36. package/dist/types.d.mts +1 -0
  37. package/dist/types.d.mts.map +1 -1
  38. package/package.json +7 -3
  39. package/src/index.mts +1 -1
  40. package/src/plugin.mts +47 -3
  41. package/src/plugin.test.mts +24 -0
  42. package/src/rule-types.mts +6 -54
  43. package/src/rules/import-utils.mts +0 -1
  44. package/src/rules/prefer-arr-is-bounded-length-array.mts +1 -1
  45. package/src/rules/prefer-arr-is-bounded-length-array.test.mts +5 -5
  46. package/src/rules/prefer-arr-is-fixed-length-array.mts +1 -1
  47. package/src/rules/prefer-arr-is-fixed-length-array.test.mts +11 -11
  48. package/src/rules/prefer-arr-is-max-length-array.mts +1 -1
  49. package/src/rules/prefer-arr-is-max-length-array.test.mts +8 -8
  50. package/src/rules/prefer-arr-is-min-length-array.mts +1 -1
  51. package/src/rules/prefer-arr-is-min-length-array.test.mts +9 -9
  52. package/src/rules/prefer-arr-sum.mts +2 -2
  53. package/src/rules/prefer-canonical-array-slicing.mts +0 -2
  54. package/src/rules/prefer-canonical-length-cast.mts +290 -0
  55. package/src/rules/prefer-canonical-length-cast.test.mts +305 -0
  56. package/src/rules/prefer-canonical-length-guard.mts +250 -0
  57. package/src/rules/prefer-canonical-length-guard.test.mts +307 -0
  58. package/src/rules/prefer-range-for-loop.mts +1 -1
  59. package/src/rules/rules.mts +4 -10
  60. package/src/types.mts +2 -0
package/README.md CHANGED
@@ -12,6 +12,38 @@ Requires ESLint 9+ (flat config) and TypeScript. Some rules are type-aware and n
12
12
 
13
13
  ## Usage (flat config)
14
14
 
15
+ The plugin ships a `recommended` config preset that registers the plugin and
16
+ turns on **every** rule at `error`:
17
+
18
+ ```ts
19
+ // eslint.config.mts
20
+ import { eslintPluginTsDataForge } from 'eslint-plugin-ts-data-forge';
21
+
22
+ export default [eslintPluginTsDataForge.configs.recommended];
23
+ ```
24
+
25
+ Since the preset is a plain flat-config object, individual rules can be
26
+ adjusted by a later config entry:
27
+
28
+ ```ts
29
+ // eslint.config.mts
30
+ import {
31
+ eslintPluginTsDataForge,
32
+ type EslintTsDataForgeRules,
33
+ } from 'eslint-plugin-ts-data-forge';
34
+
35
+ export default [
36
+ eslintPluginTsDataForge.configs.recommended,
37
+ {
38
+ rules: {
39
+ 'ts-data-forge/prefer-range-for-loop': 'off',
40
+ } satisfies Partial<EslintTsDataForgeRules>,
41
+ },
42
+ ];
43
+ ```
44
+
45
+ Or register the plugin yourself and pick the rules one by one:
46
+
15
47
  ```ts
16
48
  // eslint.config.mts
17
49
  import {
@@ -23,7 +55,8 @@ export default [
23
55
  {
24
56
  plugins: { 'ts-data-forge': eslintPluginTsDataForge },
25
57
  rules: {
26
- 'ts-data-forge/prefer-arr-is-non-empty': 'error',
58
+ 'ts-data-forge/prefer-canonical-length-guard': 'error',
59
+ 'ts-data-forge/prefer-canonical-length-cast': 'error',
27
60
  'ts-data-forge/prefer-is-record-and-has-key': 'error',
28
61
  // ...enable the rules you want
29
62
  } satisfies Partial<EslintTsDataForgeRules>,
@@ -33,24 +66,21 @@ export default [
33
66
 
34
67
  ## Rules
35
68
 
36
- | Rule | Description |
37
- | -------------------------------------- | ----------------------------------------------------------------------------------- |
38
- | `prefer-canonical-array-slicing` | Unify non-mutating array add/remove patterns into `Arr.tail`/`skip`/`take`/etc. |
39
- | `prefer-arr-is-min-length-array` | Replace `xs.length >= n` with `Arr.isMinLengthArray(xs, n)`. |
40
- | `prefer-arr-is-max-length-array` | Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)`. |
41
- | `prefer-arr-is-bounded-length-array` | Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(…)`. |
42
- | `prefer-arr-is-fixed-length-array` | Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)`. |
43
- | `prefer-arr-is-array` | Replace `Array.isArray` with `Arr.isArray`. |
44
- | `prefer-arr-is-non-empty` | Replace `xs.length > 0` with `Arr.isNonEmpty(xs)`. |
45
- | `prefer-arr-sum` | Replace `xs.reduce((a, b) => a + b, 0)` with `Arr.sum(xs)` / `Arr.sumBy(xs, fn)`. |
46
- | `prefer-as-int` | Replace branded-number assertions (`x as Int`) with `asInt(x)`-style casts. |
47
- | `prefer-is-non-null-object` | Replace `typeof u === 'object' && u !== null` with `isNonNullObject(u)`. |
48
- | `prefer-range-for-loop` | Replace C-style `for` loops with `for (const i of range(begin, end))`. |
49
- | `prefer-is-record-and-has-key` | Replace `Object.hasOwn(obj, key)` / `key in obj` with `isRecord(obj) && hasKey(…)`. |
50
- | `prefer-num-safe-parse-int` | Replace `parseInt(x, 10)` with `Result.unwrapOkOr(Num.safeParseInt(x), NaN)`. |
51
- | `prefer-num-safe-parse-float` | Replace `parseFloat(x)` / `Number(x)` with `Num.safeParseFloat`-based parsing. |
52
- | `no-unnecessary-type-guard` | Flag `ts-data-forge` type-guard calls that do no narrowing (type-aware). |
53
- | `prefer-comparison-over-nullish-guard` | Prefer direct `=== null` / `!== undefined` over `isNull` / `isNotUndefined` calls. |
69
+ | Rule | Description |
70
+ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
71
+ | `prefer-canonical-array-slicing` | Unify non-mutating array add/remove patterns into `Arr.tail`/`skip`/`take`/etc. |
72
+ | `prefer-canonical-length-guard` | Normalize array-length checks to the canonical `Arr` guard (`xs.length > 0` → `Arr.isNonEmpty`, `Arr.isFixedLengthArray(0, xs)` → `Arr.isEmpty`, `Arr.isFixedLengthTuple(0, xs)` → `Arr.isEmptyTuple`, …). |
73
+ | `prefer-canonical-length-cast` | Normalize a degenerate `Arr` length cast to the canonical one (`Arr.asMinLengthArray(1, xs)` → `Arr.asNonEmptyArray(xs)`, `Arr.asBoundedLengthArray(n, n, xs)` → `Arr.asFixedLengthArray(n, xs)`, …). |
74
+ | `prefer-arr-is-array` | Replace `Array.isArray` with `Arr.isArray`. |
75
+ | `prefer-arr-sum` | Replace `xs.reduce((a, b) => a + b, 0)` with `Arr.sum(xs)` / `Arr.sumBy(xs, fn)`. |
76
+ | `prefer-as-int` | Replace branded-number assertions (`x as Int`) with `asInt(x)`-style casts. |
77
+ | `prefer-is-non-null-object` | Replace `typeof u === 'object' && u !== null` with `isNonNullObject(u)`. |
78
+ | `prefer-range-for-loop` | Replace C-style `for` loops with `for (const i of range(begin, end))`. |
79
+ | `prefer-is-record-and-has-key` | Replace `Object.hasOwn(obj, key)` / `key in obj` with `isRecord(obj) && hasKey(…)`. |
80
+ | `prefer-num-safe-parse-int` | Replace `parseInt(x, 10)` with `Result.unwrapOkOr(Num.safeParseInt(x), NaN)`. |
81
+ | `prefer-num-safe-parse-float` | Replace `parseFloat(x)` / `Number(x)` with `Num.safeParseFloat`-based parsing. |
82
+ | `no-unnecessary-type-guard` | Flag `ts-data-forge` type-guard calls that do no narrowing (type-aware). |
83
+ | `prefer-comparison-over-nullish-guard` | Prefer direct `=== null` / `!== undefined` over `isNull` / `isNotUndefined` calls. |
54
84
 
55
85
  ## License
56
86
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { eslintPluginTsDataForge } from './plugin.mjs';
2
2
  export type { EslintTsDataForgeRules, EslintTsDataForgeRulesOption, } from './rule-types.mjs';
3
3
  export { tsDataForgeRules } from './rules/index.mjs';
4
- export { type ESLintPlugin } from './types.mjs';
4
+ export { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';
5
5
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/dist/plugin.d.mts CHANGED
@@ -1,3 +1,48 @@
1
1
  import { type ESLintPlugin } from './types.mjs';
2
- export declare const eslintPluginTsDataForge: ESLintPlugin;
2
+ export declare const eslintPluginTsDataForge: {
3
+ readonly meta: {
4
+ readonly name: 'eslint-plugin-ts-data-forge';
5
+ };
6
+ readonly rules: {
7
+ readonly 'prefer-canonical-array-slicing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferCanonicalArraySlicing", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
+ 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>;
9
+ readonly 'prefer-canonical-length-cast': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalCast", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
+ readonly 'prefer-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
11
+ readonly 'prefer-arr-sum': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrSum" | "useArrSumBy", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
12
+ readonly 'prefer-as-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useBrandedNumberCastFunction", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
13
+ readonly 'prefer-is-non-null-object': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNonNullObject", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
14
+ readonly 'prefer-range-for-loop': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useRangeForLoop", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
15
+ readonly 'prefer-is-record-and-has-key': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsRecordAndHasKey", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
16
+ readonly 'prefer-num-safe-parse-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
17
+ readonly 'prefer-num-safe-parse-float': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
18
+ readonly 'no-unnecessary-type-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"alwaysFalse" | "alwaysTrue" | "replaceTypeGuard", readonly [(Readonly<{
19
+ ignore?: readonly string[];
20
+ }> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
21
+ readonly 'prefer-comparison-over-nullish-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferComparison", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
22
+ };
23
+ readonly configs: {
24
+ /** Enables every rule of this plugin at `error`. */
25
+ readonly recommended: {
26
+ readonly name: 'ts-data-forge/recommended';
27
+ readonly plugins: {
28
+ readonly 'ts-data-forge': ESLintPlugin;
29
+ };
30
+ readonly rules: {
31
+ readonly 'ts-data-forge/prefer-canonical-array-slicing': 'error';
32
+ readonly 'ts-data-forge/prefer-canonical-length-cast': 'error';
33
+ readonly 'ts-data-forge/prefer-canonical-length-guard': 'error';
34
+ readonly 'ts-data-forge/prefer-arr-is-array': 'error';
35
+ readonly 'ts-data-forge/prefer-arr-sum': 'error';
36
+ readonly 'ts-data-forge/prefer-as-int': 'error';
37
+ readonly 'ts-data-forge/prefer-is-non-null-object': 'error';
38
+ readonly 'ts-data-forge/prefer-range-for-loop': 'error';
39
+ readonly 'ts-data-forge/prefer-is-record-and-has-key': 'error';
40
+ readonly 'ts-data-forge/prefer-num-safe-parse-int': 'error';
41
+ readonly 'ts-data-forge/prefer-num-safe-parse-float': 'error';
42
+ readonly 'ts-data-forge/no-unnecessary-type-guard': 'error';
43
+ readonly 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error';
44
+ };
45
+ };
46
+ };
47
+ };
3
48
  //# sourceMappingURL=plugin.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,uBAAuB,EAAE,YAK5B,CAAC"}
1
+ {"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AA0CvE,eAAO,MAAM,uBAAuB;;iBAEhC,IAAI,EAAE,6BAA6B;;;;;;;;;;;;;;;;;;;;QAInC,oDAAoD;iBACpD,WAAW;2BAtBP,2BAA2B;;yBAQ3B,eAAe,EAAI,YAAY;;;yEA1BW,OAAO;uEACT,OAAO;wEACN,OAAO;8DACjB,OAAO;yDACZ,OAAO;wDACR,OAAO;oEACK,OAAO;gEACX,OAAO;uEACA,OAAO;oEACV,OAAO;sEACL,OAAO;oEACT,OAAO;+EACI,OAAO;;;;CA8B9B,CAAC"}
package/dist/plugin.mjs CHANGED
@@ -1,10 +1,44 @@
1
1
  import { tsDataForgeRules } from './rules/rules.mjs';
2
2
 
3
+ const recommendedRules = {
4
+ "ts-data-forge/prefer-canonical-array-slicing": "error",
5
+ "ts-data-forge/prefer-canonical-length-cast": "error",
6
+ "ts-data-forge/prefer-canonical-length-guard": "error",
7
+ "ts-data-forge/prefer-arr-is-array": "error",
8
+ "ts-data-forge/prefer-arr-sum": "error",
9
+ "ts-data-forge/prefer-as-int": "error",
10
+ "ts-data-forge/prefer-is-non-null-object": "error",
11
+ "ts-data-forge/prefer-range-for-loop": "error",
12
+ "ts-data-forge/prefer-is-record-and-has-key": "error",
13
+ "ts-data-forge/prefer-num-safe-parse-int": "error",
14
+ "ts-data-forge/prefer-num-safe-parse-float": "error",
15
+ "ts-data-forge/no-unnecessary-type-guard": "error",
16
+ "ts-data-forge/prefer-comparison-over-nullish-guard": "error"
17
+ };
18
+ const recommendedConfig = {
19
+ name: "ts-data-forge/recommended",
20
+ plugins: {
21
+ // Resolved lazily so that this config registers the *same* object that
22
+ // `eslintPluginTsDataForge` refers to. ESLint rejects a plugin name that
23
+ // maps to two different objects with `Cannot redefine plugin`, which is
24
+ // what a user combining this preset with their own
25
+ // `plugins: { 'ts-data-forge': eslintPluginTsDataForge }` entry would hit
26
+ // if the preset embedded a separate copy of the plugin.
27
+ get "ts-data-forge"() {
28
+ return eslintPluginTsDataForge;
29
+ }
30
+ },
31
+ rules: recommendedRules
32
+ };
3
33
  const eslintPluginTsDataForge = {
4
34
  meta: {
5
35
  name: "eslint-plugin-ts-data-forge"
6
36
  },
7
- rules: tsDataForgeRules
37
+ rules: tsDataForgeRules,
38
+ configs: {
39
+ /** Enables every rule of this plugin at `error`. */
40
+ recommended: recommendedConfig
41
+ }
8
42
  };
9
43
 
10
44
  export { eslintPluginTsDataForge };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","sources":["../src/plugin.mts"],"sourcesContent":["import { tsDataForgeRules } from './rules/index.mjs';\nimport { type ESLintPlugin } from './types.mjs';\n\nexport const eslintPluginTsDataForge: ESLintPlugin = {\n meta: {\n name: 'eslint-plugin-ts-data-forge',\n },\n rules: tsDataForgeRules,\n} as const;\n"],"names":[],"mappings":";;AAGO,MAAM,uBAAA,GAAwC;AAAA,EACnD,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM;AAAA,GACR;AAAA,EACA,KAAA,EAAO;AACT;;;;"}
1
+ {"version":3,"file":"plugin.mjs","sources":["../src/plugin.mts"],"sourcesContent":["import { tsDataForgeRules } from './rules/index.mjs';\nimport { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';\n\n/**\n * Every rule this plugin ships, at `error`.\n *\n * The `satisfies` clause below is keyed off {@link tsDataForgeRules}, so adding\n * a rule without listing it here fails to type-check.\n */\nconst recommendedRules = {\n 'ts-data-forge/prefer-canonical-array-slicing': 'error',\n 'ts-data-forge/prefer-canonical-length-cast': 'error',\n 'ts-data-forge/prefer-canonical-length-guard': 'error',\n 'ts-data-forge/prefer-arr-is-array': 'error',\n 'ts-data-forge/prefer-arr-sum': 'error',\n 'ts-data-forge/prefer-as-int': 'error',\n 'ts-data-forge/prefer-is-non-null-object': 'error',\n 'ts-data-forge/prefer-range-for-loop': 'error',\n 'ts-data-forge/prefer-is-record-and-has-key': 'error',\n 'ts-data-forge/prefer-num-safe-parse-int': 'error',\n 'ts-data-forge/prefer-num-safe-parse-float': 'error',\n 'ts-data-forge/no-unnecessary-type-guard': 'error',\n 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',\n} as const satisfies Readonly<\n Record<`ts-data-forge/${keyof typeof tsDataForgeRules}`, 'error'>\n>;\n\nconst recommendedConfig = {\n name: 'ts-data-forge/recommended',\n plugins: {\n // Resolved lazily so that this config registers the *same* object that\n // `eslintPluginTsDataForge` refers to. ESLint rejects a plugin name that\n // maps to two different objects with `Cannot redefine plugin`, which is\n // what a user combining this preset with their own\n // `plugins: { 'ts-data-forge': eslintPluginTsDataForge }` entry would hit\n // if the preset embedded a separate copy of the plugin.\n get 'ts-data-forge'(): ESLintPlugin {\n return eslintPluginTsDataForge;\n },\n },\n rules: recommendedRules,\n} as const satisfies ESLintFlatConfig;\n\nexport const eslintPluginTsDataForge = {\n meta: {\n name: 'eslint-plugin-ts-data-forge',\n },\n rules: tsDataForgeRules,\n configs: {\n /** Enables every rule of this plugin at `error`. */\n recommended: recommendedConfig,\n },\n} as const satisfies ESLintPlugin;\n"],"names":[],"mappings":";;AASA,MAAM,gBAAA,GAAmB;AAAA,EACvB,8CAAA,EAAgD,OAAA;AAAA,EAChD,4CAAA,EAA8C,OAAA;AAAA,EAC9C,6CAAA,EAA+C,OAAA;AAAA,EAC/C,mCAAA,EAAqC,OAAA;AAAA,EACrC,8BAAA,EAAgC,OAAA;AAAA,EAChC,6BAAA,EAA+B,OAAA;AAAA,EAC/B,yCAAA,EAA2C,OAAA;AAAA,EAC3C,qCAAA,EAAuC,OAAA;AAAA,EACvC,4CAAA,EAA8C,OAAA;AAAA,EAC9C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,2CAAA,EAA6C,OAAA;AAAA,EAC7C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,oDAAA,EAAsD;AACxD,CAAA;AAIA,MAAM,iBAAA,GAAoB;AAAA,EACxB,IAAA,EAAM,2BAAA;AAAA,EACN,OAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOP,IAAI,eAAA,GAAgC;AAClC,MAAA,OAAO,uBAAA;AAAA,IACT;AAAA,GACF;AAAA,EACA,KAAA,EAAO;AACT,CAAA;AAEO,MAAM,uBAAA,GAA0B;AAAA,EACrC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM;AAAA,GACR;AAAA,EACA,KAAA,EAAO,gBAAA;AAAA,EACP,OAAA,EAAS;AAAA;AAAA,IAEP,WAAA,EAAa;AAAA;AAEjB;;;;"}
@@ -23,7 +23,7 @@ declare namespace PreferCanonicalArraySlicing {
23
23
  type RuleEntry = Linter.StringSeverity;
24
24
  }
25
25
  /**
26
- * @description Replace `xs.length >= n` with `Arr.isMinLengthArray(xs, n)` from ts-data-forge.
26
+ * @description Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and a degenerate guard becomes the named one for its family (`Arr.isFixedLengthArray(0, xs)` → `Arr.isEmpty`, `Arr.isFixedLengthTuple(0, xs)` → `Arr.isEmptyTuple`).
27
27
  *
28
28
  * ```md
29
29
  * | key | value |
@@ -33,11 +33,11 @@ declare namespace PreferCanonicalArraySlicing {
33
33
  * | fixable | code |
34
34
  * ```
35
35
  */
36
- declare namespace PreferArrIsMinLengthArray {
36
+ declare namespace PreferCanonicalLengthGuard {
37
37
  type RuleEntry = Linter.StringSeverity;
38
38
  }
39
39
  /**
40
- * @description Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)` from ts-data-forge.
40
+ * @description Normalize an `Arr` length cast whose bounds are degenerate (e.g. `Arr.asMinLengthArray(1, xs)`, `Arr.asFixedLengthTuple(0, xs)`) to the cast that names that constraint directly, staying within the branded `*Array` or structural `*Tuple` family.
41
41
  *
42
42
  * ```md
43
43
  * | key | value |
@@ -47,35 +47,7 @@ declare namespace PreferArrIsMinLengthArray {
47
47
  * | fixable | code |
48
48
  * ```
49
49
  */
50
- declare namespace PreferArrIsMaxLengthArray {
51
- type RuleEntry = Linter.StringSeverity;
52
- }
53
- /**
54
- * @description Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(xs, min, max)` from ts-data-forge.
55
- *
56
- * ```md
57
- * | key | value |
58
- * | :--------- | :--------- |
59
- * | type | suggestion |
60
- * | deprecated | false |
61
- * | fixable | code |
62
- * ```
63
- */
64
- declare namespace PreferArrIsBoundedLengthArray {
65
- type RuleEntry = Linter.StringSeverity;
66
- }
67
- /**
68
- * @description Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)` from ts-data-forge.
69
- *
70
- * ```md
71
- * | key | value |
72
- * | :--------- | :--------- |
73
- * | type | suggestion |
74
- * | deprecated | false |
75
- * | fixable | code |
76
- * ```
77
- */
78
- declare namespace PreferArrIsFixedLengthArray {
50
+ declare namespace PreferCanonicalLengthCast {
79
51
  type RuleEntry = Linter.StringSeverity;
80
52
  }
81
53
  /**
@@ -92,20 +64,6 @@ declare namespace PreferArrIsFixedLengthArray {
92
64
  declare namespace PreferArrIsArray {
93
65
  type RuleEntry = Linter.StringSeverity;
94
66
  }
95
- /**
96
- * @description Replace `xs.length > 0` with `Arr.isNonEmpty(xs)` from ts-data-forge.
97
- *
98
- * ```md
99
- * | key | value |
100
- * | :--------- | :--------- |
101
- * | type | suggestion |
102
- * | deprecated | false |
103
- * | fixable | code |
104
- * ```
105
- */
106
- declare namespace PreferArrIsNonEmpty {
107
- type RuleEntry = Linter.StringSeverity;
108
- }
109
67
  /**
110
68
  * @description Replace `xs.reduce((a, b) => a + b, 0)` with `Arr.sum(xs)` or `Arr.sumBy(xs, fn)` from ts-data-forge.
111
69
  *
@@ -256,12 +214,9 @@ declare namespace PreferComparisonOverNullishGuard {
256
214
  }
257
215
  export type EslintTsDataForgeRules = Readonly<{
258
216
  'ts-data-forge/prefer-canonical-array-slicing': PreferCanonicalArraySlicing.RuleEntry;
259
- 'ts-data-forge/prefer-arr-is-min-length-array': PreferArrIsMinLengthArray.RuleEntry;
260
- 'ts-data-forge/prefer-arr-is-max-length-array': PreferArrIsMaxLengthArray.RuleEntry;
261
- 'ts-data-forge/prefer-arr-is-bounded-length-array': PreferArrIsBoundedLengthArray.RuleEntry;
262
- 'ts-data-forge/prefer-arr-is-fixed-length-array': PreferArrIsFixedLengthArray.RuleEntry;
217
+ 'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
218
+ 'ts-data-forge/prefer-canonical-length-cast': PreferCanonicalLengthCast.RuleEntry;
263
219
  'ts-data-forge/prefer-arr-is-array': PreferArrIsArray.RuleEntry;
264
- 'ts-data-forge/prefer-arr-is-non-empty': PreferArrIsNonEmpty.RuleEntry;
265
220
  'ts-data-forge/prefer-arr-sum': PreferArrSum.RuleEntry;
266
221
  'ts-data-forge/prefer-as-int': PreferAsInt.RuleEntry;
267
222
  'ts-data-forge/prefer-is-non-null-object': PreferIsNonNullObject.RuleEntry;
@@ -1 +1 @@
1
- {"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rule-types.d.mts","sourceRoot":"","sources":["../src/rule-types.mts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"import-utils.mjs","sources":["../../src/rules/import-utils.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { TS_DATA_FORGE_MODULE } from './constants.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\nexport const getTsDataForgeImport = (\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined =>\n program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === AST_NODE_TYPES.ImportDeclaration &&\n node.source.value === TS_DATA_FORGE_MODULE,\n );\n\nexport const getNamedImports = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n): readonly string[] => {\n if (node === undefined) return [];\n\n return node.specifiers.flatMap((specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier\n ? (() => {\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string' ? [importedName] : [];\n })()\n : [],\n );\n};\n\n/**\n * Returns the in-scope local name a given canonical export is imported as\n * (handling aliases such as `import { isX as y }` → `'y'`), or `undefined` when\n * it is not imported. Used so autofixes reference the binding that actually\n * exists rather than the canonical name.\n */\nexport const getImportedLocalName = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n importedName: string,\n): string | undefined =>\n node?.specifiers.find(\n (specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n (specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value) === importedName,\n )?.local.name;\n\ntype ResolvedCallee = Readonly<{\n /** The canonical (imported) ts-data-forge name, regardless of local alias. */\n canonicalName: string;\n /** The identifier node to rewrite when replacing the callee name. */\n propertyNode: TSESTree.Node;\n /** Whether the call was made through a namespace import (`tf.isX(...)`). */\n isNamespace: boolean;\n}>;\n\n/**\n * Builds a resolver that maps a call expression's callee to the canonical\n * ts-data-forge function it refers to. Handles named imports (including aliases\n * such as `import { isX as y }`) and namespace imports (`import * as tf` →\n * `tf.isX(...)`). Returns `undefined` for any callee that does not reference the\n * given ts-data-forge import.\n */\nexport const buildCalleeResolver = (\n importDecl: TSESTree.ImportDeclaration | undefined,\n): ((\n callee: DeepReadonly<TSESTree.Expression>,\n) => ResolvedCallee | undefined) => {\n const specifiers = importDecl?.specifiers ?? [];\n\n const namespaceName = specifiers.find(\n (specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,\n )?.local.name;\n\n const localToCanonical = new Map<string, string>(\n specifiers.flatMap((specifier) => {\n if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) return [];\n\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string'\n ? [[specifier.local.name, importedName] as const]\n : [];\n }),\n );\n\n return (callee) => {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n const canonicalName = localToCanonical.get(callee.name);\n\n return canonicalName === undefined\n ? undefined\n : {\n canonicalName,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee as TSESTree.Node,\n isNamespace: false,\n };\n }\n\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n namespaceName !== undefined &&\n callee.object.name === namespaceName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return {\n canonicalName: callee.property.name,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee.property as TSESTree.Node,\n isNamespace: true,\n };\n }\n\n return undefined;\n };\n};\n\nexport const buildImportFixes = (\n fixer: TSESLint.RuleFixer,\n program: TSESTree.Program,\n _tsDataForgeImport: TSESTree.ImportDeclaration | undefined,\n requiredNames: readonly string[],\n): readonly TSESLint.RuleFix[] => {\n const specifierText = requiredNames.join(', ');\n\n const importStatement =\n `import { ${specifierText} } from '${TS_DATA_FORGE_MODULE}';` as const;\n\n const newLine = '\\n';\n\n const insertionText = `${importStatement}${newLine}` as const;\n\n // Always insert at the beginning of the file\n // (organize-imports will handle merging/deduplication)\n return [fixer.insertTextBefore(program, insertionText)];\n};\n/* eslint-enable @typescript-eslint/prefer-readonly-parameter-types */\n"],"names":[],"mappings":";;;AAUO,MAAM,oBAAA,GAAuB,CAClC,OAAA,KAEA,OAAA,CAAQ,IAAA,CAAK,IAAA;AAAA,EACX,CAAC,SACC,IAAA,CAAK,IAAA,KAAS,eAAe,iBAAA,IAC7B,IAAA,CAAK,OAAO,KAAA,KAAU;AAC1B;AAEK,MAAM,eAAA,GAAkB,CAC7B,IAAA,KACsB;AACtB,EAAA,IAAI,IAAA,KAAS,MAAA,EAAW,OAAO,EAAC;AAEhC,EAAA,OAAO,KAAK,UAAA,CAAW,OAAA;AAAA,IAAQ,CAAC,SAAA,KAC9B,SAAA,CAAU,IAAA,KAAS,cAAA,CAAe,mBAC7B,MAAM;AACL,MAAA,MAAM,YAAA,GACJ,SAAA,CAAU,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,aACvC,SAAA,CAAU,QAAA,CAAS,IAAA,GACnB,SAAA,CAAU,QAAA,CAAS,KAAA;AAEzB,MAAA,OAAO,OAAO,YAAA,KAAiB,QAAA,GAAW,CAAC,YAAY,IAAI,EAAC;AAAA,IAC9D,CAAA,MACA;AAAC,GACP;AACF;AAQO,MAAM,oBAAA,GAAuB,CAClC,IAAA,EACA,YAAA,KAEA,MAAM,UAAA,CAAW,IAAA;AAAA,EACf,CAAC,SAAA,KACC,SAAA,CAAU,IAAA,KAAS,cAAA,CAAe,oBACjC,SAAA,CAAU,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,aACxC,SAAA,CAAU,QAAA,CAAS,IAAA,GACnB,SAAA,CAAU,SAAS,KAAA,MAAW;AACtC,CAAA,EAAG,KAAA,CAAM;AAkBJ,MAAM,mBAAA,GAAsB,CACjC,UAAA,KAGkC;AAClC,EAAA,MAAM,UAAA,GAAa,UAAA,EAAY,UAAA,IAAc,EAAC;AAE9C,EAAA,MAAM,gBAAgB,UAAA,CAAW,IAAA;AAAA,IAC/B,CAAC,SAAA,KAAc,SAAA,CAAU,IAAA,KAAS,cAAA,CAAe;AAAA,KAChD,KAAA,CAAM,IAAA;AAET,EAAA,MAAM,mBAAmB,IAAI,GAAA;AAAA,IAC3B,UAAA,CAAW,OAAA,CAAQ,CAAC,SAAA,KAAc;AAChC,MAAA,IAAI,SAAA,CAAU,IAAA,KAAS,cAAA,CAAe,eAAA,SAAwB,EAAC;AAE/D,MAAA,MAAM,YAAA,GACJ,SAAA,CAAU,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,aACvC,SAAA,CAAU,QAAA,CAAS,IAAA,GACnB,SAAA,CAAU,QAAA,CAAS,KAAA;AAEzB,MAAA,OAAO,OAAO,YAAA,KAAiB,QAAA,GAC3B,CAAC,CAAC,SAAA,CAAU,KAAA,CAAM,IAAA,EAAM,YAAY,CAAU,CAAA,GAC9C,EAAC;AAAA,IACP,CAAC;AAAA,GACH;AAEA,EAAA,OAAO,CAAC,MAAA,KAAW;AACjB,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,UAAA,EAAY;AAC7C,MAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA;AAEtD,MAAA,OAAO,aAAA,KAAkB,SACrB,MAAA,GACA;AAAA,QACE,aAAA;AAAA;AAAA,QAEA,YAAA,EAAc,MAAA;AAAA,QACd,WAAA,EAAa;AAAA,OACf;AAAA,IACN;AAEA,IAAA,IACE,MAAA,CAAO,SAAS,cAAA,CAAe,gBAAA,IAC/B,CAAC,MAAA,CAAO,QAAA,IACR,MAAA,CAAO,MAAA,CAAO,IAAA,KAAS,cAAA,CAAe,cACtC,aAAA,KAAkB,MAAA,IAClB,OAAO,MAAA,CAAO,IAAA,KAAS,iBACvB,MAAA,CAAO,QAAA,CAAS,IAAA,KAAS,cAAA,CAAe,UAAA,EACxC;AACA,MAAA,OAAO;AAAA,QACL,aAAA,EAAe,OAAO,QAAA,CAAS,IAAA;AAAA;AAAA,QAE/B,cAAc,MAAA,CAAO,QAAA;AAAA,QACrB,WAAA,EAAa;AAAA,OACf;AAAA,IACF;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACF;AAEO,MAAM,gBAAA,GAAmB,CAC9B,KAAA,EACA,OAAA,EACA,oBACA,aAAA,KACgC;AAChC,EAAA,MAAM,aAAA,GAAgB,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA;AAE7C,EAAA,MAAM,eAAA,GACJ,CAAA,SAAA,EAAY,aAAa,CAAA,SAAA,EAAY,oBAAoB,CAAA,EAAA,CAAA;AAE3D,EAAA,MAAM,OAAA,GAAU,IAAA;AAEhB,EAAA,MAAM,aAAA,GAAgB,CAAA,EAAG,eAAe,CAAA,EAAG,OAAO,CAAA,CAAA;AAIlD,EAAA,OAAO,CAAC,KAAA,CAAM,gBAAA,CAAiB,OAAA,EAAS,aAAa,CAAC,CAAA;AACxD;;;;"}
1
+ {"version":3,"file":"import-utils.mjs","sources":["../../src/rules/import-utils.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { TS_DATA_FORGE_MODULE } from './constants.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\nexport const getTsDataForgeImport = (\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined =>\n program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === AST_NODE_TYPES.ImportDeclaration &&\n node.source.value === TS_DATA_FORGE_MODULE,\n );\n\nexport const getNamedImports = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n): readonly string[] => {\n if (node === undefined) return [];\n\n return node.specifiers.flatMap((specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier\n ? (() => {\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string' ? [importedName] : [];\n })()\n : [],\n );\n};\n\n/**\n * Returns the in-scope local name a given canonical export is imported as\n * (handling aliases such as `import { isX as y }` → `'y'`), or `undefined` when\n * it is not imported. Used so autofixes reference the binding that actually\n * exists rather than the canonical name.\n */\nexport const getImportedLocalName = (\n node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,\n importedName: string,\n): string | undefined =>\n node?.specifiers.find(\n (specifier) =>\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n (specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value) === importedName,\n )?.local.name;\n\ntype ResolvedCallee = Readonly<{\n /** The canonical (imported) ts-data-forge name, regardless of local alias. */\n canonicalName: string;\n /** The identifier node to rewrite when replacing the callee name. */\n propertyNode: TSESTree.Node;\n /** Whether the call was made through a namespace import (`tf.isX(...)`). */\n isNamespace: boolean;\n}>;\n\n/**\n * Builds a resolver that maps a call expression's callee to the canonical\n * ts-data-forge function it refers to. Handles named imports (including aliases\n * such as `import { isX as y }`) and namespace imports (`import * as tf` →\n * `tf.isX(...)`). Returns `undefined` for any callee that does not reference the\n * given ts-data-forge import.\n */\nexport const buildCalleeResolver = (\n importDecl: TSESTree.ImportDeclaration | undefined,\n): ((\n callee: DeepReadonly<TSESTree.Expression>,\n) => ResolvedCallee | undefined) => {\n const specifiers = importDecl?.specifiers ?? [];\n\n const namespaceName = specifiers.find(\n (specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,\n )?.local.name;\n\n const localToCanonical = new Map<string, string>(\n specifiers.flatMap((specifier) => {\n if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) return [];\n\n const importedName =\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ? specifier.imported.name\n : specifier.imported.value;\n\n return typeof importedName === 'string'\n ? [[specifier.local.name, importedName] as const]\n : [];\n }),\n );\n\n return (callee) => {\n if (callee.type === AST_NODE_TYPES.Identifier) {\n const canonicalName = localToCanonical.get(callee.name);\n\n return canonicalName === undefined\n ? undefined\n : {\n canonicalName,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee as TSESTree.Node,\n isNamespace: false,\n };\n }\n\n if (\n callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n namespaceName !== undefined &&\n callee.object.name === namespaceName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ) {\n return {\n canonicalName: callee.property.name,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n propertyNode: callee.property as TSESTree.Node,\n isNamespace: true,\n };\n }\n\n return undefined;\n };\n};\n\nexport const buildImportFixes = (\n fixer: TSESLint.RuleFixer,\n program: TSESTree.Program,\n _tsDataForgeImport: TSESTree.ImportDeclaration | undefined,\n requiredNames: readonly string[],\n): readonly TSESLint.RuleFix[] => {\n const specifierText = requiredNames.join(', ');\n\n const importStatement =\n `import { ${specifierText} } from '${TS_DATA_FORGE_MODULE}';` as const;\n\n const newLine = '\\n';\n\n const insertionText = `${importStatement}${newLine}` as const;\n\n // Always insert at the beginning of the file\n // (organize-imports will handle merging/deduplication)\n return [fixer.insertTextBefore(program, insertionText)];\n};\n"],"names":[],"mappings":"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"}
@@ -73,7 +73,7 @@ const preferArrIsBoundedLengthArray = {
73
73
  max: maxText
74
74
  },
75
75
  fix: (fixer) => {
76
- const replacement = `Arr.isBoundedLengthArray(${arrayText}, ${minText}, ${maxText})`;
76
+ const replacement = `Arr.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;
77
77
  const importFixes = index === 0 && !hasArrImport ? buildImportFixes(fixer, program, tsDataForgeImport, ["Arr"]) : [];
78
78
  return [...importFixes, fixer.replaceText(node, replacement)];
79
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-bounded-length-array.mjs","sources":["../../src/rules/prefer-arr-is-bounded-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport { parseLengthComparison } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsBoundedLengthArray';\n\nexport const preferArrIsBoundedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(xs, min, max)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsBoundedLengthArray:\n 'Replace `{{original}}` with `Arr.isBoundedLengthArray({{arrayName}}, {{min}}, {{max}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.LogicalExpression;\n arrayExpression: TSESTree.Expression;\n minExpression: TSESTree.Expression;\n maxExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n LogicalExpression: (node) => {\n // Only `a && b` can encode a `min && max` length range.\n if (node.operator !== '&&') return;\n\n const left = parseLengthComparison(node.left, sourceCode);\n\n const right = parseLengthComparison(node.right, sourceCode);\n\n if (left === undefined || right === undefined) return;\n\n // Need one lower bound and one upper bound on the same array.\n if (left.kind === right.kind) return;\n\n if (\n sourceCode.getText(left.array) !== sourceCode.getText(right.array)\n ) {\n return;\n }\n\n const min = left.kind === 'min' ? left : right;\n\n const max = left.kind === 'min' ? right : left;\n\n const arrayExpression = min.array;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n minExpression: min.bound,\n maxExpression: max.bound,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, minExpression, maxExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const minText = sourceCode.getText(minExpression);\n\n const maxText = sourceCode.getText(maxExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsBoundedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n min: minText,\n max: maxText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isBoundedLengthArray(${arrayText}, ${minText}, ${maxText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-bounded-length-array.mjs","sources":["../../src/rules/prefer-arr-is-bounded-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport { parseLengthComparison } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsBoundedLengthArray';\n\nexport const preferArrIsBoundedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length >= min && xs.length <= max` with `Arr.isBoundedLengthArray(xs, min, max)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsBoundedLengthArray:\n 'Replace `{{original}}` with `Arr.isBoundedLengthArray({{arrayName}}, {{min}}, {{max}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.LogicalExpression;\n arrayExpression: TSESTree.Expression;\n minExpression: TSESTree.Expression;\n maxExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n LogicalExpression: (node) => {\n // Only `a && b` can encode a `min && max` length range.\n if (node.operator !== '&&') return;\n\n const left = parseLengthComparison(node.left, sourceCode);\n\n const right = parseLengthComparison(node.right, sourceCode);\n\n if (left === undefined || right === undefined) return;\n\n // Need one lower bound and one upper bound on the same array.\n if (left.kind === right.kind) return;\n\n if (\n sourceCode.getText(left.array) !== sourceCode.getText(right.array)\n ) {\n return;\n }\n\n const min = left.kind === 'min' ? left : right;\n\n const max = left.kind === 'min' ? right : left;\n\n const arrayExpression = min.array;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n minExpression: min.bound,\n maxExpression: max.bound,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, minExpression, maxExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const minText = sourceCode.getText(minExpression);\n\n const maxText = sourceCode.getText(maxExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsBoundedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n min: minText,\n max: maxText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isBoundedLengthArray(${minText}, ${maxText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
@@ -69,7 +69,7 @@ const preferArrIsFixedLengthArray = {
69
69
  length: lengthText
70
70
  },
71
71
  fix: (fixer) => {
72
- const baseCall = `Arr.isFixedLengthArray(${arrayText}, ${lengthText})`;
72
+ const baseCall = `Arr.isFixedLengthArray(${lengthText}, ${arrayText})`;
73
73
  const replacement = isNegated ? `!${baseCall}` : baseCall;
74
74
  const importFixes = index === 0 && !hasArrImport ? buildImportFixes(fixer, program, tsDataForgeImport, ["Arr"]) : [];
75
75
  return [...importFixes, fixer.replaceText(node, replacement)];
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-fixed-length-array.mjs","sources":["../../src/rules/prefer-arr-is-fixed-length-array.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { isIntegerLiteralOrConstant } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsFixedLengthArray';\n\nexport const preferArrIsFixedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsFixedLengthArray:\n 'Replace `{{original}}` with `Arr.isFixedLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n isNegated: boolean;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length === n` or `n === xs.length` or `xs.length !== n`\n if (node.operator !== '===' && node.operator !== '!==') return;\n\n const isNegated = node.operator === '!==';\n\n const isLengthOnLeft = isLengthAccess(node.left);\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n isNegated,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression, isNegated },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsFixedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const baseCall = `Arr.isFixedLengthArray(${arrayText}, ${lengthText})`;\n\n const replacement = isNegated ? `!${baseCall}` : baseCall;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\nconst isLengthAccess = (\n node: DeepReadonly<TSESTree.Expression>,\n): node is TSESTree.MemberExpression =>\n node.type === AST_NODE_TYPES.MemberExpression &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n node.property.name === 'length';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-fixed-length-array.mjs","sources":["../../src/rules/prefer-arr-is-fixed-length-array.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { isIntegerLiteralOrConstant } from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsFixedLengthArray';\n\nexport const preferArrIsFixedLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length === n` with `Arr.isFixedLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsFixedLengthArray:\n 'Replace `{{original}}` with `Arr.isFixedLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n isNegated: boolean;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length === n` or `n === xs.length` or `xs.length !== n`\n if (node.operator !== '===' && node.operator !== '!==') return;\n\n const isNegated = node.operator === '!==';\n\n const isLengthOnLeft = isLengthAccess(node.left);\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n isNegated,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression, isNegated },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsFixedLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const baseCall = `Arr.isFixedLengthArray(${lengthText}, ${arrayText})`;\n\n const replacement = isNegated ? `!${baseCall}` : baseCall;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\nconst isLengthAccess = (\n node: DeepReadonly<TSESTree.Expression>,\n): node is TSESTree.MemberExpression =>\n node.type === AST_NODE_TYPES.MemberExpression &&\n node.property.type === AST_NODE_TYPES.Identifier &&\n node.property.name === 'length';\n"],"names":[],"mappings":"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"}
@@ -67,7 +67,7 @@ const preferArrIsMaxLengthArray = {
67
67
  length: lengthText
68
68
  },
69
69
  fix: (fixer) => {
70
- const replacement = `Arr.isMaxLengthArray(${arrayText}, ${lengthText})`;
70
+ const replacement = `Arr.isMaxLengthArray(${lengthText}, ${arrayText})`;
71
71
  const importFixes = index === 0 && !hasArrImport ? buildImportFixes(fixer, program, tsDataForgeImport, ["Arr"]) : [];
72
72
  return [...importFixes, fixer.replaceText(node, replacement)];
73
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-max-length-array.mjs","sources":["../../src/rules/prefer-arr-is-max-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport {\n isIntegerLiteralOrConstant,\n isLengthAccess,\n isPartOfBoundedLengthCheck,\n} from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsMaxLengthArray';\n\nexport const preferArrIsMaxLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsMaxLengthArray:\n 'Replace `{{original}}` with `Arr.isMaxLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length <= n` or `n >= xs.length`\n if (node.operator !== '>=' && node.operator !== '<=') return;\n\n // Defer to `prefer-arr-is-bounded-length-array` when this comparison is\n // the upper bound of a `min && max` pair on the same array.\n if (isPartOfBoundedLengthCheck(node, sourceCode)) return;\n\n // xs.length <= n or n >= xs.length\n const isLengthOnLeft = node.operator === '<=';\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n // Check if lengthSide is accessing .length\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsMaxLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isMaxLengthArray(${arrayText}, ${lengthText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-max-length-array.mjs","sources":["../../src/rules/prefer-arr-is-max-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport {\n isIntegerLiteralOrConstant,\n isLengthAccess,\n isPartOfBoundedLengthCheck,\n} from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsMaxLengthArray';\n\nexport const preferArrIsMaxLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsMaxLengthArray:\n 'Replace `{{original}}` with `Arr.isMaxLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length <= n` or `n >= xs.length`\n if (node.operator !== '>=' && node.operator !== '<=') return;\n\n // Defer to `prefer-arr-is-bounded-length-array` when this comparison is\n // the upper bound of a `min && max` pair on the same array.\n if (isPartOfBoundedLengthCheck(node, sourceCode)) return;\n\n // xs.length <= n or n >= xs.length\n const isLengthOnLeft = node.operator === '<=';\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n // Check if lengthSide is accessing .length\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsMaxLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isMaxLengthArray(${lengthText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
@@ -67,7 +67,7 @@ const preferArrIsMinLengthArray = {
67
67
  length: lengthText
68
68
  },
69
69
  fix: (fixer) => {
70
- const replacement = `Arr.isMinLengthArray(${arrayText}, ${lengthText})`;
70
+ const replacement = `Arr.isMinLengthArray(${lengthText}, ${arrayText})`;
71
71
  const importFixes = index === 0 && !hasArrImport ? buildImportFixes(fixer, program, tsDataForgeImport, ["Arr"]) : [];
72
72
  return [...importFixes, fixer.replaceText(node, replacement)];
73
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-arr-is-min-length-array.mjs","sources":["../../src/rules/prefer-arr-is-min-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport {\n isIntegerLiteralOrConstant,\n isLengthAccess,\n isPartOfBoundedLengthCheck,\n} from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsMinLengthArray';\n\nexport const preferArrIsMinLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length >= n` with `Arr.isMinLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsMinLengthArray:\n 'Replace `{{original}}` with `Arr.isMinLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length >= n` or `n <= xs.length`\n if (node.operator !== '>=' && node.operator !== '<=') return;\n\n // Defer to `prefer-arr-is-bounded-length-array` when this comparison is\n // the lower bound of a `min && max` pair on the same array.\n if (isPartOfBoundedLengthCheck(node, sourceCode)) return;\n\n // xs.length >= n or n <= xs.length\n const isLengthOnLeft = node.operator === '>=';\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n // Check if lengthSide is accessing .length\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsMinLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isMinLengthArray(${arrayText}, ${lengthText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefer-arr-is-min-length-array.mjs","sources":["../../src/rules/prefer-arr-is-min-length-array.mts"],"sourcesContent":["import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';\nimport {\n isIntegerLiteralOrConstant,\n isLengthAccess,\n isPartOfBoundedLengthCheck,\n} from './ast-utils.mjs';\nimport {\n buildImportFixes,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds = 'useIsMinLengthArray';\n\nexport const preferArrIsMinLengthArray: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace `xs.length >= n` with `Arr.isMinLengthArray(xs, n)` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useIsMinLengthArray:\n 'Replace `{{original}}` with `Arr.isMinLengthArray({{arrayName}}, {{length}})` from ts-data-forge.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const services = context.sourceCode.parserServices;\n\n const mut_nodesToFix: {\n node: TSESTree.BinaryExpression;\n arrayExpression: TSESTree.Expression;\n lengthExpression: TSESTree.Expression;\n }[] = [];\n\n return {\n BinaryExpression: (node) => {\n // Check for `xs.length >= n` or `n <= xs.length`\n if (node.operator !== '>=' && node.operator !== '<=') return;\n\n // Defer to `prefer-arr-is-bounded-length-array` when this comparison is\n // the lower bound of a `min && max` pair on the same array.\n if (isPartOfBoundedLengthCheck(node, sourceCode)) return;\n\n // xs.length >= n or n <= xs.length\n const isLengthOnLeft = node.operator === '>=';\n\n const lengthSide = node[isLengthOnLeft ? 'left' : 'right'];\n\n const valueSide = node[isLengthOnLeft ? 'right' : 'left'];\n\n // Check if lengthSide is accessing .length\n if (!isLengthAccess(lengthSide)) return;\n\n // Only match integer literals or const variables initialized with integer literals\n if (!isIntegerLiteralOrConstant(valueSide, sourceCode)) return;\n\n // lengthSide is MemberExpression accessing .length\n const arrayExpression = lengthSide.object;\n\n // Check if arrayExpression is actually an array type\n if (services?.program !== undefined && services.program !== null) {\n const checker = services.program.getTypeChecker();\n\n const tsNode = services.esTreeNodeToTSNodeMap?.get(arrayExpression);\n\n if (tsNode !== undefined) {\n const type = checker.getTypeAtLocation(tsNode);\n\n // Check if it's an array type or tuple type\n const isArrayType =\n checker.isArrayType(type) || checker.isTupleType(type);\n\n if (!isArrayType) return;\n } else {\n return;\n }\n } else {\n return;\n }\n\n mut_nodesToFix.push({\n node,\n arrayExpression,\n lengthExpression: valueSide,\n });\n },\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const hasArrImport = namedImports.includes('Arr');\n\n for (const [\n index,\n { node, arrayExpression, lengthExpression },\n ] of mut_nodesToFix.entries()) {\n const arrayText = sourceCode.getText(arrayExpression);\n\n const lengthText = sourceCode.getText(lengthExpression);\n\n const originalText = sourceCode.getText(node);\n\n context.report({\n node,\n messageId: 'useIsMinLengthArray',\n data: {\n original: originalText,\n arrayName: arrayText,\n length: lengthText,\n },\n fix: (fixer) => {\n const replacement = `Arr.isMinLengthArray(${lengthText}, ${arrayText})`;\n\n const importFixes =\n index === 0 && !hasArrImport\n ? buildImportFixes(fixer, program, tsDataForgeImport, ['Arr'])\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n"],"names":[],"mappings":";;;AAgBO,MAAM,yBAAA,GAGT;AAAA,EACF,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM;AAAA,MACJ,WAAA,EACE;AAAA,KACJ;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,QAAQ,EAAC;AAAA,IACT,QAAA,EAAU;AAAA,MACR,mBAAA,EACE;AAAA;AACJ,GACF;AAAA,EAEA,MAAA,EAAQ,CAAC,OAAA,KAAY;AACnB,IAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAE3B,IAAA,MAAM,UAAU,UAAA,CAAW,GAAA;AAE3B,IAAA,MAAM,iBAAA,GAAoB,qBAAqB,OAAO,CAAA;AAEtD,IAAA,MAAM,QAAA,GAAW,QAAQ,UAAA,CAAW,cAAA;AAEpC,IAAA,MAAM,iBAIA,EAAC;AAEP,IAAA,OAAO;AAAA,MACL,gBAAA,EAAkB,CAAC,IAAA,KAAS;AAE1B,QAAA,IAAI,IAAA,CAAK,QAAA,KAAa,IAAA,IAAQ,IAAA,CAAK,aAAa,IAAA,EAAM;AAItD,QAAA,IAAI,0BAAA,CAA2B,IAAA,EAAM,UAAU,CAAA,EAAG;AAGlD,QAAA,MAAM,cAAA,GAAiB,KAAK,QAAA,KAAa,IAAA;AAEzC,QAAA,MAAM,UAAA,GAAa,IAAA,CAAK,cAAA,GAAiB,MAAA,GAAS,OAAO,CAAA;AAEzD,QAAA,MAAM,SAAA,GAAY,IAAA,CAAK,cAAA,GAAiB,OAAA,GAAU,MAAM,CAAA;AAGxD,QAAA,IAAI,CAAC,cAAA,CAAe,UAAU,CAAA,EAAG;AAGjC,QAAA,IAAI,CAAC,0BAAA,CAA2B,SAAA,EAAW,UAAU,CAAA,EAAG;AAGxD,QAAA,MAAM,kBAAkB,UAAA,CAAW,MAAA;AAGnC,QAAA,IAAI,QAAA,EAAU,OAAA,KAAY,MAAA,IAAa,QAAA,CAAS,YAAY,IAAA,EAAM;AAChE,UAAA,MAAM,OAAA,GAAU,QAAA,CAAS,OAAA,CAAQ,cAAA,EAAe;AAEhD,UAAA,MAAM,MAAA,GAAS,QAAA,CAAS,qBAAA,EAAuB,GAAA,CAAI,eAAe,CAAA;AAElE,UAAA,IAAI,WAAW,MAAA,EAAW;AACxB,YAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,iBAAA,CAAkB,MAAM,CAAA;AAG7C,YAAA,MAAM,cACJ,OAAA,CAAQ,WAAA,CAAY,IAAI,CAAA,IAAK,OAAA,CAAQ,YAAY,IAAI,CAAA;AAEvD,YAAA,IAAI,CAAC,WAAA,EAAa;AAAA,UACpB,CAAA,MAAO;AACL,YAAA;AAAA,UACF;AAAA,QACF,CAAA,MAAO;AACL,UAAA;AAAA,QACF;AAEA,QAAA,cAAA,CAAe,IAAA,CAAK;AAAA,UAClB,IAAA;AAAA,UACA,eAAA;AAAA,UACA,gBAAA,EAAkB;AAAA,SACnB,CAAA;AAAA,MACH,CAAA;AAAA,MACA,gBAAgB,MAAM;AACpB,QAAA,MAAM,YAAA,GAAe,gBAAgB,iBAAiB,CAAA;AAEtD,QAAA,MAAM,YAAA,GAAe,YAAA,CAAa,QAAA,CAAS,KAAK,CAAA;AAEhD,QAAA,KAAA,MAAW;AAAA,UACT,KAAA;AAAA,UACA,EAAE,IAAA,EAAM,eAAA,EAAiB,gBAAA;AAAiB,SAC5C,IAAK,cAAA,CAAe,OAAA,EAAQ,EAAG;AAC7B,UAAA,MAAM,SAAA,GAAY,UAAA,CAAW,OAAA,CAAQ,eAAe,CAAA;AAEpD,UAAA,MAAM,UAAA,GAAa,UAAA,CAAW,OAAA,CAAQ,gBAAgB,CAAA;AAEtD,UAAA,MAAM,YAAA,GAAe,UAAA,CAAW,OAAA,CAAQ,IAAI,CAAA;AAE5C,UAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,YACb,IAAA;AAAA,YACA,SAAA,EAAW,qBAAA;AAAA,YACX,IAAA,EAAM;AAAA,cACJ,QAAA,EAAU,YAAA;AAAA,cACV,SAAA,EAAW,SAAA;AAAA,cACX,MAAA,EAAQ;AAAA,aACV;AAAA,YACA,GAAA,EAAK,CAAC,KAAA,KAAU;AACd,cAAA,MAAM,WAAA,GAAc,CAAA,qBAAA,EAAwB,UAAU,CAAA,EAAA,EAAK,SAAS,CAAA,CAAA,CAAA;AAEpE,cAAA,MAAM,WAAA,GACJ,KAAA,KAAU,CAAA,IAAK,CAAC,YAAA,GACZ,gBAAA,CAAiB,KAAA,EAAO,OAAA,EAAS,iBAAA,EAAmB,CAAC,KAAK,CAAC,IAC3D,EAAC;AAEP,cAAA,OAAO,CAAC,GAAG,WAAA,EAAa,MAAM,WAAA,CAAY,IAAA,EAAM,WAAW,CAAC,CAAA;AAAA,YAC9D;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAAA,KACF;AAAA,EACF,CAAA;AAAA,EACA,gBAAgB;AAClB;;;;"}
@@ -27,13 +27,13 @@ const preferArrSum = {
27
27
  return;
28
28
  }
29
29
  const arrayExpression = node.callee.object;
30
- if (!Arr.isFixedLengthTuple(node.arguments, 2)) return;
30
+ if (!Arr.isFixedLengthTuple(2, node.arguments)) return;
31
31
  const reducer = node.arguments[0];
32
32
  const initialValue = node.arguments[1];
33
33
  if (initialValue.type !== AST_NODE_TYPES.Literal || initialValue.value !== 0) {
34
34
  return;
35
35
  }
36
- if (reducer.type !== AST_NODE_TYPES.ArrowFunctionExpression || !Arr.isFixedLengthTuple(reducer.params, 2)) {
36
+ if (reducer.type !== AST_NODE_TYPES.ArrowFunctionExpression || !Arr.isFixedLengthTuple(2, reducer.params)) {
37
37
  return;
38
38
  }
39
39
  const [param1, param2] = reducer.params;