eslint-plugin-ts-data-forge 0.4.8 → 0.5.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 +1 -0
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +2 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +15 -0
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.d.mts +6 -0
- package/dist/rules/prefer-obj-over-entries-round-trip.d.mts.map +1 -0
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs +218 -0
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -0
- package/dist/rules/rules.d.mts +1 -0
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +3 -1
- package/dist/rules/rules.mjs.map +1 -1
- package/package.json +6 -6
- package/src/plugin.mts +1 -0
- package/src/rule-types.mts +16 -0
- package/src/rules/prefer-obj-over-entries-round-trip.mts +546 -0
- package/src/rules/prefer-obj-over-entries-round-trip.test.mts +397 -0
- package/src/rules/rules.mts +2 -0
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ export default [
|
|
|
81
81
|
| `prefer-num-safe-parse-float` | Replace `parseFloat(x)` / `Number(x)` with `Num.safeParseFloat`-based parsing. |
|
|
82
82
|
| `no-unnecessary-type-guard` | Flag `ts-data-forge` type-guard calls that do no narrowing (type-aware). |
|
|
83
83
|
| `prefer-comparison-over-nullish-guard` | Prefer direct `=== null` / `!== undefined` over `isNull` / `isNotUndefined` calls. |
|
|
84
|
+
| `prefer-obj-over-entries-round-trip` | Replace an `Object.fromEntries(Object.entries(o).map/filter/flatMap(…))` round trip with `Obj.map` / `Obj.filter` / `Obj.filterMap`. |
|
|
84
85
|
|
|
85
86
|
## License
|
|
86
87
|
|
package/dist/plugin.d.mts
CHANGED
|
@@ -19,6 +19,7 @@ export declare const eslintPluginTsDataForge: {
|
|
|
19
19
|
ignore?: readonly string[];
|
|
20
20
|
}> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
21
21
|
readonly 'prefer-comparison-over-nullish-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferComparison", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
22
|
+
readonly 'prefer-obj-over-entries-round-trip': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useObjFilter" | "useObjFilterMap" | "useObjMap", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
22
23
|
};
|
|
23
24
|
readonly configs: {
|
|
24
25
|
/** Enables every rule of this plugin at `error`. */
|
|
@@ -41,6 +42,7 @@ export declare const eslintPluginTsDataForge: {
|
|
|
41
42
|
readonly 'ts-data-forge/prefer-num-safe-parse-float': 'error';
|
|
42
43
|
readonly 'ts-data-forge/no-unnecessary-type-guard': 'error';
|
|
43
44
|
readonly 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error';
|
|
45
|
+
readonly 'ts-data-forge/prefer-obj-over-entries-round-trip': 'error';
|
|
44
46
|
};
|
|
45
47
|
};
|
|
46
48
|
};
|
package/dist/plugin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","sourceRoot":"","sources":["../src/plugin.mts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AA4CvE,eAAO,MAAM,uBAAuB;;iBAEhC,IAAI,EAAE,6BAA6B;;;;;;;;;;;;;;;;;;;;;QAInC,oDAAoD;iBACpD,WAAW;2BAtBP,2BAA2B;;yBAQ3B,eAAe,EAAI,YAAY;;;yEA5BW,OAAO;uEACT,OAAO;wEACN,OAAO;8DACjB,OAAO;yDACZ,OAAO;wDACR,OAAO;oEACK,OAAO;gEACX,OAAO;uEACA,OAAO;oEACV,OAAO;sEACL,OAAO;oEACT,OAAO;+EACI,OAAO;6EACT,OAAO;;;;CA+B5B,CAAC"}
|
package/dist/plugin.mjs
CHANGED
|
@@ -13,7 +13,8 @@ const recommendedRules = {
|
|
|
13
13
|
"ts-data-forge/prefer-num-safe-parse-int": "error",
|
|
14
14
|
"ts-data-forge/prefer-num-safe-parse-float": "error",
|
|
15
15
|
"ts-data-forge/no-unnecessary-type-guard": "error",
|
|
16
|
-
"ts-data-forge/prefer-comparison-over-nullish-guard": "error"
|
|
16
|
+
"ts-data-forge/prefer-comparison-over-nullish-guard": "error",
|
|
17
|
+
"ts-data-forge/prefer-obj-over-entries-round-trip": "error"
|
|
17
18
|
};
|
|
18
19
|
const recommendedConfig = {
|
|
19
20
|
name: "ts-data-forge/recommended",
|
package/dist/plugin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","sources":["../src/plugin.mts"],"sourcesContent":["import { type ReadonlyRecord } from 'ts-type-forge';\nimport { tsDataForgeRules } from './rules/index.mjs';\nimport { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';\n\n/**\n * Every rule this plugin ships, at `error`.\n *\n * The `satisfies` clause below is keyed off {@link tsDataForgeRules}, so adding\n * a rule without listing it here fails to type-check.\n */\nconst recommendedRules = {\n 'ts-data-forge/prefer-canonical-array-slicing': 'error',\n 'ts-data-forge/prefer-canonical-length-cast': 'error',\n 'ts-data-forge/prefer-canonical-length-guard': 'error',\n 'ts-data-forge/prefer-arr-is-array': 'error',\n 'ts-data-forge/prefer-arr-sum': 'error',\n 'ts-data-forge/prefer-as-int': 'error',\n 'ts-data-forge/prefer-is-non-null-object': 'error',\n 'ts-data-forge/prefer-range-for-loop': 'error',\n 'ts-data-forge/prefer-is-record-and-has-key': 'error',\n 'ts-data-forge/prefer-num-safe-parse-int': 'error',\n 'ts-data-forge/prefer-num-safe-parse-float': 'error',\n 'ts-data-forge/no-unnecessary-type-guard': 'error',\n 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',\n} as const satisfies ReadonlyRecord<\n `ts-data-forge/${keyof typeof tsDataForgeRules}`,\n '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":";;AAUA,MAAM,gBAAA,GAAmB;AAAA,EACvB,8CAAA,EAAgD,OAAA;AAAA,EAChD,4CAAA,EAA8C,OAAA;AAAA,EAC9C,6CAAA,EAA+C,OAAA;AAAA,EAC/C,mCAAA,EAAqC,OAAA;AAAA,EACrC,8BAAA,EAAgC,OAAA;AAAA,EAChC,6BAAA,EAA+B,OAAA;AAAA,EAC/B,yCAAA,EAA2C,OAAA;AAAA,EAC3C,qCAAA,EAAuC,OAAA;AAAA,EACvC,4CAAA,EAA8C,OAAA;AAAA,EAC9C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,2CAAA,EAA6C,OAAA;AAAA,EAC7C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,oDAAA,EAAsD;
|
|
1
|
+
{"version":3,"file":"plugin.mjs","sources":["../src/plugin.mts"],"sourcesContent":["import { type ReadonlyRecord } from 'ts-type-forge';\nimport { tsDataForgeRules } from './rules/index.mjs';\nimport { type ESLintFlatConfig, type ESLintPlugin } from './types.mjs';\n\n/**\n * Every rule this plugin ships, at `error`.\n *\n * The `satisfies` clause below is keyed off {@link tsDataForgeRules}, so adding\n * a rule without listing it here fails to type-check.\n */\nconst recommendedRules = {\n 'ts-data-forge/prefer-canonical-array-slicing': 'error',\n 'ts-data-forge/prefer-canonical-length-cast': 'error',\n 'ts-data-forge/prefer-canonical-length-guard': 'error',\n 'ts-data-forge/prefer-arr-is-array': 'error',\n 'ts-data-forge/prefer-arr-sum': 'error',\n 'ts-data-forge/prefer-as-int': 'error',\n 'ts-data-forge/prefer-is-non-null-object': 'error',\n 'ts-data-forge/prefer-range-for-loop': 'error',\n 'ts-data-forge/prefer-is-record-and-has-key': 'error',\n 'ts-data-forge/prefer-num-safe-parse-int': 'error',\n 'ts-data-forge/prefer-num-safe-parse-float': 'error',\n 'ts-data-forge/no-unnecessary-type-guard': 'error',\n 'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',\n 'ts-data-forge/prefer-obj-over-entries-round-trip': 'error',\n} as const satisfies ReadonlyRecord<\n `ts-data-forge/${keyof typeof tsDataForgeRules}`,\n '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":";;AAUA,MAAM,gBAAA,GAAmB;AAAA,EACvB,8CAAA,EAAgD,OAAA;AAAA,EAChD,4CAAA,EAA8C,OAAA;AAAA,EAC9C,6CAAA,EAA+C,OAAA;AAAA,EAC/C,mCAAA,EAAqC,OAAA;AAAA,EACrC,8BAAA,EAAgC,OAAA;AAAA,EAChC,6BAAA,EAA+B,OAAA;AAAA,EAC/B,yCAAA,EAA2C,OAAA;AAAA,EAC3C,qCAAA,EAAuC,OAAA;AAAA,EACvC,4CAAA,EAA8C,OAAA;AAAA,EAC9C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,2CAAA,EAA6C,OAAA;AAAA,EAC7C,yCAAA,EAA2C,OAAA;AAAA,EAC3C,oDAAA,EAAsD,OAAA;AAAA,EACtD,kDAAA,EAAoD;AACtD,CAAA;AAKA,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
|
@@ -212,6 +212,20 @@ declare namespace NoUnnecessaryTypeGuard {
|
|
|
212
212
|
declare namespace PreferComparisonOverNullishGuard {
|
|
213
213
|
type RuleEntry = Linter.StringSeverity;
|
|
214
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* @description Replace an `Object.fromEntries(Object.entries(record).map/filter/flatMap(...))` round trip with `Obj.map` / `Obj.filter` / `Obj.filterMap` from ts-data-forge.
|
|
217
|
+
*
|
|
218
|
+
* ```md
|
|
219
|
+
* | key | value |
|
|
220
|
+
* | :--------- | :--------- |
|
|
221
|
+
* | type | suggestion |
|
|
222
|
+
* | deprecated | false |
|
|
223
|
+
* | fixable | code |
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
declare namespace PreferObjOverEntriesRoundTrip {
|
|
227
|
+
type RuleEntry = Linter.StringSeverity;
|
|
228
|
+
}
|
|
215
229
|
export type EslintTsDataForgeRules = Readonly<{
|
|
216
230
|
'ts-data-forge/prefer-canonical-array-slicing': PreferCanonicalArraySlicing.RuleEntry;
|
|
217
231
|
'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
|
|
@@ -226,6 +240,7 @@ export type EslintTsDataForgeRules = Readonly<{
|
|
|
226
240
|
'ts-data-forge/prefer-num-safe-parse-float': PreferNumSafeParseFloat.RuleEntry;
|
|
227
241
|
'ts-data-forge/no-unnecessary-type-guard': NoUnnecessaryTypeGuard.RuleEntry;
|
|
228
242
|
'ts-data-forge/prefer-comparison-over-nullish-guard': PreferComparisonOverNullishGuard.RuleEntry;
|
|
243
|
+
'ts-data-forge/prefer-obj-over-entries-round-trip': PreferObjOverEntriesRoundTrip.RuleEntry;
|
|
229
244
|
}>;
|
|
230
245
|
export type EslintTsDataForgeRulesOption = Readonly<{
|
|
231
246
|
'ts-data-forge/no-unnecessary-type-guard': NoUnnecessaryTypeGuard.Options;
|
|
@@ -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 = 'useObjFilter' | 'useObjFilterMap' | 'useObjMap';
|
|
4
|
+
export declare const preferObjOverEntriesRoundTrip: TSESLint.RuleModule<MessageIds, Options>;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=prefer-obj-over-entries-round-trip.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-obj-over-entries-round-trip.d.mts","sourceRoot":"","sources":["../../src/rules/prefer-obj-over-entries-round-trip.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAYlC,KAAK,OAAO,GAAG,SAAS,EAAE,CAAC;AAE3B,KAAK,UAAU,GAAG,cAAc,GAAG,iBAAiB,GAAG,WAAW,CAAC;AAgDnE,eAAO,MAAM,6BAA6B,EAAE,QAAQ,CAAC,UAAU,CAC7D,UAAU,EACV,OAAO,CAoJC,CAAC"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
+
import { Arr } from 'ts-data-forge';
|
|
3
|
+
import { getTsDataForgeImport, getNamedImports, getImportedLocalName, buildImportFixes } from './import-utils.mjs';
|
|
4
|
+
|
|
5
|
+
const MESSAGE_ID_OF = {
|
|
6
|
+
map: "useObjMap",
|
|
7
|
+
filter: "useObjFilter",
|
|
8
|
+
filterMap: "useObjFilterMap"
|
|
9
|
+
};
|
|
10
|
+
const preferObjOverEntriesRoundTrip = {
|
|
11
|
+
meta: {
|
|
12
|
+
type: "suggestion",
|
|
13
|
+
docs: {
|
|
14
|
+
description: "Replace an `Object.fromEntries(Object.entries(record).map/filter/flatMap(...))` round trip with `Obj.map` / `Obj.filter` / `Obj.filterMap` from ts-data-forge."
|
|
15
|
+
},
|
|
16
|
+
fixable: "code",
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
useObjMap: "Replace this `Object.fromEntries(Object.entries(\u2026).map(\u2026))` round trip with `Obj.map` from ts-data-forge, which keeps the key set of the record in the result type.",
|
|
20
|
+
useObjFilter: "Replace this `Object.fromEntries(Object.entries(\u2026).filter(\u2026))` round trip with `Obj.filter` from ts-data-forge, which keeps the key and value types of the record in the result type.",
|
|
21
|
+
useObjFilterMap: "Replace this `Object.fromEntries(Object.entries(\u2026).flatMap(\u2026))` round trip with `Obj.filterMap` from ts-data-forge, which keeps the key types of the record in the result type."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
create: (context) => {
|
|
25
|
+
const sourceCode = context.sourceCode;
|
|
26
|
+
const program = sourceCode.ast;
|
|
27
|
+
const tsDataForgeImport = getTsDataForgeImport(program);
|
|
28
|
+
const mut_roundTrips = [];
|
|
29
|
+
const buildParams = (roundTrip) => {
|
|
30
|
+
const scope = sourceCode.getScope(roundTrip.callback);
|
|
31
|
+
const dropped = new Set(roundTrip.dropped);
|
|
32
|
+
const keyName = roundTrip.keyParam !== void 0 && isReferencedOutside(scope, roundTrip.keyParam.name, dropped) ? roundTrip.keyParam.name : void 0;
|
|
33
|
+
const valueName = roundTrip.valueParam?.name;
|
|
34
|
+
if (keyName !== void 0) {
|
|
35
|
+
return `(${valueName ?? unusedValueParamName(scope)}, ${keyName})`;
|
|
36
|
+
}
|
|
37
|
+
return valueName !== void 0 && isReferencedOutside(scope, valueName, dropped) ? `(${valueName})` : "()";
|
|
38
|
+
};
|
|
39
|
+
const buildCallbackBody = (roundTrip, optionalName) => {
|
|
40
|
+
const { body } = roundTrip;
|
|
41
|
+
if (body.kind === "expression") {
|
|
42
|
+
return arrowBodyText(
|
|
43
|
+
sourceCode.getText(body.expression),
|
|
44
|
+
body.expression
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const testText = conditionalTestText(
|
|
48
|
+
sourceCode.getText(body.test),
|
|
49
|
+
body.test
|
|
50
|
+
);
|
|
51
|
+
const someText = `${optionalName}.some(${argumentText(sourceCode.getText(body.kept), body.kept)})`;
|
|
52
|
+
const noneText = `${optionalName}.none`;
|
|
53
|
+
return body.keepOnTrue ? `${testText} ? ${someText} : ${noneText}` : `${testText} ? ${noneText} : ${someText}`;
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
CallExpression: (node) => {
|
|
57
|
+
const roundTrip = parseEntriesRoundTrip(node);
|
|
58
|
+
if (roundTrip === void 0) return;
|
|
59
|
+
mut_roundTrips.push({ node, roundTrip });
|
|
60
|
+
},
|
|
61
|
+
"Program:exit": () => {
|
|
62
|
+
const namedImports = getNamedImports(tsDataForgeImport);
|
|
63
|
+
const objName = getImportedLocalName(tsDataForgeImport, "Obj") ?? "Obj";
|
|
64
|
+
const optionalName = getImportedLocalName(tsDataForgeImport, "Optional") ?? "Optional";
|
|
65
|
+
const needsOptional = mut_roundTrips.some(
|
|
66
|
+
({ roundTrip }) => roundTrip.body.kind === "optional"
|
|
67
|
+
);
|
|
68
|
+
const missingImports = ["Obj", "Optional"].filter(
|
|
69
|
+
(name) => (name !== "Optional" || needsOptional) && !namedImports.includes(name)
|
|
70
|
+
);
|
|
71
|
+
for (const [index, { node, roundTrip }] of mut_roundTrips.entries()) {
|
|
72
|
+
context.report({
|
|
73
|
+
node,
|
|
74
|
+
messageId: MESSAGE_ID_OF[roundTrip.method],
|
|
75
|
+
fix: (fixer) => {
|
|
76
|
+
const recordText = argumentText(
|
|
77
|
+
sourceCode.getText(roundTrip.record),
|
|
78
|
+
roundTrip.record
|
|
79
|
+
);
|
|
80
|
+
const replacement = `${objName}.${roundTrip.method}(${recordText}, ${buildParams(roundTrip)} => ${buildCallbackBody(roundTrip, optionalName)})`;
|
|
81
|
+
const importFixes = index === 0 && missingImports.length > 0 ? buildImportFixes(
|
|
82
|
+
fixer,
|
|
83
|
+
program,
|
|
84
|
+
tsDataForgeImport,
|
|
85
|
+
missingImports
|
|
86
|
+
) : [];
|
|
87
|
+
return [...importFixes, fixer.replaceText(node, replacement)];
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
defaultOptions: []
|
|
95
|
+
};
|
|
96
|
+
const parseEntriesRoundTrip = (node) => {
|
|
97
|
+
if (!isObjectStaticCall(node, "fromEntries")) return void 0;
|
|
98
|
+
if (!Arr.isFixedLengthTuple(1, node.arguments)) return void 0;
|
|
99
|
+
const [transform] = node.arguments;
|
|
100
|
+
if (transform.type !== AST_NODE_TYPES.CallExpression) return void 0;
|
|
101
|
+
const { callee } = transform;
|
|
102
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES.Identifier) {
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
const arrayMethod = callee.property.name;
|
|
106
|
+
if (arrayMethod !== "map" && arrayMethod !== "filter" && arrayMethod !== "flatMap") {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
const entriesCall = callee.object;
|
|
110
|
+
if (entriesCall.type !== AST_NODE_TYPES.CallExpression || !isObjectStaticCall(entriesCall, "entries") || !Arr.isFixedLengthTuple(1, entriesCall.arguments)) {
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
const [record] = entriesCall.arguments;
|
|
114
|
+
if (record.type === AST_NODE_TYPES.SpreadElement) return void 0;
|
|
115
|
+
if (!Arr.isFixedLengthTuple(1, transform.arguments)) return void 0;
|
|
116
|
+
const [callback] = transform.arguments;
|
|
117
|
+
if (callback.type !== AST_NODE_TYPES.ArrowFunctionExpression || callback.async || // An explicit return type is either redundant or a type predicate; the
|
|
118
|
+
// latter would be lost by the rewrite.
|
|
119
|
+
callback.returnType !== void 0 || !Arr.isFixedLengthTuple(1, callback.params) || callback.body.type === AST_NODE_TYPES.BlockStatement) {
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
const [entryPattern] = callback.params;
|
|
123
|
+
if (entryPattern.type !== AST_NODE_TYPES.ArrayPattern) return void 0;
|
|
124
|
+
const entryParams = parseEntryPattern(entryPattern);
|
|
125
|
+
if (entryParams === void 0) return void 0;
|
|
126
|
+
const body = callback.body;
|
|
127
|
+
const common = { record, callback, ...entryParams };
|
|
128
|
+
switch (arrayMethod) {
|
|
129
|
+
case "filter":
|
|
130
|
+
return {
|
|
131
|
+
...common,
|
|
132
|
+
method: "filter",
|
|
133
|
+
dropped: [],
|
|
134
|
+
body: { kind: "expression", expression: body }
|
|
135
|
+
};
|
|
136
|
+
case "map": {
|
|
137
|
+
const entry = parseKeptEntry(body, entryParams.keyParam);
|
|
138
|
+
return entry === void 0 ? void 0 : {
|
|
139
|
+
...common,
|
|
140
|
+
method: "map",
|
|
141
|
+
dropped: [entry.key],
|
|
142
|
+
body: { kind: "expression", expression: entry.value }
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
case "flatMap": {
|
|
146
|
+
if (body.type !== AST_NODE_TYPES.ConditionalExpression) return void 0;
|
|
147
|
+
const onTrue = parseSingletonEntry(body.consequent, entryParams.keyParam);
|
|
148
|
+
const onFalse = parseSingletonEntry(body.alternate, entryParams.keyParam);
|
|
149
|
+
const keepOnTrue = onTrue !== void 0 && isEmptyArray(body.alternate);
|
|
150
|
+
const kept = keepOnTrue ? onTrue : onFalse !== void 0 && isEmptyArray(body.consequent) ? onFalse : void 0;
|
|
151
|
+
return kept === void 0 ? void 0 : {
|
|
152
|
+
...common,
|
|
153
|
+
method: "filterMap",
|
|
154
|
+
dropped: [kept.key],
|
|
155
|
+
body: {
|
|
156
|
+
kind: "optional",
|
|
157
|
+
test: body.test,
|
|
158
|
+
kept: kept.value,
|
|
159
|
+
keepOnTrue
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const parseEntryPattern = (pattern) => {
|
|
166
|
+
if (pattern.elements.length > 2) return void 0;
|
|
167
|
+
const [keyElement, valueElement] = pattern.elements;
|
|
168
|
+
return isElidedOrIdentifier(keyElement) && isElidedOrIdentifier(valueElement) ? {
|
|
169
|
+
keyParam: keyElement ?? void 0,
|
|
170
|
+
valueParam: valueElement ?? void 0
|
|
171
|
+
} : void 0;
|
|
172
|
+
};
|
|
173
|
+
const isElidedOrIdentifier = (element) => element === null || element === void 0 || element.type === AST_NODE_TYPES.Identifier;
|
|
174
|
+
const parseKeptEntry = (node, keyParam) => {
|
|
175
|
+
if (keyParam === void 0) return void 0;
|
|
176
|
+
const tuple = stripTypeWrappers(node);
|
|
177
|
+
if (tuple.type !== AST_NODE_TYPES.ArrayExpression || !Arr.isFixedLengthTuple(2, tuple.elements)) {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
const [key, value] = tuple.elements;
|
|
181
|
+
return key !== null && value !== null && key.type === AST_NODE_TYPES.Identifier && value.type !== AST_NODE_TYPES.SpreadElement && key.name === keyParam.name ? { key, value } : void 0;
|
|
182
|
+
};
|
|
183
|
+
const parseSingletonEntry = (node, keyParam) => {
|
|
184
|
+
const array = stripTypeWrappers(node);
|
|
185
|
+
if (array.type !== AST_NODE_TYPES.ArrayExpression || !Arr.isFixedLengthTuple(1, array.elements)) {
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
188
|
+
const [entry] = array.elements;
|
|
189
|
+
return entry === null || entry.type === AST_NODE_TYPES.SpreadElement ? void 0 : parseKeptEntry(entry, keyParam);
|
|
190
|
+
};
|
|
191
|
+
const isEmptyArray = (node) => {
|
|
192
|
+
const array = stripTypeWrappers(node);
|
|
193
|
+
return array.type === AST_NODE_TYPES.ArrayExpression && array.elements.length === 0;
|
|
194
|
+
};
|
|
195
|
+
const stripTypeWrappers = (node) => node.type === AST_NODE_TYPES.TSSatisfiesExpression || node.type === AST_NODE_TYPES.TSAsExpression && isConstAssertion(node) ? stripTypeWrappers(node.expression) : node;
|
|
196
|
+
const isConstAssertion = (node) => node.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference && node.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier && node.typeAnnotation.typeName.name === "const";
|
|
197
|
+
const isObjectStaticCall = (node, name) => node.callee.type === AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.object.type === AST_NODE_TYPES.Identifier && node.callee.object.name === "Object" && node.callee.property.type === AST_NODE_TYPES.Identifier && node.callee.property.name === name;
|
|
198
|
+
const isReferencedOutside = (scope, name, dropped) => scope.variables.find((variable) => variable.name === name)?.references.some((reference) => !dropped.has(reference.identifier)) ?? false;
|
|
199
|
+
const unusedValueParamName = (scope) => {
|
|
200
|
+
const taken = new Set(
|
|
201
|
+
scope.through.map((reference) => reference.identifier.name).concat(scope.variables.map((variable) => variable.name))
|
|
202
|
+
);
|
|
203
|
+
const firstFree = (candidate, suffix) => taken.has(candidate) ? firstFree(`_value${suffix + 1}`, suffix + 1) : candidate;
|
|
204
|
+
return firstFree("_value", 1);
|
|
205
|
+
};
|
|
206
|
+
const NEEDS_PARENS_AS_TEST = /* @__PURE__ */ new Set([
|
|
207
|
+
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
208
|
+
AST_NODE_TYPES.AssignmentExpression,
|
|
209
|
+
AST_NODE_TYPES.ConditionalExpression,
|
|
210
|
+
AST_NODE_TYPES.SequenceExpression,
|
|
211
|
+
AST_NODE_TYPES.YieldExpression
|
|
212
|
+
]);
|
|
213
|
+
const conditionalTestText = (text, node) => NEEDS_PARENS_AS_TEST.has(node.type) ? `(${text})` : text;
|
|
214
|
+
const argumentText = (text, node) => node.type === AST_NODE_TYPES.SequenceExpression ? `(${text})` : text;
|
|
215
|
+
const arrowBodyText = (text, node) => text.startsWith("{") || node.type === AST_NODE_TYPES.SequenceExpression ? `(${text})` : text;
|
|
216
|
+
|
|
217
|
+
export { preferObjOverEntriesRoundTrip };
|
|
218
|
+
//# sourceMappingURL=prefer-obj-over-entries-round-trip.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-obj-over-entries-round-trip.mjs","sources":["../../src/rules/prefer-obj-over-entries-round-trip.mts"],"sourcesContent":["import {\n AST_NODE_TYPES,\n type TSESLint,\n type TSESTree,\n} from '@typescript-eslint/utils';\nimport { Arr } from 'ts-data-forge';\nimport { type ReadonlyRecord } from 'ts-type-forge';\nimport {\n buildImportFixes,\n getImportedLocalName,\n getNamedImports,\n getTsDataForgeImport,\n} from './import-utils.mjs';\n\n/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\ntype Options = readonly [];\n\ntype MessageIds = 'useObjFilter' | 'useObjFilterMap' | 'useObjMap';\n\n/** The `Obj` function an entries round trip is rewritten to. */\ntype ObjMethod = 'filter' | 'filterMap' | 'map';\n\nconst MESSAGE_ID_OF = {\n map: 'useObjMap',\n filter: 'useObjFilter',\n filterMap: 'useObjFilterMap',\n} as const satisfies ReadonlyRecord<ObjMethod, MessageIds>;\n\n/**\n * The body the rewritten callback gets:\n *\n * - `expression` — the mapped value (`Obj.map`) or the predicate (`Obj.filter`),\n * used verbatim.\n * - `optional` — the two branches of the `flatMap` callback's conditional,\n * rebuilt as `Optional.some(...)` / `Optional.none` (`Obj.filterMap`).\n */\ntype CallbackBody = Readonly<\n | { kind: 'expression'; expression: TSESTree.Expression }\n | {\n kind: 'optional';\n test: TSESTree.Expression;\n kept: TSESTree.Expression;\n keepOnTrue: boolean;\n }\n>;\n\ntype RoundTrip = Readonly<{\n method: ObjMethod;\n /** The record `Object.entries` was called on. */\n record: TSESTree.Expression;\n /** The callback given to `map` / `filter` / `flatMap`. */\n callback: TSESTree.ArrowFunctionExpression;\n /** `k` in `([k, v]) => …`, absent when the entry pattern elides it. */\n keyParam: TSESTree.Identifier | undefined;\n /** `v` in `([k, v]) => …`, absent when the entry pattern elides it. */\n valueParam: TSESTree.Identifier | undefined;\n /**\n * The key identifiers the rewrite drops — the ones that only rebuilt the\n * entry. They are excluded when deciding whether the callback still needs a\n * key parameter.\n */\n dropped: readonly TSESTree.Identifier[];\n body: CallbackBody;\n}>;\n\nexport const preferObjOverEntriesRoundTrip: TSESLint.RuleModule<\n MessageIds,\n Options\n> = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Replace an `Object.fromEntries(Object.entries(record).map/filter/flatMap(...))` round trip with `Obj.map` / `Obj.filter` / `Obj.filterMap` from ts-data-forge.',\n },\n fixable: 'code',\n schema: [],\n messages: {\n useObjMap:\n 'Replace this `Object.fromEntries(Object.entries(…).map(…))` round trip with `Obj.map` from ts-data-forge, which keeps the key set of the record in the result type.',\n useObjFilter:\n 'Replace this `Object.fromEntries(Object.entries(…).filter(…))` round trip with `Obj.filter` from ts-data-forge, which keeps the key and value types of the record in the result type.',\n useObjFilterMap:\n 'Replace this `Object.fromEntries(Object.entries(…).flatMap(…))` round trip with `Obj.filterMap` from ts-data-forge, which keeps the key types of the record in the result type.',\n },\n },\n\n create: (context) => {\n const sourceCode = context.sourceCode;\n\n const program = sourceCode.ast;\n\n const tsDataForgeImport = getTsDataForgeImport(program);\n\n const mut_roundTrips: {\n node: TSESTree.CallExpression;\n roundTrip: RoundTrip;\n }[] = [];\n\n /**\n * Rebuilds the callback's parameter list. The entry pattern binds\n * `[key, value]`; `Obj` passes `(value, key)`, and either parameter is\n * dropped when the rewritten body no longer refers to it.\n */\n const buildParams = (roundTrip: RoundTrip): string => {\n const scope = sourceCode.getScope(roundTrip.callback);\n\n const dropped = new Set<TSESTree.Node>(roundTrip.dropped);\n\n const keyName =\n roundTrip.keyParam !== undefined &&\n isReferencedOutside(scope, roundTrip.keyParam.name, dropped)\n ? roundTrip.keyParam.name\n : undefined;\n\n const valueName = roundTrip.valueParam?.name;\n\n if (keyName !== undefined) {\n // `Obj` puts the value first, so a callback that only needs the key\n // still has to name a value parameter.\n return `(${valueName ?? unusedValueParamName(scope)}, ${keyName})`;\n }\n\n return valueName !== undefined &&\n isReferencedOutside(scope, valueName, dropped)\n ? `(${valueName})`\n : '()';\n };\n\n const buildCallbackBody = (\n roundTrip: RoundTrip,\n optionalName: string,\n ): string => {\n const { body } = roundTrip;\n\n if (body.kind === 'expression') {\n return arrowBodyText(\n sourceCode.getText(body.expression),\n body.expression,\n );\n }\n\n const testText = conditionalTestText(\n sourceCode.getText(body.test),\n body.test,\n );\n\n const someText = `${optionalName}.some(${argumentText(sourceCode.getText(body.kept), body.kept)})`;\n\n const noneText = `${optionalName}.none`;\n\n return body.keepOnTrue\n ? `${testText} ? ${someText} : ${noneText}`\n : `${testText} ? ${noneText} : ${someText}`;\n };\n\n return {\n CallExpression: (node) => {\n const roundTrip = parseEntriesRoundTrip(node);\n\n if (roundTrip === undefined) return;\n\n mut_roundTrips.push({ node, roundTrip });\n },\n\n 'Program:exit': () => {\n const namedImports = getNamedImports(tsDataForgeImport);\n\n const objName = getImportedLocalName(tsDataForgeImport, 'Obj') ?? 'Obj';\n\n const optionalName =\n getImportedLocalName(tsDataForgeImport, 'Optional') ?? 'Optional';\n\n const needsOptional = mut_roundTrips.some(\n ({ roundTrip }) => roundTrip.body.kind === 'optional',\n );\n\n const missingImports = (['Obj', 'Optional'] as const).filter(\n (name) =>\n (name !== 'Optional' || needsOptional) &&\n !namedImports.includes(name),\n );\n\n // Note: the import is added by the first fix only, so that several\n // fixes in one file do not each insert it at the same position.\n for (const [index, { node, roundTrip }] of mut_roundTrips.entries()) {\n context.report({\n node,\n messageId: MESSAGE_ID_OF[roundTrip.method],\n fix: (fixer) => {\n const recordText = argumentText(\n sourceCode.getText(roundTrip.record),\n roundTrip.record,\n );\n\n const replacement = `${objName}.${roundTrip.method}(${recordText}, ${buildParams(roundTrip)} => ${buildCallbackBody(roundTrip, optionalName)})`;\n\n const importFixes =\n index === 0 && missingImports.length > 0\n ? buildImportFixes(\n fixer,\n program,\n tsDataForgeImport,\n missingImports,\n )\n : [];\n\n return [...importFixes, fixer.replaceText(node, replacement)];\n },\n });\n }\n },\n };\n },\n defaultOptions: [],\n} as const;\n\n/**\n * Matches `Object.fromEntries(Object.entries(record).map|filter|flatMap(cb))`\n * and reports which `Obj` function replaces it, or `undefined` when the call is\n * not such a round trip or `cb` has a shape `Obj` cannot express (a key\n * transform, a block body, an entry-index parameter, …).\n */\nconst parseEntriesRoundTrip = (\n node: TSESTree.CallExpression,\n): RoundTrip | undefined => {\n if (!isObjectStaticCall(node, 'fromEntries')) return undefined;\n\n if (!Arr.isFixedLengthTuple(1, node.arguments)) return undefined;\n\n const [transform] = node.arguments;\n\n if (transform.type !== AST_NODE_TYPES.CallExpression) return undefined;\n\n const { callee } = transform;\n\n if (\n callee.type !== AST_NODE_TYPES.MemberExpression ||\n callee.computed ||\n callee.property.type !== AST_NODE_TYPES.Identifier\n ) {\n return undefined;\n }\n\n const arrayMethod = callee.property.name;\n\n if (\n arrayMethod !== 'map' &&\n arrayMethod !== 'filter' &&\n arrayMethod !== 'flatMap'\n ) {\n return undefined;\n }\n\n const entriesCall = callee.object;\n\n if (\n entriesCall.type !== AST_NODE_TYPES.CallExpression ||\n !isObjectStaticCall(entriesCall, 'entries') ||\n !Arr.isFixedLengthTuple(1, entriesCall.arguments)\n ) {\n return undefined;\n }\n\n const [record] = entriesCall.arguments;\n\n if (record.type === AST_NODE_TYPES.SpreadElement) return undefined;\n\n // A second argument (`thisArg`) or a callback that also takes the entry index\n // has no counterpart in `Obj`.\n if (!Arr.isFixedLengthTuple(1, transform.arguments)) return undefined;\n\n const [callback] = transform.arguments;\n\n if (\n callback.type !== AST_NODE_TYPES.ArrowFunctionExpression ||\n callback.async ||\n // An explicit return type is either redundant or a type predicate; the\n // latter would be lost by the rewrite.\n callback.returnType !== undefined ||\n !Arr.isFixedLengthTuple(1, callback.params) ||\n callback.body.type === AST_NODE_TYPES.BlockStatement\n ) {\n return undefined;\n }\n\n const [entryPattern] = callback.params;\n\n if (entryPattern.type !== AST_NODE_TYPES.ArrayPattern) return undefined;\n\n const entryParams = parseEntryPattern(entryPattern);\n\n if (entryParams === undefined) return undefined;\n\n const body = callback.body;\n\n const common = { record, callback, ...entryParams } as const;\n\n switch (arrayMethod) {\n case 'filter':\n return {\n ...common,\n method: 'filter',\n dropped: [],\n body: { kind: 'expression', expression: body },\n };\n\n case 'map': {\n const entry = parseKeptEntry(body, entryParams.keyParam);\n\n return entry === undefined\n ? undefined\n : {\n ...common,\n method: 'map',\n dropped: [entry.key],\n body: { kind: 'expression', expression: entry.value },\n };\n }\n\n case 'flatMap': {\n if (body.type !== AST_NODE_TYPES.ConditionalExpression) return undefined;\n\n const onTrue = parseSingletonEntry(body.consequent, entryParams.keyParam);\n\n const onFalse = parseSingletonEntry(body.alternate, entryParams.keyParam);\n\n const keepOnTrue = onTrue !== undefined && isEmptyArray(body.alternate);\n\n const kept = keepOnTrue\n ? onTrue\n : onFalse !== undefined && isEmptyArray(body.consequent)\n ? onFalse\n : undefined;\n\n return kept === undefined\n ? undefined\n : {\n ...common,\n method: 'filterMap',\n dropped: [kept.key],\n body: {\n kind: 'optional',\n test: body.test,\n kept: kept.value,\n keepOnTrue,\n },\n };\n }\n }\n};\n\ntype EntryParams = Readonly<{\n keyParam: TSESTree.Identifier | undefined;\n valueParam: TSESTree.Identifier | undefined;\n}>;\n\n/**\n * Reads the `[key, value]` destructuring the entry callback binds. Anything\n * beyond a plain binding — a default, a rest element, a nested pattern — is\n * rejected, because the rewritten callback receives the value and the key\n * directly rather than an entry tuple.\n */\nconst parseEntryPattern = (\n pattern: TSESTree.ArrayPattern,\n): EntryParams | undefined => {\n if (pattern.elements.length > 2) return undefined;\n\n const [keyElement, valueElement] = pattern.elements;\n\n return isElidedOrIdentifier(keyElement) && isElidedOrIdentifier(valueElement)\n ? {\n keyParam: keyElement ?? undefined,\n valueParam: valueElement ?? undefined,\n }\n : undefined;\n};\n\nconst isElidedOrIdentifier = (\n element: TSESTree.DestructuringPattern | null | undefined,\n): element is TSESTree.Identifier | null | undefined =>\n element === null ||\n element === undefined ||\n element.type === AST_NODE_TYPES.Identifier;\n\ntype KeptEntry = Readonly<{\n /** The key identifier that only rebuilds the entry, dropped by the rewrite. */\n key: TSESTree.Identifier;\n value: TSESTree.Expression;\n}>;\n\n/**\n * Matches `[k, value]` — an entry rebuilt under the very key it came from,\n * which is the only shape `Obj.map` / `Obj.filterMap` can express. An entry\n * built under a different key is left alone.\n */\nconst parseKeptEntry = (\n node: TSESTree.Expression,\n keyParam: TSESTree.Identifier | undefined,\n): KeptEntry | undefined => {\n if (keyParam === undefined) return undefined;\n\n const tuple = stripTypeWrappers(node);\n\n if (\n tuple.type !== AST_NODE_TYPES.ArrayExpression ||\n !Arr.isFixedLengthTuple(2, tuple.elements)\n ) {\n return undefined;\n }\n\n const [key, value] = tuple.elements;\n\n return key !== null &&\n value !== null &&\n key.type === AST_NODE_TYPES.Identifier &&\n value.type !== AST_NODE_TYPES.SpreadElement &&\n key.name === keyParam.name\n ? { key, value }\n : undefined;\n};\n\n/** Matches `[[k, value]]` — the `flatMap` branch that keeps an entry. */\nconst parseSingletonEntry = (\n node: TSESTree.Expression,\n keyParam: TSESTree.Identifier | undefined,\n): KeptEntry | undefined => {\n const array = stripTypeWrappers(node);\n\n if (\n array.type !== AST_NODE_TYPES.ArrayExpression ||\n !Arr.isFixedLengthTuple(1, array.elements)\n ) {\n return undefined;\n }\n\n const [entry] = array.elements;\n\n return entry === null || entry.type === AST_NODE_TYPES.SpreadElement\n ? undefined\n : parseKeptEntry(entry, keyParam);\n};\n\n/** Matches `[]` — the `flatMap` branch that drops an entry. */\nconst isEmptyArray = (node: TSESTree.Expression): boolean => {\n const array = stripTypeWrappers(node);\n\n return (\n array.type === AST_NODE_TYPES.ArrayExpression && array.elements.length === 0\n );\n};\n\n/**\n * Looks through the `as const` / `satisfies` an entry tuple usually carries.\n * Any other assertion is left in place, so that a tuple asserted to a wider\n * type than the callback returns is not reported: `Obj` would infer the\n * narrower one and the rewrite would not type-check.\n */\nconst stripTypeWrappers = (node: TSESTree.Expression): TSESTree.Expression =>\n node.type === AST_NODE_TYPES.TSSatisfiesExpression ||\n (node.type === AST_NODE_TYPES.TSAsExpression && isConstAssertion(node))\n ? stripTypeWrappers(node.expression)\n : node;\n\nconst isConstAssertion = (node: TSESTree.TSAsExpression): boolean =>\n node.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&\n node.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier &&\n node.typeAnnotation.typeName.name === 'const';\n\nconst isObjectStaticCall = (\n node: TSESTree.CallExpression,\n name: 'entries' | 'fromEntries',\n): boolean =>\n node.callee.type === AST_NODE_TYPES.MemberExpression &&\n !node.callee.computed &&\n node.callee.object.type === AST_NODE_TYPES.Identifier &&\n node.callee.object.name === 'Object' &&\n node.callee.property.type === AST_NODE_TYPES.Identifier &&\n node.callee.property.name === name;\n\n/**\n * `true` when the parameter is still referenced once the identifiers in\n * `dropped` — the ones that only rebuilt the entry key — are discounted.\n * References from nested functions resolve to the same variable, so they count.\n */\nconst isReferencedOutside = (\n scope: TSESLint.Scope.Scope,\n name: string,\n dropped: ReadonlySet<TSESTree.Node>,\n): boolean =>\n scope.variables\n .find((variable) => variable.name === name)\n ?.references.some((reference) => !dropped.has(reference.identifier)) ??\n false;\n\n/**\n * A name for the value parameter of a callback that only uses the key. `_value`\n * is what the `Obj` documentation uses; a suffix is added if that name is\n * already referenced inside the callback, so the new parameter shadows nothing.\n */\nconst unusedValueParamName = (scope: TSESLint.Scope.Scope): string => {\n const taken = new Set<string>(\n scope.through\n .map((reference) => reference.identifier.name)\n .concat(scope.variables.map((variable) => variable.name)),\n );\n\n const firstFree = (candidate: string, suffix: number): string =>\n taken.has(candidate)\n ? firstFree(`_value${suffix + 1}`, suffix + 1)\n : candidate;\n\n return firstFree('_value', 1);\n};\n\n/** Expressions that bind looser than a conditional's test position. */\nconst NEEDS_PARENS_AS_TEST: ReadonlySet<string> = new Set<string>([\n AST_NODE_TYPES.ArrowFunctionExpression,\n AST_NODE_TYPES.AssignmentExpression,\n AST_NODE_TYPES.ConditionalExpression,\n AST_NODE_TYPES.SequenceExpression,\n AST_NODE_TYPES.YieldExpression,\n]);\n\nconst conditionalTestText = (\n text: string,\n node: TSESTree.Expression,\n): string =>\n NEEDS_PARENS_AS_TEST.has(node.type) ? (`(${text})` as const) : text;\n\n/** A comma expression would read as another argument without parentheses. */\nconst argumentText = (text: string, node: TSESTree.Expression): string =>\n node.type === AST_NODE_TYPES.SequenceExpression\n ? (`(${text})` as const)\n : text;\n\n/**\n * A concise arrow body cannot start with `{`, nor hold a bare comma. The text\n * is what decides the first case rather than the node type, because an object\n * literal keeps its leading brace under an `as const`.\n */\nconst arrowBodyText = (text: string, node: TSESTree.Expression): string =>\n text.startsWith('{') || node.type === AST_NODE_TYPES.SequenceExpression\n ? (`(${text})` as const)\n : text;\n"],"names":[],"mappings":"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"}
|
package/dist/rules/rules.d.mts
CHANGED
|
@@ -14,5 +14,6 @@ export declare const tsDataForgeRules: {
|
|
|
14
14
|
ignore?: readonly string[];
|
|
15
15
|
}> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
16
16
|
readonly 'prefer-comparison-over-nullish-guard': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferComparison", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
17
|
+
readonly 'prefer-obj-over-entries-round-trip': import("@typescript-eslint/utils/ts-eslint").RuleModule<"useObjFilter" | "useObjFilterMap" | "useObjMap", readonly [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
17
18
|
};
|
|
18
19
|
//# sourceMappingURL=rules.d.mts.map
|
|
@@ -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":"AAgBA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;CAea,CAAC"}
|
package/dist/rules/rules.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { preferIsNonNullObject } from './prefer-is-non-null-object.mjs';
|
|
|
10
10
|
import { preferIsRecordAndHasKey } from './prefer-is-record-and-has-key.mjs';
|
|
11
11
|
import { preferNumSafeParseFloat } from './prefer-num-safe-parse-float.mjs';
|
|
12
12
|
import { preferNumSafeParseInt } from './prefer-num-safe-parse-int.mjs';
|
|
13
|
+
import { preferObjOverEntriesRoundTrip } from './prefer-obj-over-entries-round-trip.mjs';
|
|
13
14
|
import { preferRangeForLoop } from './prefer-range-for-loop.mjs';
|
|
14
15
|
|
|
15
16
|
const tsDataForgeRules = {
|
|
@@ -25,7 +26,8 @@ const tsDataForgeRules = {
|
|
|
25
26
|
"prefer-num-safe-parse-int": preferNumSafeParseInt,
|
|
26
27
|
"prefer-num-safe-parse-float": preferNumSafeParseFloat,
|
|
27
28
|
"no-unnecessary-type-guard": noUnnecessaryTypeGuard,
|
|
28
|
-
"prefer-comparison-over-nullish-guard": preferComparisonOverNullishGuard
|
|
29
|
+
"prefer-comparison-over-nullish-guard": preferComparisonOverNullishGuard,
|
|
30
|
+
"prefer-obj-over-entries-round-trip": preferObjOverEntriesRoundTrip
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
export { tsDataForgeRules };
|
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 { preferArrSum } from './prefer-arr-sum.mjs';\nimport { preferAsInt } from './prefer-as-int.mjs';\nimport { preferCanonicalArraySlicing } from './prefer-canonical-array-slicing.mjs';\nimport { preferCanonicalLengthCast } from './prefer-canonical-length-cast.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-canonical-length-cast': preferCanonicalLengthCast,\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":"
|
|
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 { preferCanonicalLengthCast } from './prefer-canonical-length-cast.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 { preferObjOverEntriesRoundTrip } from './prefer-obj-over-entries-round-trip.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-canonical-length-cast': preferCanonicalLengthCast,\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 'prefer-obj-over-entries-round-trip': preferObjOverEntriesRoundTrip,\n} as const satisfies ESLintPlugin['rules'];\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAgBO,MAAM,gBAAA,GAAmB;AAAA,EAC9B,gCAAA,EAAkC,2BAAA;AAAA,EAClC,+BAAA,EAAiC,0BAAA;AAAA,EACjC,8BAAA,EAAgC,yBAAA;AAAA,EAChC,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,gCAAA;AAAA,EACxC,oCAAA,EAAsC;AACxC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-ts-data-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ESLint rules that steer code toward ts-data-forge idioms.",
|
|
6
6
|
"keywords": [
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@typescript-eslint/utils": "^8.67.0",
|
|
38
38
|
"ts-data-forge": "14.4.0",
|
|
39
|
-
"ts-type-forge": "~9.2.
|
|
39
|
+
"ts-type-forge": "~9.2.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@typescript-eslint/parser": "8.67.0",
|
|
43
43
|
"@typescript-eslint/rule-tester": "8.67.0",
|
|
44
44
|
"dedent": "1.7.2",
|
|
45
|
+
"eslint-config-typed": "5.9.5",
|
|
46
|
+
"eslint-plugin-ts-fortress": "1.0.4",
|
|
47
|
+
"eslint-plugin-ts-type-forge": "0.7.0",
|
|
45
48
|
"prettier": "3.9.6",
|
|
46
49
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
47
50
|
"prettier-plugin-packagejson": "3.0.2",
|
|
48
51
|
"rollup": "4.62.4",
|
|
49
|
-
"
|
|
50
|
-
"eslint-plugin-ts-fortress": "1.0.4",
|
|
51
|
-
"eslint-plugin-ts-type-forge": "0.6.6",
|
|
52
|
-
"ts-repo-utils": "10.2.0"
|
|
52
|
+
"ts-repo-utils": "10.3.2"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"eslint": ">=9.0.0",
|
package/src/plugin.mts
CHANGED
|
@@ -22,6 +22,7 @@ const recommendedRules = {
|
|
|
22
22
|
'ts-data-forge/prefer-num-safe-parse-float': 'error',
|
|
23
23
|
'ts-data-forge/no-unnecessary-type-guard': 'error',
|
|
24
24
|
'ts-data-forge/prefer-comparison-over-nullish-guard': 'error',
|
|
25
|
+
'ts-data-forge/prefer-obj-over-entries-round-trip': 'error',
|
|
25
26
|
} as const satisfies ReadonlyRecord<
|
|
26
27
|
`ts-data-forge/${keyof typeof tsDataForgeRules}`,
|
|
27
28
|
'error'
|
package/src/rule-types.mts
CHANGED
|
@@ -250,6 +250,21 @@ namespace PreferComparisonOverNullishGuard {
|
|
|
250
250
|
export type RuleEntry = Linter.StringSeverity;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/**
|
|
254
|
+
* @description Replace an `Object.fromEntries(Object.entries(record).map/filter/flatMap(...))` round trip with `Obj.map` / `Obj.filter` / `Obj.filterMap` from ts-data-forge.
|
|
255
|
+
*
|
|
256
|
+
* ```md
|
|
257
|
+
* | key | value |
|
|
258
|
+
* | :--------- | :--------- |
|
|
259
|
+
* | type | suggestion |
|
|
260
|
+
* | deprecated | false |
|
|
261
|
+
* | fixable | code |
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
namespace PreferObjOverEntriesRoundTrip {
|
|
265
|
+
export type RuleEntry = Linter.StringSeverity;
|
|
266
|
+
}
|
|
267
|
+
|
|
253
268
|
export type EslintTsDataForgeRules = Readonly<{
|
|
254
269
|
'ts-data-forge/prefer-canonical-array-slicing': PreferCanonicalArraySlicing.RuleEntry;
|
|
255
270
|
'ts-data-forge/prefer-canonical-length-guard': PreferCanonicalLengthGuard.RuleEntry;
|
|
@@ -264,6 +279,7 @@ export type EslintTsDataForgeRules = Readonly<{
|
|
|
264
279
|
'ts-data-forge/prefer-num-safe-parse-float': PreferNumSafeParseFloat.RuleEntry;
|
|
265
280
|
'ts-data-forge/no-unnecessary-type-guard': NoUnnecessaryTypeGuard.RuleEntry;
|
|
266
281
|
'ts-data-forge/prefer-comparison-over-nullish-guard': PreferComparisonOverNullishGuard.RuleEntry;
|
|
282
|
+
'ts-data-forge/prefer-obj-over-entries-round-trip': PreferObjOverEntriesRoundTrip.RuleEntry;
|
|
267
283
|
}>;
|
|
268
284
|
|
|
269
285
|
export type EslintTsDataForgeRulesOption = Readonly<{
|