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 +47 -19
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/plugin.d.mts +44 -1
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +34 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +3 -63
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.d.mts +6 -0
- package/dist/rules/prefer-canonical-length-guard.d.mts.map +1 -0
- package/dist/rules/prefer-canonical-length-guard.mjs +125 -0
- package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -0
- package/dist/rules/rules.d.mts +1 -5
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +2 -10
- package/dist/rules/rules.mjs.map +1 -1
- package/dist/types.d.mts +1 -0
- package/dist/types.d.mts.map +1 -1
- package/package.json +6 -2
- package/src/index.mts +1 -1
- package/src/plugin.mts +46 -3
- package/src/plugin.test.mts +24 -0
- package/src/rule-types.mts +3 -67
- package/src/rules/prefer-canonical-length-guard.mts +231 -0
- package/src/rules/prefer-canonical-length-guard.test.mts +307 -0
- package/src/rules/rules.mts +2 -10
- 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,7 @@ export default [
|
|
|
23
55
|
{
|
|
24
56
|
plugins: { 'ts-data-forge': eslintPluginTsDataForge },
|
|
25
57
|
rules: {
|
|
26
|
-
'ts-data-forge/prefer-
|
|
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-
|
|
40
|
-
| `prefer-arr-is-
|
|
41
|
-
| `prefer-arr-
|
|
42
|
-
| `prefer-
|
|
43
|
-
| `prefer-
|
|
44
|
-
| `prefer-
|
|
45
|
-
| `prefer-
|
|
46
|
-
| `prefer-
|
|
47
|
-
| `prefer-
|
|
48
|
-
| `
|
|
49
|
-
| `prefer-
|
|
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
|
package/dist/index.d.mts.map
CHANGED
|
@@ -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:
|
|
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
|
package/dist/plugin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AACA,OAAO,
|
|
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 };
|
package/dist/plugin.mjs.map
CHANGED
|
@@ -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\
|
|
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;;;;"}
|
package/dist/rule-types.d.mts
CHANGED
|
@@ -23,7 +23,7 @@ declare namespace PreferCanonicalArraySlicing {
|
|
|
23
23
|
type RuleEntry = Linter.StringSeverity;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* @description
|
|
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
|
|
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-
|
|
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":"AAKA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,KAAK,sBAAsB,CACzB,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,IACjD,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,OAAO,EAAE,GAC/B,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GACzC,CAAC,CAAC;AAEN;;;;GAIG;AACH,KAAK,aAAa,CAAC,IAAI,SAAS,MAAM,OAAO,gBAAgB,IAC3D,CAAC,OAAO,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,UAAU,CACzD,MAAM,WAAW,SAAS,MAAM,EAChC,MAAM,OAAO,SAAS,SAAS,OAAO,EAAE,CACzC,GACG,OAAO,GACP,KAAK,CAAC;AAEZ;;;;;;;;;;GAUG;AACH,kBAAU,2BAA2B,CAAC;IACpC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,
|
|
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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"}
|
package/dist/rules/rules.d.mts
CHANGED
|
@@ -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-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../src/rules/rules.mts"],"names":[],"mappings":"AAcA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;CAaa,CAAC"}
|
package/dist/rules/rules.mjs
CHANGED
|
@@ -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-
|
|
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,
|
package/dist/rules/rules.mjs.map
CHANGED
|
@@ -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 {
|
|
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
|
package/dist/types.d.mts.map
CHANGED
|
@@ -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.
|
|
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": "
|
|
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