eslint-plugin-ts-std-forge 0.1.7 → 0.1.9
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/dist/rule-types.d.mts +2 -1
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rule-types.mjs.map +1 -1
- package/dist/rules/ast-utils.d.mts +8 -31
- package/dist/rules/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +13 -75
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
- package/dist/rules/prefer-is-non-null-object.mjs +7 -5
- package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.mjs +9 -13
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-safe-array-is-array.d.mts.map +1 -1
- package/dist/rules/prefer-safe-array-is-array.mjs +8 -5
- package/dist/rules/prefer-safe-array-is-array.mjs.map +1 -1
- package/dist/rules/prefer-safe-array-length-guard.d.mts.map +1 -1
- package/dist/rules/prefer-safe-array-length-guard.mjs +11 -14
- package/dist/rules/prefer-safe-array-length-guard.mjs.map +1 -1
- package/dist/rules/prefer-safe-number-parse-integer.d.mts.map +1 -1
- package/dist/rules/prefer-safe-number-parse-integer.mjs +9 -7
- package/dist/rules/prefer-safe-number-parse-integer.mjs.map +1 -1
- package/dist/rules/prefer-safe-number-parse.d.mts.map +1 -1
- package/dist/rules/prefer-safe-number-parse.mjs +9 -7
- package/dist/rules/prefer-safe-number-parse.mjs.map +1 -1
- package/package.json +5 -4
- package/src/rule-types.mts +2 -1
- package/src/rules/ast-utils.mts +20 -110
- package/src/rules/prefer-is-non-null-object.mts +13 -7
- package/src/rules/prefer-is-record-and-has-key.mts +28 -27
- package/src/rules/prefer-is-record-and-has-key.test.mts +30 -3
- package/src/rules/prefer-safe-array-is-array.mts +14 -10
- package/src/rules/prefer-safe-array-is-array.test.mts +14 -0
- package/src/rules/prefer-safe-array-length-guard.mts +25 -26
- package/src/rules/prefer-safe-array-length-guard.test.mts +19 -5
- package/src/rules/prefer-safe-number-parse-integer.mts +25 -19
- package/src/rules/prefer-safe-number-parse-integer.test.mts +19 -5
- package/src/rules/prefer-safe-number-parse.mts +25 -19
- package/src/rules/prefer-safe-number-parse.test.mts +19 -5
- package/dist/rules/import-utils.d.mts +0 -6
- package/dist/rules/import-utils.d.mts.map +0 -1
- package/dist/rules/import-utils.mjs +0 -28
- package/dist/rules/import-utils.mjs.map +0 -1
- package/src/rules/import-utils.mts +0 -58
|
@@ -168,6 +168,20 @@ describe('prefer-safe-array-length-guard through type wrappers', () => {
|
|
|
168
168
|
const ok = xs.length > (2 as const);
|
|
169
169
|
`,
|
|
170
170
|
},
|
|
171
|
+
{
|
|
172
|
+
name: 'an array widened by `as` to one that may be a string',
|
|
173
|
+
code: dedent`
|
|
174
|
+
declare const xs: readonly string[];
|
|
175
|
+
const ok = (xs as readonly string[] | string).length > 0;
|
|
176
|
+
`,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: '`!` on an optional array, which leaves undefined in place',
|
|
180
|
+
code: dedent`
|
|
181
|
+
declare const xs: readonly number[] | undefined;
|
|
182
|
+
const ok = xs!.length !== 0;
|
|
183
|
+
`,
|
|
184
|
+
},
|
|
171
185
|
],
|
|
172
186
|
invalid: [
|
|
173
187
|
{
|
|
@@ -210,15 +224,15 @@ describe('prefer-safe-array-length-guard through type wrappers', () => {
|
|
|
210
224
|
errors: [{ messageId: 'useIsEmpty' }],
|
|
211
225
|
},
|
|
212
226
|
{
|
|
213
|
-
name: 'keeps a wrapper on
|
|
227
|
+
name: 'keeps a wrapper on an array that is an array at every layer',
|
|
214
228
|
code: dedent`
|
|
215
|
-
declare const xs: readonly number[]
|
|
216
|
-
const ok = xs
|
|
229
|
+
declare const xs: readonly number[];
|
|
230
|
+
const ok = (xs! as readonly number[]).length !== 0;
|
|
217
231
|
`,
|
|
218
232
|
output: dedent`
|
|
219
233
|
import { SafeArray } from 'ts-std-forge';
|
|
220
|
-
declare const xs: readonly number[]
|
|
221
|
-
const ok = SafeArray.isNonEmpty(xs!);
|
|
234
|
+
declare const xs: readonly number[];
|
|
235
|
+
const ok = SafeArray.isNonEmpty(xs! as readonly number[]);
|
|
222
236
|
`,
|
|
223
237
|
errors: [{ messageId: 'useIsNonEmpty' }],
|
|
224
238
|
},
|
|
@@ -3,18 +3,16 @@ import {
|
|
|
3
3
|
type TSESLint,
|
|
4
4
|
type TSESTree,
|
|
5
5
|
} from '@typescript-eslint/utils';
|
|
6
|
-
import * as ts from 'typescript';
|
|
7
6
|
import {
|
|
7
|
+
buildNamedImportFix,
|
|
8
8
|
getArgumentText,
|
|
9
|
-
|
|
10
|
-
isIdentifierNamed,
|
|
11
|
-
skipTypeWrappers,
|
|
12
|
-
} from './ast-utils.mjs';
|
|
13
|
-
import {
|
|
14
|
-
buildImportFixes,
|
|
9
|
+
getImportDeclarations,
|
|
15
10
|
getNamedImports,
|
|
16
|
-
|
|
17
|
-
} from '
|
|
11
|
+
skipTypeWrappers,
|
|
12
|
+
} from 'eslint-rule-forge';
|
|
13
|
+
import * as ts from 'typescript';
|
|
14
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
15
|
+
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
18
16
|
|
|
19
17
|
type Options = readonly [];
|
|
20
18
|
|
|
@@ -66,7 +64,10 @@ export const preferSafeNumberParseInteger: TSESLint.RuleModule<
|
|
|
66
64
|
|
|
67
65
|
const program = sourceCode.ast;
|
|
68
66
|
|
|
69
|
-
const
|
|
67
|
+
const tsStdForgeImports = getImportDeclarations(
|
|
68
|
+
program,
|
|
69
|
+
TS_STD_FORGE_MODULE,
|
|
70
|
+
);
|
|
70
71
|
|
|
71
72
|
const mut_nodesToFix: {
|
|
72
73
|
node: TSESTree.CallExpression;
|
|
@@ -117,20 +118,20 @@ export const preferSafeNumberParseInteger: TSESLint.RuleModule<
|
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
// The argument must be a `string`
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
121
|
+
// The argument must be a `string` at every layer of type wrappers:
|
|
122
|
+
// the value's own type is what `parseInt` sees at run time
|
|
123
|
+
// (`parseInt(1e21 as unknown as string)` is 1, the rewrite 1e21), and
|
|
124
|
+
// the type a wrapper states is what the fix, which keeps the wrapper,
|
|
125
|
+
// hands `SafeNumber.parseInteger(value: string)`. Without type
|
|
126
|
+
// information, skip.
|
|
127
|
+
if (!typeHoldsAtEveryLayer(sourceCode, firstArg, isStringType)) {
|
|
127
128
|
return;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
mut_nodesToFix.push({ node, argExpression: firstArg });
|
|
131
132
|
},
|
|
132
133
|
'Program:exit': () => {
|
|
133
|
-
const namedImports = getNamedImports(
|
|
134
|
+
const namedImports = getNamedImports(tsStdForgeImports);
|
|
134
135
|
|
|
135
136
|
const missingImports = (['Result', 'SafeNumber'] as const).filter(
|
|
136
137
|
(name) => !namedImports.includes(name),
|
|
@@ -154,7 +155,12 @@ export const preferSafeNumberParseInteger: TSESLint.RuleModule<
|
|
|
154
155
|
|
|
155
156
|
const importFixes =
|
|
156
157
|
index === 0 && missingImports.length > 0
|
|
157
|
-
?
|
|
158
|
+
? buildNamedImportFix(
|
|
159
|
+
fixer,
|
|
160
|
+
program,
|
|
161
|
+
TS_STD_FORGE_MODULE,
|
|
162
|
+
missingImports,
|
|
163
|
+
)
|
|
158
164
|
: [];
|
|
159
165
|
|
|
160
166
|
return [...importFixes, fixer.replaceText(node, core)];
|
|
@@ -84,6 +84,20 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
|
|
|
84
84
|
const n = parseInt(s, 16 as const);
|
|
85
85
|
`,
|
|
86
86
|
},
|
|
87
|
+
{
|
|
88
|
+
name: 'ignores a string widened by `as` to one that may be a number',
|
|
89
|
+
code: dedent`
|
|
90
|
+
declare const s: string;
|
|
91
|
+
const n = parseInt(s as string | number as string);
|
|
92
|
+
`,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'ignores `!` on an optional string, which leaves undefined in place',
|
|
96
|
+
code: dedent`
|
|
97
|
+
declare const s: string | undefined;
|
|
98
|
+
const n = parseInt(s!);
|
|
99
|
+
`,
|
|
100
|
+
},
|
|
87
101
|
],
|
|
88
102
|
invalid: [
|
|
89
103
|
{
|
|
@@ -131,15 +145,15 @@ describe('prefer-safe-number-parse-integer through type wrappers', () => {
|
|
|
131
145
|
],
|
|
132
146
|
},
|
|
133
147
|
{
|
|
134
|
-
name: '
|
|
148
|
+
name: 'rewrites a string that is a string at every layer',
|
|
135
149
|
code: dedent`
|
|
136
|
-
declare const s: string
|
|
137
|
-
const n = parseInt(s!);
|
|
150
|
+
declare const s: string;
|
|
151
|
+
const n = parseInt(s! as string);
|
|
138
152
|
`,
|
|
139
153
|
output: dedent`
|
|
140
154
|
import { Result, SafeNumber } from 'ts-std-forge';
|
|
141
|
-
declare const s: string
|
|
142
|
-
const n = Result.unwrapOkOr(SafeNumber.parseInteger(s!), Number.NaN);
|
|
155
|
+
declare const s: string;
|
|
156
|
+
const n = Result.unwrapOkOr(SafeNumber.parseInteger(s! as string), Number.NaN);
|
|
143
157
|
`,
|
|
144
158
|
errors: [{ messageId: 'useSafeNumberParseInteger' }],
|
|
145
159
|
},
|
|
@@ -3,18 +3,16 @@ import {
|
|
|
3
3
|
type TSESLint,
|
|
4
4
|
type TSESTree,
|
|
5
5
|
} from '@typescript-eslint/utils';
|
|
6
|
-
import * as ts from 'typescript';
|
|
7
6
|
import {
|
|
7
|
+
buildNamedImportFix,
|
|
8
8
|
getArgumentText,
|
|
9
|
-
|
|
10
|
-
isIdentifierNamed,
|
|
11
|
-
skipTypeWrappers,
|
|
12
|
-
} from './ast-utils.mjs';
|
|
13
|
-
import {
|
|
14
|
-
buildImportFixes,
|
|
9
|
+
getImportDeclarations,
|
|
15
10
|
getNamedImports,
|
|
16
|
-
|
|
17
|
-
} from '
|
|
11
|
+
skipTypeWrappers,
|
|
12
|
+
} from 'eslint-rule-forge';
|
|
13
|
+
import * as ts from 'typescript';
|
|
14
|
+
import { isIdentifierNamed, typeHoldsAtEveryLayer } from './ast-utils.mjs';
|
|
15
|
+
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
18
16
|
|
|
19
17
|
type Options = readonly [];
|
|
20
18
|
|
|
@@ -69,7 +67,10 @@ export const preferSafeNumberParse: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
69
67
|
|
|
70
68
|
const program = sourceCode.ast;
|
|
71
69
|
|
|
72
|
-
const
|
|
70
|
+
const tsStdForgeImports = getImportDeclarations(
|
|
71
|
+
program,
|
|
72
|
+
TS_STD_FORGE_MODULE,
|
|
73
|
+
);
|
|
73
74
|
|
|
74
75
|
const mut_nodesToFix: {
|
|
75
76
|
node: TSESTree.CallExpression;
|
|
@@ -114,20 +115,20 @@ export const preferSafeNumberParse: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
114
115
|
return;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
// The argument must be purely `string`
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (
|
|
118
|
+
// The argument must be purely `string` at every layer of type
|
|
119
|
+
// wrappers: the value's own type is what `Number` sees at run time
|
|
120
|
+
// (`Number(true as unknown as string)` is 1, the rewrite NaN), and the
|
|
121
|
+
// type a wrapper states is what the fix, which keeps the wrapper,
|
|
122
|
+
// hands `SafeNumber.parse(value: string)`. Without type information,
|
|
123
|
+
// skip.
|
|
124
|
+
if (!typeHoldsAtEveryLayer(sourceCode, firstArg, isStringType)) {
|
|
124
125
|
return;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
mut_nodesToFix.push({ node, argExpression: firstArg });
|
|
128
129
|
},
|
|
129
130
|
'Program:exit': () => {
|
|
130
|
-
const namedImports = getNamedImports(
|
|
131
|
+
const namedImports = getNamedImports(tsStdForgeImports);
|
|
131
132
|
|
|
132
133
|
const missingImports = (['Result', 'SafeNumber'] as const).filter(
|
|
133
134
|
(name) => !namedImports.includes(name),
|
|
@@ -151,7 +152,12 @@ export const preferSafeNumberParse: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
151
152
|
|
|
152
153
|
const importFixes =
|
|
153
154
|
index === 0 && missingImports.length > 0
|
|
154
|
-
?
|
|
155
|
+
? buildNamedImportFix(
|
|
156
|
+
fixer,
|
|
157
|
+
program,
|
|
158
|
+
TS_STD_FORGE_MODULE,
|
|
159
|
+
missingImports,
|
|
160
|
+
)
|
|
155
161
|
: [];
|
|
156
162
|
|
|
157
163
|
return [...importFixes, fixer.replaceText(node, core)];
|
|
@@ -132,6 +132,20 @@ describe('prefer-safe-number-parse through type wrappers', () => {
|
|
|
132
132
|
const n = parseFloat(x! as string);
|
|
133
133
|
`,
|
|
134
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: 'ignores a string widened by `as` to a type Number() reads otherwise',
|
|
137
|
+
code: dedent`
|
|
138
|
+
declare const s: string;
|
|
139
|
+
const n = Number(s as string | boolean);
|
|
140
|
+
`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'ignores `!` on an optional string, which leaves undefined in place',
|
|
144
|
+
code: dedent`
|
|
145
|
+
declare const s: string | undefined;
|
|
146
|
+
const n = parseFloat(s!);
|
|
147
|
+
`,
|
|
148
|
+
},
|
|
135
149
|
],
|
|
136
150
|
invalid: [
|
|
137
151
|
{
|
|
@@ -166,15 +180,15 @@ describe('prefer-safe-number-parse through type wrappers', () => {
|
|
|
166
180
|
],
|
|
167
181
|
},
|
|
168
182
|
{
|
|
169
|
-
name: '
|
|
183
|
+
name: 'rewrites a string that is a string at every layer',
|
|
170
184
|
code: dedent`
|
|
171
|
-
declare const s: string
|
|
172
|
-
const n = parseFloat(s!);
|
|
185
|
+
declare const s: string;
|
|
186
|
+
const n = parseFloat(s! as string);
|
|
173
187
|
`,
|
|
174
188
|
output: dedent`
|
|
175
189
|
import { Result, SafeNumber } from 'ts-std-forge';
|
|
176
|
-
declare const s: string
|
|
177
|
-
const n = Result.unwrapOkOr(SafeNumber.parse(s!), Number.NaN);
|
|
190
|
+
declare const s: string;
|
|
191
|
+
const n = Result.unwrapOkOr(SafeNumber.parse(s! as string), Number.NaN);
|
|
178
192
|
`,
|
|
179
193
|
errors: [{ messageId: 'useSafeNumberParse' }],
|
|
180
194
|
},
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';
|
|
2
|
-
import type { DeepReadonly } from 'ts-type-forge';
|
|
3
|
-
export declare const getTsStdForgeImport: (program: TSESTree.Program) => TSESTree.ImportDeclaration | undefined;
|
|
4
|
-
export declare const getNamedImports: (node: DeepReadonly<TSESTree.ImportDeclaration> | undefined) => readonly string[];
|
|
5
|
-
export declare const buildImportFixes: (fixer: TSESLint.RuleFixer, program: TSESTree.Program, requiredNames: readonly string[]) => readonly TSESLint.RuleFix[];
|
|
6
|
-
//# sourceMappingURL=import-utils.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"import-utils.d.mts","sourceRoot":"","sources":["../../src/rules/import-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAKlD,eAAO,MAAM,mBAAmB,YACrB,QAAQ,CAAC,OAAO,KACxB,QAAQ,CAAC,iBAAiB,GAAG,SAK7B,CAAC;AAEJ,eAAO,MAAM,eAAe,SACpB,YAAY,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GAAG,SAAS,KACzD,SAAS,MAAM,EAiBjB,CAAC;AAEF,eAAO,MAAM,gBAAgB,UACpB,QAAQ,CAAC,SAAS,WAChB,QAAQ,CAAC,OAAO,iBACV,SAAS,MAAM,EAAE,KAC/B,SAAS,QAAQ,CAAC,OAAO,EAa3B,CAAC"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
|
-
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
3
|
-
|
|
4
|
-
export const getTsStdForgeImport = (program) => program.body.find((node) => node.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
5
|
-
node.source.value === TS_STD_FORGE_MODULE);
|
|
6
|
-
export const getNamedImports = (node) => {
|
|
7
|
-
if (node === undefined) {
|
|
8
|
-
return [];
|
|
9
|
-
}
|
|
10
|
-
return node.specifiers.flatMap((specifier) => specifier.type === AST_NODE_TYPES.ImportSpecifier
|
|
11
|
-
? (() => {
|
|
12
|
-
const importedName = specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
13
|
-
? specifier.imported.name
|
|
14
|
-
: specifier.imported.value;
|
|
15
|
-
return typeof importedName === 'string' ? [importedName] : [];
|
|
16
|
-
})()
|
|
17
|
-
: []);
|
|
18
|
-
};
|
|
19
|
-
export const buildImportFixes = (fixer, program, requiredNames) => {
|
|
20
|
-
const specifierText = requiredNames.join(', ');
|
|
21
|
-
const importStatement = `import { ${specifierText} } from '${TS_STD_FORGE_MODULE}';`;
|
|
22
|
-
const newLine = '\n';
|
|
23
|
-
const insertionText = `${importStatement}${newLine}`;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return [fixer.insertTextBefore(program, insertionText)];
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=import-utils.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"import-utils.mjs","sourceRoot":"","sources":["../../src/rules/import-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,uEAAuE;AAEvE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAyB,EACe,EAAE,CAC1C,OAAO,CAAC,IAAI,CAAC,IAAI,CACf,CAAC,IAAI,EAAsC,EAAE,CAC3C,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,iBAAiB;IAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,mBAAmB,CAC5C,CAAC;AAEJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAA0D,EACvC,EAAE;IACrB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAC3C,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe;QAC/C,CAAC,CAAC,CAAC,GAAG,EAAE;YACJ,MAAM,YAAY,GAChB,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;gBACnD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;gBACzB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;YAE/B,OAAO,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,CAAC,CAAC,EAAE;QACN,CAAC,CAAC,EAAE,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAAyB,EACzB,OAAyB,EACzB,aAAgC,EACH,EAAE;IAC/B,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/C,MAAM,eAAe,GACnB,YAAY,aAAa,YAAY,mBAAmB,IAAa,CAAC;IAExE,MAAM,OAAO,GAAG,IAAI,CAAC;IAErB,MAAM,aAAa,GAAG,GAAG,eAAe,GAAG,OAAO,EAAW,CAAC;IAE9D,6CAA6C;IAC7C,uDAAuD;IACvD,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;AAC1D,CAAC,CAAC"}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AST_NODE_TYPES,
|
|
3
|
-
type TSESLint,
|
|
4
|
-
type TSESTree,
|
|
5
|
-
} from '@typescript-eslint/utils';
|
|
6
|
-
import type { DeepReadonly } from 'ts-type-forge';
|
|
7
|
-
import { TS_STD_FORGE_MODULE } from './constants.mjs';
|
|
8
|
-
|
|
9
|
-
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
|
|
10
|
-
|
|
11
|
-
export const getTsStdForgeImport = (
|
|
12
|
-
program: TSESTree.Program,
|
|
13
|
-
): TSESTree.ImportDeclaration | undefined =>
|
|
14
|
-
program.body.find(
|
|
15
|
-
(node): node is TSESTree.ImportDeclaration =>
|
|
16
|
-
node.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
17
|
-
node.source.value === TS_STD_FORGE_MODULE,
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
export const getNamedImports = (
|
|
21
|
-
node: DeepReadonly<TSESTree.ImportDeclaration> | undefined,
|
|
22
|
-
): readonly string[] => {
|
|
23
|
-
if (node === undefined) {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return node.specifiers.flatMap((specifier) =>
|
|
28
|
-
specifier.type === AST_NODE_TYPES.ImportSpecifier
|
|
29
|
-
? (() => {
|
|
30
|
-
const importedName =
|
|
31
|
-
specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
32
|
-
? specifier.imported.name
|
|
33
|
-
: specifier.imported.value;
|
|
34
|
-
|
|
35
|
-
return typeof importedName === 'string' ? [importedName] : [];
|
|
36
|
-
})()
|
|
37
|
-
: [],
|
|
38
|
-
);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const buildImportFixes = (
|
|
42
|
-
fixer: TSESLint.RuleFixer,
|
|
43
|
-
program: TSESTree.Program,
|
|
44
|
-
requiredNames: readonly string[],
|
|
45
|
-
): readonly TSESLint.RuleFix[] => {
|
|
46
|
-
const specifierText = requiredNames.join(', ');
|
|
47
|
-
|
|
48
|
-
const importStatement =
|
|
49
|
-
`import { ${specifierText} } from '${TS_STD_FORGE_MODULE}';` as const;
|
|
50
|
-
|
|
51
|
-
const newLine = '\n';
|
|
52
|
-
|
|
53
|
-
const insertionText = `${importStatement}${newLine}` as const;
|
|
54
|
-
|
|
55
|
-
// Always insert at the beginning of the file
|
|
56
|
-
// (organize-imports will handle merging/deduplication)
|
|
57
|
-
return [fixer.insertTextBefore(program, insertionText)];
|
|
58
|
-
};
|