eslint-plugin-ts-data-forge 0.1.7 → 0.2.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.
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,7 @@ 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',
27
59
  'ts-data-forge/prefer-is-record-and-has-key': 'error',
28
60
  // ...enable the rules you want
29
61
  } satisfies Partial<EslintTsDataForgeRules>,
@@ -33,24 +65,20 @@ export default [
33
65
 
34
66
  ## Rules
35
67
 
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. |
68
+ | Rule | Description |
69
+ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
70
+ | `prefer-canonical-array-slicing` | Unify non-mutating array add/remove patterns into `Arr.tail`/`skip`/`take`/etc. |
71
+ | `prefer-canonical-length-guard` | Normalize array-length checks to the canonical `Arr` guard (`xs.length > 0` → `Arr.isNonEmpty`, `Arr.isFixedLengthTuple(xs, 0)` → `Arr.isEmpty`, …). |
72
+ | `prefer-arr-is-array` | Replace `Array.isArray` with `Arr.isArray`. |
73
+ | `prefer-arr-sum` | Replace `xs.reduce((a, b) => a + b, 0)` with `Arr.sum(xs)` / `Arr.sumBy(xs, fn)`. |
74
+ | `prefer-as-int` | Replace branded-number assertions (`x as Int`) with `asInt(x)`-style casts. |
75
+ | `prefer-is-non-null-object` | Replace `typeof u === 'object' && u !== null` with `isNonNullObject(u)`. |
76
+ | `prefer-range-for-loop` | Replace C-style `for` loops with `for (const i of range(begin, end))`. |
77
+ | `prefer-is-record-and-has-key` | Replace `Object.hasOwn(obj, key)` / `key in obj` with `isRecord(obj) && hasKey(…)`. |
78
+ | `prefer-num-safe-parse-int` | Replace `parseInt(x, 10)` with `Result.unwrapOkOr(Num.safeParseInt(x), NaN)`. |
79
+ | `prefer-num-safe-parse-float` | Replace `parseFloat(x)` / `Number(x)` with `Num.safeParseFloat`-based parsing. |
80
+ | `no-unnecessary-type-guard` | Flag `ts-data-forge` type-guard calls that do no narrowing (type-aware). |
81
+ | `prefer-comparison-over-nullish-guard` | Prefer direct `=== null` / `!== undefined` over `isNull` / `isNotUndefined` calls. |
54
82
 
55
83
  ## License
56
84
 
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,46 @@
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-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
+ readonly 'prefer-arr-sum': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrSum" | "useArrSumBy", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
11
+ readonly 'prefer-as-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useBrandedNumberCastFunction", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
12
+ readonly 'prefer-is-non-null-object': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNonNullObject", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
13
+ readonly 'prefer-range-for-loop': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useRangeForLoop", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
14
+ readonly 'prefer-is-record-and-has-key': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsRecordAndHasKey", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
15
+ readonly 'prefer-num-safe-parse-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseInt", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
16
+ readonly 'prefer-num-safe-parse-float': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useSafeParseFloat", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
17
+ readonly 'no-unnecessary-type-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"alwaysFalse" | "alwaysTrue" | "replaceTypeGuard", readonly [(Readonly<{
18
+ ignore?: readonly string[];
19
+ }> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
20
+ readonly 'prefer-comparison-over-nullish-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferComparison", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
21
+ };
22
+ readonly configs: {
23
+ /** Enables every rule of this plugin at `error`. */
24
+ readonly recommended: {
25
+ readonly name: 'ts-data-forge/recommended';
26
+ readonly plugins: {
27
+ readonly 'ts-data-forge': ESLintPlugin;
28
+ };
29
+ readonly rules: {
30
+ readonly 'ts-data-forge/prefer-canonical-array-slicing': 'error';
31
+ readonly 'ts-data-forge/prefer-canonical-length-guard': 'error';
32
+ readonly 'ts-data-forge/prefer-arr-is-array': 'error';
33
+ readonly 'ts-data-forge/prefer-arr-sum': 'error';
34
+ readonly 'ts-data-forge/prefer-as-int': 'error';
35
+ readonly 'ts-data-forge/prefer-is-non-null-object': 'error';
36
+ readonly 'ts-data-forge/prefer-range-for-loop': 'error';
37
+ readonly 'ts-data-forge/prefer-is-record-and-has-key': 'error';
38
+ readonly 'ts-data-forge/prefer-num-safe-parse-int': 'error';
39
+ readonly 'ts-data-forge/prefer-num-safe-parse-float': 'error';
40
+ readonly 'ts-data-forge/no-unnecessary-type-guard': 'error';
41
+ readonly 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error';
42
+ };
43
+ };
44
+ };
45
+ };
3
46
  //# 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;AAyCvE,eAAO,MAAM,uBAAuB;;iBAEhC,IAAI,EAAE,6BAA6B;;;;;;;;;;;;;;;;;;;QAInC,oDAAoD;iBACpD,WAAW;2BAtBP,2BAA2B;;yBAQ3B,eAAe,EAAI,YAAY;;;yEAzBW,OAAO;wEACR,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,43 @@
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-guard": "error",
6
+ "ts-data-forge/prefer-arr-is-array": "error",
7
+ "ts-data-forge/prefer-arr-sum": "error",
8
+ "ts-data-forge/prefer-as-int": "error",
9
+ "ts-data-forge/prefer-is-non-null-object": "error",
10
+ "ts-data-forge/prefer-range-for-loop": "error",
11
+ "ts-data-forge/prefer-is-record-and-has-key": "error",
12
+ "ts-data-forge/prefer-num-safe-parse-int": "error",
13
+ "ts-data-forge/prefer-num-safe-parse-float": "error",
14
+ "ts-data-forge/no-unnecessary-type-guard": "error",
15
+ "ts-data-forge/prefer-comparison-over-nullish-guard": "error"
16
+ };
17
+ const recommendedConfig = {
18
+ name: "ts-data-forge/recommended",
19
+ plugins: {
20
+ // Resolved lazily so that this config registers the *same* object that
21
+ // `eslintPluginTsDataForge` refers to. ESLint rejects a plugin name that
22
+ // maps to two different objects with `Cannot redefine plugin`, which is
23
+ // what a user combining this preset with their own
24
+ // `plugins: { 'ts-data-forge': eslintPluginTsDataForge }` entry would hit
25
+ // if the preset embedded a separate copy of the plugin.
26
+ get "ts-data-forge"() {
27
+ return eslintPluginTsDataForge;
28
+ }
29
+ },
30
+ rules: recommendedRules
31
+ };
3
32
  const eslintPluginTsDataForge = {
4
33
  meta: {
5
34
  name: "eslint-plugin-ts-data-forge"
6
35
  },
7
- rules: tsDataForgeRules
36
+ rules: tsDataForgeRules,
37
+ configs: {
38
+ /** Enables every rule of this plugin at `error`. */
39
+ recommended: recommendedConfig
40
+ }
8
41
  };
9
42
 
10
43
  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-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,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 degenerate guards (e.g. `Arr.isFixedLengthTuple(xs, 0)`) become `Arr.isEmpty` / `Arr.isNonEmpty`.
27
27
  *
28
28
  * ```md
29
29
  * | key | value |
@@ -33,49 +33,7 @@ declare namespace PreferCanonicalArraySlicing {
33
33
  * | fixable | code |
34
34
  * ```
35
35
  */
36
- declare namespace PreferArrIsMinLengthArray {
37
- type RuleEntry = Linter.StringSeverity;
38
- }
39
- /**
40
- * @description Replace `xs.length <= n` with `Arr.isMaxLengthArray(xs, n)` from ts-data-forge.
41
- *
42
- * ```md
43
- * | key | value |
44
- * | :--------- | :--------- |
45
- * | type | suggestion |
46
- * | deprecated | false |
47
- * | fixable | code |
48
- * ```
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 {
36
+ declare namespace PreferCanonicalLengthGuard {
79
37
  type RuleEntry = Linter.StringSeverity;
80
38
  }
81
39
  /**
@@ -92,20 +50,6 @@ declare namespace PreferArrIsFixedLengthArray {
92
50
  declare namespace PreferArrIsArray {
93
51
  type RuleEntry = Linter.StringSeverity;
94
52
  }
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
53
  /**
110
54
  * @description Replace `xs.reduce((a, b) => a + b, 0)` with `Arr.sum(xs)` or `Arr.sumBy(xs, fn)` from ts-data-forge.
111
55
  *
@@ -256,12 +200,8 @@ declare namespace PreferComparisonOverNullishGuard {
256
200
  }
257
201
  export type EslintTsDataForgeRules = Readonly<{
258
202
  '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;
203
+ 'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
263
204
  'ts-data-forge/prefer-arr-is-array': PreferArrIsArray.RuleEntry;
264
- 'ts-data-forge/prefer-arr-is-non-empty': PreferArrIsNonEmpty.RuleEntry;
265
205
  'ts-data-forge/prefer-arr-sum': PreferArrSum.RuleEntry;
266
206
  'ts-data-forge/prefer-as-int': PreferAsInt.RuleEntry;
267
207
  '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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"}
@@ -0,0 +1,6 @@
1
+ import { type TSESLint } from '@typescript-eslint/utils';
2
+ type Options = readonly [];
3
+ type MessageIds = 'useCanonicalGuard' | 'useIsBoundedLengthArray' | 'useIsFixedLengthArray' | 'useIsMaxLengthArray' | 'useIsMinLengthArray' | 'useIsNonEmpty';
4
+ export declare const preferCanonicalLengthGuard: TSESLint.RuleModule<MessageIds, Options>;
5
+ export {};
6
+ //# sourceMappingURL=prefer-canonical-length-guard.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer-canonical-length-guard.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-canonical-length-guard.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAQlC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GACX,mBAAmB,GACnB,yBAAyB,GACzB,uBAAuB,GACvB,qBAAqB,GACrB,qBAAqB,GACrB,eAAe,CAAC;AA8DpB,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,UAAU,CAC1D,UAAU,EACV,OAAO,CAqEC,CAAC"}
@@ -0,0 +1,125 @@
1
+ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
2
+ import { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';
3
+ import { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';
4
+ import { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';
5
+ import { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';
6
+ import { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';
7
+ import { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';
8
+
9
+ const COMPARISON_RULES = [
10
+ preferArrIsNonEmpty,
11
+ preferArrIsMinLengthArray,
12
+ preferArrIsMaxLengthArray,
13
+ preferArrIsBoundedLengthArray,
14
+ preferArrIsFixedLengthArray
15
+ ];
16
+ const MESSAGES = {
17
+ useCanonicalGuard: "Replace `{{arrName}}.{{guard}}({{boundsText}})` with `{{arrName}}.{{replacement}}(...)`: the bound makes the two guards equivalent.",
18
+ useIsNonEmpty: preferArrIsNonEmpty.meta.messages.useIsNonEmpty,
19
+ useIsMinLengthArray: preferArrIsMinLengthArray.meta.messages.useIsMinLengthArray,
20
+ useIsMaxLengthArray: preferArrIsMaxLengthArray.meta.messages.useIsMaxLengthArray,
21
+ useIsBoundedLengthArray: preferArrIsBoundedLengthArray.meta.messages.useIsBoundedLengthArray,
22
+ useIsFixedLengthArray: preferArrIsFixedLengthArray.meta.messages.useIsFixedLengthArray
23
+ };
24
+ const GUARD_REWRITES = [
25
+ { guard: "isFixedLengthArray", bounds: [0], replacement: "isEmpty" },
26
+ { guard: "isFixedLengthTuple", bounds: [0], replacement: "isEmpty" },
27
+ { guard: "isMaxLengthTuple", bounds: [0], replacement: "isEmpty" },
28
+ { guard: "isMaxLengthArray", bounds: [0], replacement: "isEmpty" },
29
+ { guard: "isBoundedLengthTuple", bounds: [0, 0], replacement: "isEmpty" },
30
+ { guard: "isBoundedLengthArray", bounds: [0, 0], replacement: "isEmpty" },
31
+ { guard: "isMinLengthArray", bounds: [1], replacement: "isNonEmpty" },
32
+ { guard: "isMinLengthTuple", bounds: [1], replacement: "isNonEmpty" }
33
+ ];
34
+ const preferCanonicalLengthGuard = {
35
+ meta: {
36
+ type: "suggestion",
37
+ docs: {
38
+ description: "Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and degenerate guards (e.g. `Arr.isFixedLengthTuple(xs, 0)`) become `Arr.isEmpty` / `Arr.isNonEmpty`."
39
+ },
40
+ fixable: "code",
41
+ schema: [],
42
+ messages: MESSAGES
43
+ },
44
+ create: (context) => {
45
+ const sourceCode = context.sourceCode;
46
+ const arrLocalName = getImportedLocalName(
47
+ getTsDataForgeImport(sourceCode.ast),
48
+ "Arr"
49
+ );
50
+ const comparisonVisitors = COMPARISON_RULES.map(
51
+ (rule) => rule.create(context)
52
+ );
53
+ if (arrLocalName === void 0) return mergeVisitors(comparisonVisitors);
54
+ return mergeVisitors([
55
+ ...comparisonVisitors,
56
+ {
57
+ CallExpression: (node) => {
58
+ const guardName = getGuardName(node, arrLocalName);
59
+ if (guardName === void 0) return;
60
+ const rewrite = GUARD_REWRITES.find(
61
+ (entry) => entry.guard === guardName
62
+ );
63
+ if (rewrite === void 0 || !matchesBounds(node, rewrite.bounds)) {
64
+ return;
65
+ }
66
+ const [array] = node.arguments;
67
+ if (array === void 0) return;
68
+ const arrayText = sourceCode.getText(array);
69
+ context.report({
70
+ node,
71
+ messageId: "useCanonicalGuard",
72
+ data: {
73
+ arrName: arrLocalName,
74
+ guard: guardName,
75
+ boundsText: ["\u2026", ...rewrite.bounds.map(String)].join(", "),
76
+ replacement: rewrite.replacement
77
+ },
78
+ fix: (fixer) => fixer.replaceText(
79
+ node,
80
+ `${arrLocalName}.${rewrite.replacement}(${arrayText})`
81
+ )
82
+ });
83
+ }
84
+ }
85
+ ]);
86
+ },
87
+ defaultOptions: []
88
+ };
89
+ const mergeVisitors = (visitors) => {
90
+ const mut_merged = {};
91
+ for (const visitor of visitors) {
92
+ for (const [selector, handler] of Object.entries(visitor)) {
93
+ if (typeof handler !== "function") continue;
94
+ const mut_handlers = mut_merged[selector] ?? [];
95
+ mut_handlers.push(handler);
96
+ mut_merged[selector] = mut_handlers;
97
+ }
98
+ }
99
+ return Object.fromEntries(
100
+ Object.entries(mut_merged).map(([selector, handlers]) => [
101
+ selector,
102
+ (node) => {
103
+ for (const handler of handlers) {
104
+ handler(node);
105
+ }
106
+ }
107
+ ])
108
+ );
109
+ };
110
+ const getGuardName = (node, arrLocalName) => {
111
+ const { callee } = node;
112
+ return callee.type === AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES.Identifier && callee.object.name === arrLocalName && callee.property.type === AST_NODE_TYPES.Identifier ? callee.property.name : void 0;
113
+ };
114
+ const matchesBounds = (node, bounds) => {
115
+ const args = node.arguments;
116
+ if (args.length !== bounds.length + 1 || node.typeArguments !== void 0) {
117
+ return false;
118
+ }
119
+ return args.every(
120
+ (arg, index) => arg.type !== AST_NODE_TYPES.SpreadElement && (index === 0 || arg.type === AST_NODE_TYPES.Literal && arg.value === bounds[index - 1])
121
+ );
122
+ };
123
+
124
+ export { preferCanonicalLengthGuard };
125
+ //# sourceMappingURL=prefer-canonical-length-guard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer-canonical-length-guard.mjs","sources":["../../src/rules/prefer-canonical-length-guard.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { getImportedLocalName, getTsDataForgeImport } from './import-utils.mjs';\nimport { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';\nimport { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';\nimport { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';\nimport { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';\nimport { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';\n\ntype Options = readonly [];\n\ntype MessageIds =\n | 'useCanonicalGuard'\n | 'useIsBoundedLengthArray'\n | 'useIsFixedLengthArray'\n | 'useIsMaxLengthArray'\n | 'useIsMinLengthArray'\n | 'useIsNonEmpty';\n\n/**\n * The `xs.length <op> n` → `Arr.is*` rules folded into this one.\n *\n * Their implementations are reused as-is rather than rewritten: this rule\n * merges their visitors, so every case they already cover (type-aware array\n * checks, `Arr` import insertion, the `&&`-bounded-range special case) keeps\n * behaving exactly as before, and their co-located tests keep exercising them\n * directly.\n */\nconst COMPARISON_RULES = [\n preferArrIsNonEmpty,\n preferArrIsMinLengthArray,\n preferArrIsMaxLengthArray,\n preferArrIsBoundedLengthArray,\n preferArrIsFixedLengthArray,\n] as const;\n\nconst MESSAGES = {\n useCanonicalGuard:\n 'Replace `{{arrName}}.{{guard}}({{boundsText}})` with `{{arrName}}.{{replacement}}(...)`: the bound makes the two guards equivalent.',\n useIsNonEmpty: preferArrIsNonEmpty.meta.messages.useIsNonEmpty,\n useIsMinLengthArray:\n preferArrIsMinLengthArray.meta.messages.useIsMinLengthArray,\n useIsMaxLengthArray:\n preferArrIsMaxLengthArray.meta.messages.useIsMaxLengthArray,\n useIsBoundedLengthArray:\n preferArrIsBoundedLengthArray.meta.messages.useIsBoundedLengthArray,\n useIsFixedLengthArray:\n preferArrIsFixedLengthArray.meta.messages.useIsFixedLengthArray,\n} as const satisfies Record<MessageIds, string>;\n\n/**\n * Length guards whose bound makes them redundant with `Arr.isEmpty` /\n * `Arr.isNonEmpty`, keyed by guard name.\n *\n * `isEmpty` narrows to `FixedLengthArray<0, E> & Xs` and `isNonEmpty` to\n * `MinLengthArray<1, E> & Xs`, so:\n *\n * - The `*Array` entries are **type-identical** — `isFixedLengthArray(xs, 0)`\n * and `isMinLengthArray(xs, 1)` produce exactly those types.\n * - The `*Tuple` entries **narrow**: they resolve to the structural\n * `readonly []` / `readonly [E, ...E[]]`, and the canonical guards add the\n * brand on top. The result stays assignable everywhere the old type was, so\n * the rewrite is safe, but it is a strengthening rather than a pure rename.\n */\nconst GUARD_REWRITES = [\n { guard: 'isFixedLengthArray', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isFixedLengthTuple', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isMaxLengthTuple', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isMaxLengthArray', bounds: [0], replacement: 'isEmpty' },\n { guard: 'isBoundedLengthTuple', bounds: [0, 0], replacement: 'isEmpty' },\n { guard: 'isBoundedLengthArray', bounds: [0, 0], replacement: 'isEmpty' },\n { guard: 'isMinLengthArray', bounds: [1], replacement: 'isNonEmpty' },\n { guard: 'isMinLengthTuple', bounds: [1], replacement: 'isNonEmpty' },\n] as const satisfies readonly Readonly<{\n guard: string;\n bounds: readonly number[];\n replacement: string;\n}>[];\n\nexport const preferCanonicalLengthGuard: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Normalize array-length checks to their canonical `Arr` guard: `xs.length <op> n` becomes the matching `Arr.is*` guard, and degenerate guards (e.g. `Arr.isFixedLengthTuple(xs, 0)`) become `Arr.isEmpty` / `Arr.isNonEmpty`.',\n },\n fixable: 'code',\n schema: [],\n messages: MESSAGES,\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const arrLocalName = getImportedLocalName(\n getTsDataForgeImport(sourceCode.ast),\n 'Arr',\n );\n\n const comparisonVisitors = COMPARISON_RULES.map((rule) =>\n rule.create(context),\n );\n\n if (arrLocalName === undefined) return mergeVisitors(comparisonVisitors);\n\n return mergeVisitors([\n ...comparisonVisitors,\n {\n CallExpression: (node) => {\n const guardName = getGuardName(node, arrLocalName);\n\n if (guardName === undefined) return;\n\n const rewrite = GUARD_REWRITES.find(\n (entry) => entry.guard === guardName,\n );\n\n if (rewrite === undefined || !matchesBounds(node, rewrite.bounds)) {\n return;\n }\n\n const [array] = node.arguments;\n\n if (array === undefined) return;\n\n const arrayText = sourceCode.getText(array);\n\n context.report({\n node,\n messageId: 'useCanonicalGuard',\n data: {\n arrName: arrLocalName,\n guard: guardName,\n boundsText: ['…', ...rewrite.bounds.map(String)].join(', '),\n replacement: rewrite.replacement,\n },\n fix: (fixer) =>\n fixer.replaceText(\n node,\n `${arrLocalName}.${rewrite.replacement}(${arrayText})`,\n ),\n });\n },\n },\n ]);\n },\n defaultOptions: [],\n} as const;\n\n/**\n * Combines several rule listeners into one, calling every handler registered\n * for a given selector in order.\n */\nconst mergeVisitors = (\n // `RuleListener` holds mutable AST handler signatures, so it is not deeply\n // readonly.\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n visitors: readonly TSESLint.RuleListener[],\n): TSESLint.RuleListener => {\n const mut_merged: Record<string, ((node: never) => void)[]> = {};\n\n for (const visitor of visitors) {\n for (const [selector, handler] of Object.entries(visitor)) {\n if (typeof handler !== 'function') continue;\n\n const mut_handlers = mut_merged[selector] ?? [];\n\n mut_handlers.push(handler);\n\n mut_merged[selector] = mut_handlers;\n }\n }\n\n return Object.fromEntries(\n Object.entries(mut_merged).map(([selector, handlers]) => [\n selector,\n (node: never) => {\n for (const handler of handlers) {\n handler(node);\n }\n },\n ]),\n );\n};\n\n/** The `Arr.<guard>` method name of `node`, or `undefined` for other calls. */\nconst getGuardName = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.CallExpression,\n arrLocalName: string,\n): string | undefined => {\n const { callee } = node;\n\n return callee.type === AST_NODE_TYPES.MemberExpression &&\n !callee.computed &&\n callee.object.type === AST_NODE_TYPES.Identifier &&\n callee.object.name === arrLocalName &&\n callee.property.type === AST_NODE_TYPES.Identifier\n ? callee.property.name\n : undefined;\n};\n\n/**\n * Whether the call is `guard(array, ...bounds)` with every bound written as the\n * exact numeric literal the rewrite requires.\n */\nconst matchesBounds = (\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n node: TSESTree.CallExpression,\n bounds: readonly number[],\n): boolean => {\n const args = node.arguments;\n\n if (args.length !== bounds.length + 1 || node.typeArguments !== undefined) {\n return false;\n }\n\n return args.every(\n (arg, index) =>\n arg.type !== AST_NODE_TYPES.SpreadElement &&\n (index === 0 ||\n (arg.type === AST_NODE_TYPES.Literal &&\n arg.value === bounds[index - 1])),\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,7 @@
1
1
  export declare const tsDataForgeRules: {
2
2
  readonly 'prefer-canonical-array-slicing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferCanonicalArraySlicing", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
3
- readonly 'prefer-arr-is-min-length-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsMinLengthArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
4
- readonly 'prefer-arr-is-max-length-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsMaxLengthArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
5
- readonly 'prefer-arr-is-bounded-length-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsBoundedLengthArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
6
- readonly 'prefer-arr-is-fixed-length-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsFixedLengthArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
3
+ readonly 'prefer-canonical-length-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useCanonicalGuard" | "useIsBoundedLengthArray" | "useIsFixedLengthArray" | "useIsMaxLengthArray" | "useIsMinLengthArray" | "useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
4
  readonly 'prefer-arr-is-array': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrIsArray", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
- readonly 'prefer-arr-is-non-empty': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNonEmpty", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
9
5
  readonly 'prefer-arr-sum': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useArrSum" | "useArrSumBy", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
6
  readonly 'prefer-as-int': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useBrandedNumberCastFunction", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
11
7
  readonly 'prefer-is-non-null-object': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useIsNonNullObject", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;CAiBa,CAAC"}
1
+ {"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAcA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;CAaa,CAAC"}
@@ -1,13 +1,9 @@
1
1
  import { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';
2
2
  import { preferArrIsArray } from './prefer-arr-is-array.mjs';
3
- import { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';
4
- import { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';
5
- import { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';
6
- import { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';
7
- import { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';
8
3
  import { preferArrSum } from './prefer-arr-sum.mjs';
9
4
  import { preferAsInt } from './prefer-as-int.mjs';
10
5
  import { preferCanonicalArraySlicing } from './prefer-canonical-array-slicing.mjs';
6
+ import { preferCanonicalLengthGuard } from './prefer-canonical-length-guard.mjs';
11
7
  import { preferComparisonOverNullishGuard } from './prefer-comparison-over-nullish-guard.mjs';
12
8
  import { preferIsNonNullObject } from './prefer-is-non-null-object.mjs';
13
9
  import { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';
@@ -17,12 +13,8 @@ import { preferRangeForLoop } from './prefer-range-for-loop.mjs';
17
13
 
18
14
  const tsDataForgeRules = {
19
15
  "prefer-canonical-array-slicing": preferCanonicalArraySlicing,
20
- "prefer-arr-is-min-length-array": preferArrIsMinLengthArray,
21
- "prefer-arr-is-max-length-array": preferArrIsMaxLengthArray,
22
- "prefer-arr-is-bounded-length-array": preferArrIsBoundedLengthArray,
23
- "prefer-arr-is-fixed-length-array": preferArrIsFixedLengthArray,
16
+ "prefer-canonical-length-guard": preferCanonicalLengthGuard,
24
17
  "prefer-arr-is-array": preferArrIsArray,
25
- "prefer-arr-is-non-empty": preferArrIsNonEmpty,
26
18
  "prefer-arr-sum": preferArrSum,
27
19
  "prefer-as-int": preferAsInt,
28
20
  "prefer-is-non-null-object": preferIsNonNullObject,
@@ -1 +1 @@
1
- {"version":3,"file":"rules.mjs","sources":["../../src/rules/rules.mts"],"sourcesContent":["import { type ESLintPlugin } from '../types.mjs';\nimport { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';\nimport { preferArrIsArray } from './prefer-arr-is-array.mjs';\nimport { preferArrIsBoundedLengthArray } from './prefer-arr-is-bounded-length-array.mjs';\nimport { preferArrIsFixedLengthArray } from './prefer-arr-is-fixed-length-array.mjs';\nimport { preferArrIsMaxLengthArray } from './prefer-arr-is-max-length-array.mjs';\nimport { preferArrIsMinLengthArray } from './prefer-arr-is-min-length-array.mjs';\nimport { preferArrIsNonEmpty } from './prefer-arr-is-non-empty.mjs';\nimport { preferArrSum } from './prefer-arr-sum.mjs';\nimport { preferAsInt } from './prefer-as-int.mjs';\nimport { preferCanonicalArraySlicing } from './prefer-canonical-array-slicing.mjs';\nimport { preferComparisonOverNullishGuard } from './prefer-comparison-over-nullish-guard.mjs';\nimport { preferIsNonNullObject } from './prefer-is-non-null-object.mjs';\nimport { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';\nimport { preferNumSafeParseFloat } from './prefer-num-safe-parse-float.mjs';\nimport { preferNumSafeParseInt } from './prefer-num-safe-parse-int.mjs';\nimport { preferRangeForLoop } from './prefer-range-for-loop.mjs';\n\nexport const tsDataForgeRules = {\n 'prefer-canonical-array-slicing': preferCanonicalArraySlicing,\n 'prefer-arr-is-min-length-array': preferArrIsMinLengthArray,\n 'prefer-arr-is-max-length-array': preferArrIsMaxLengthArray,\n 'prefer-arr-is-bounded-length-array': preferArrIsBoundedLengthArray,\n 'prefer-arr-is-fixed-length-array': preferArrIsFixedLengthArray,\n 'prefer-arr-is-array': preferArrIsArray,\n 'prefer-arr-is-non-empty': preferArrIsNonEmpty,\n 'prefer-arr-sum': preferArrSum,\n 'prefer-as-int': preferAsInt,\n 'prefer-is-non-null-object': preferIsNonNullObject,\n 'prefer-range-for-loop': preferRangeForLoop,\n 'prefer-is-record-and-has-key': preferIsRecordAndHasKey,\n 'prefer-num-safe-parse-int': preferNumSafeParseInt,\n 'prefer-num-safe-parse-float': preferNumSafeParseFloat,\n 'no-unnecessary-type-guard': noUnnecessaryTypeGuard,\n 'prefer-comparison-over-nullish-guard': preferComparisonOverNullishGuard,\n} as const satisfies ESLintPlugin['rules'];\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAkBO,MAAM,gBAAA,GAAmB;AAAA,EAC9B,gCAAA,EAAkC,2BAAA;AAAA,EAClC,gCAAA,EAAkC,yBAAA;AAAA,EAClC,gCAAA,EAAkC,yBAAA;AAAA,EAClC,oCAAA,EAAsC,6BAAA;AAAA,EACtC,kCAAA,EAAoC,2BAAA;AAAA,EACpC,qBAAA,EAAuB,gBAAA;AAAA,EACvB,yBAAA,EAA2B,mBAAA;AAAA,EAC3B,gBAAA,EAAkB,YAAA;AAAA,EAClB,eAAA,EAAiB,WAAA;AAAA,EACjB,2BAAA,EAA6B,qBAAA;AAAA,EAC7B,uBAAA,EAAyB,kBAAA;AAAA,EACzB,8BAAA,EAAgC,uBAAA;AAAA,EAChC,2BAAA,EAA6B,qBAAA;AAAA,EAC7B,6BAAA,EAA+B,uBAAA;AAAA,EAC/B,2BAAA,EAA6B,sBAAA;AAAA,EAC7B,sCAAA,EAAwC;AAC1C;;;;"}
1
+ {"version":3,"file":"rules.mjs","sources":["../../src/rules/rules.mts"],"sourcesContent":["import { type ESLintPlugin } from '../types.mjs';\nimport { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';\nimport { preferArrIsArray } from './prefer-arr-is-array.mjs';\nimport { preferArrSum } from './prefer-arr-sum.mjs';\nimport { preferAsInt } from './prefer-as-int.mjs';\nimport { preferCanonicalArraySlicing } from './prefer-canonical-array-slicing.mjs';\nimport { preferCanonicalLengthGuard } from './prefer-canonical-length-guard.mjs';\nimport { preferComparisonOverNullishGuard } from './prefer-comparison-over-nullish-guard.mjs';\nimport { preferIsNonNullObject } from './prefer-is-non-null-object.mjs';\nimport { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';\nimport { preferNumSafeParseFloat } from './prefer-num-safe-parse-float.mjs';\nimport { preferNumSafeParseInt } from './prefer-num-safe-parse-int.mjs';\nimport { preferRangeForLoop } from './prefer-range-for-loop.mjs';\n\nexport const tsDataForgeRules = {\n 'prefer-canonical-array-slicing': preferCanonicalArraySlicing,\n 'prefer-canonical-length-guard': preferCanonicalLengthGuard,\n 'prefer-arr-is-array': preferArrIsArray,\n 'prefer-arr-sum': preferArrSum,\n 'prefer-as-int': preferAsInt,\n 'prefer-is-non-null-object': preferIsNonNullObject,\n 'prefer-range-for-loop': preferRangeForLoop,\n 'prefer-is-record-and-has-key': preferIsRecordAndHasKey,\n 'prefer-num-safe-parse-int': preferNumSafeParseInt,\n 'prefer-num-safe-parse-float': preferNumSafeParseFloat,\n 'no-unnecessary-type-guard': noUnnecessaryTypeGuard,\n 'prefer-comparison-over-nullish-guard': preferComparisonOverNullishGuard,\n} as const satisfies ESLintPlugin['rules'];\n"],"names":[],"mappings":";;;;;;;;;;;;;AAcO,MAAM,gBAAA,GAAmB;AAAA,EAC9B,gCAAA,EAAkC,2BAAA;AAAA,EAClC,+BAAA,EAAiC,0BAAA;AAAA,EACjC,qBAAA,EAAuB,gBAAA;AAAA,EACvB,gBAAA,EAAkB,YAAA;AAAA,EAClB,eAAA,EAAiB,WAAA;AAAA,EACjB,2BAAA,EAA6B,qBAAA;AAAA,EAC7B,uBAAA,EAAyB,kBAAA;AAAA,EACzB,8BAAA,EAAgC,uBAAA;AAAA,EAChC,2BAAA,EAA6B,qBAAA;AAAA,EAC7B,6BAAA,EAA+B,uBAAA;AAAA,EAC/B,2BAAA,EAA6B,sBAAA;AAAA,EAC7B,sCAAA,EAAwC;AAC1C;;;;"}
package/dist/types.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type TSESLint } from '@typescript-eslint/utils';
2
2
  import { type DeepReadonly } from 'ts-type-forge';
3
3
  export type ESLintPlugin = DeepReadonly<TSESLint.FlatConfig.Plugin>;
4
+ export type ESLintFlatConfig = DeepReadonly<TSESLint.FlatConfig.Config>;
4
5
  //# sourceMappingURL=types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../src/types.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../src/types.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-ts-data-forge",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "ESLint rules that steer code toward ts-data-forge idioms.",
6
6
  "keywords": [
@@ -36,7 +36,7 @@
36
36
  "dependencies": {
37
37
  "@typescript-eslint/utils": "8.64.0",
38
38
  "ts-type-forge": "~7.2.1",
39
- "ts-data-forge": "12.2.2"
39
+ "ts-data-forge": "13.0.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@typescript-eslint/parser": "8.64.0",
@@ -51,6 +51,10 @@
51
51
  "node": ">=22.22.2",
52
52
  "pnpm": ">=9.0.0"
53
53
  },
54
+ "publishConfig": {
55
+ "access": "public",
56
+ "provenance": true
57
+ },
54
58
  "scripts": {
55
59
  "build": "tsx ./scripts/cmd/build.mts",
56
60
  "build:min": "tsx ./scripts/cmd/build.mts --skip-check",
package/src/index.mts CHANGED
@@ -4,4 +4,4 @@ export type {
4
4
  EslintTsDataForgeRulesOption,
5
5
  } from './rule-types.mjs';
6
6
  export { tsDataForgeRules } from './rules/index.mjs';
7
- export { type ESLintPlugin } from './types.mjs';
7
+ export { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';