@checkdigit/eslint-plugin 7.4.1 → 7.5.0-PR.75-0252
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 -1
- package/dist-mjs/agent/add-assert-import.mjs +58 -0
- package/dist-mjs/agent/add-base-path-const.mjs +65 -0
- package/dist-mjs/agent/add-base-path-import.mjs +60 -0
- package/dist-mjs/agent/add-url-domain.mjs +61 -0
- package/dist-mjs/agent/agent-test-wiring.mjs +221 -0
- package/dist-mjs/agent/fetch-response-body-json.mjs +146 -0
- package/dist-mjs/agent/fetch-response-header-getter.mjs +117 -0
- package/dist-mjs/agent/fetch-response-status.mjs +66 -0
- package/dist-mjs/agent/fetch-then.mjs +269 -0
- package/dist-mjs/agent/fetch.mjs +38 -0
- package/dist-mjs/agent/file.mjs +43 -0
- package/dist-mjs/agent/fix-function-call-arguments.mjs +153 -0
- package/dist-mjs/agent/no-fixture.mjs +361 -0
- package/dist-mjs/agent/no-mapped-response.mjs +75 -0
- package/dist-mjs/agent/no-service-wrapper.mjs +185 -0
- package/dist-mjs/agent/no-status-code.mjs +59 -0
- package/dist-mjs/agent/no-unused-function-argument.mjs +79 -0
- package/dist-mjs/agent/no-unused-imports.mjs +81 -0
- package/dist-mjs/agent/no-unused-service-variable.mjs +74 -0
- package/dist-mjs/agent/response-reference.mjs +70 -0
- package/dist-mjs/agent/url.mjs +32 -0
- package/dist-mjs/index.mjs +205 -51
- package/dist-mjs/library/tree.mjs +2 -2
- package/dist-mjs/no-legacy-service-typing.mjs +39 -0
- package/dist-mjs/{agent/no-serve-runtime.mjs → no-serve-runtime.mjs} +3 -3
- package/dist-mjs/require-fixed-services-import.mjs +1 -1
- package/dist-mjs/require-resolve-full-response.mjs +30 -15
- package/dist-types/agent/add-assert-import.d.ts +4 -0
- package/dist-types/agent/add-base-path-const.d.ts +4 -0
- package/dist-types/agent/add-base-path-import.d.ts +4 -0
- package/dist-types/agent/add-url-domain.d.ts +4 -0
- package/dist-types/agent/agent-test-wiring.d.ts +4 -0
- package/dist-types/agent/fetch-response-body-json.d.ts +4 -0
- package/dist-types/agent/fetch-response-header-getter.d.ts +4 -0
- package/dist-types/agent/fetch-response-status.d.ts +4 -0
- package/dist-types/agent/fetch-then.d.ts +4 -0
- package/dist-types/agent/fetch.d.ts +5 -0
- package/dist-types/agent/file.d.ts +7 -0
- package/dist-types/agent/fix-function-call-arguments.d.ts +9 -0
- package/dist-types/agent/no-fixture.d.ts +4 -0
- package/dist-types/agent/no-mapped-response.d.ts +4 -0
- package/dist-types/agent/no-service-wrapper.d.ts +4 -0
- package/dist-types/agent/no-status-code.d.ts +4 -0
- package/dist-types/agent/no-unused-function-argument.d.ts +4 -0
- package/dist-types/agent/no-unused-imports.d.ts +4 -0
- package/dist-types/agent/no-unused-service-variable.d.ts +4 -0
- package/dist-types/agent/response-reference.d.ts +16 -0
- package/dist-types/agent/url.d.ts +4 -0
- package/dist-types/index.d.ts +4 -2
- package/dist-types/no-legacy-service-typing.d.ts +5 -0
- package/package.json +1 -96
- package/src/agent/add-assert-import.ts +74 -0
- package/src/agent/add-base-path-const.ts +81 -0
- package/src/agent/add-base-path-import.ts +69 -0
- package/src/agent/add-url-domain.ts +76 -0
- package/src/agent/agent-test-wiring.ts +273 -0
- package/src/agent/fetch-response-body-json.ts +197 -0
- package/src/agent/fetch-response-header-getter.ts +148 -0
- package/src/agent/fetch-response-status.ts +87 -0
- package/src/agent/fetch-then.ts +357 -0
- package/src/agent/fetch.ts +57 -0
- package/src/agent/file.ts +42 -0
- package/src/agent/fix-function-call-arguments.ts +200 -0
- package/src/agent/no-fixture.ts +521 -0
- package/src/agent/no-mapped-response.ts +84 -0
- package/src/agent/no-service-wrapper.ts +241 -0
- package/src/agent/no-status-code.ts +72 -0
- package/src/agent/no-unused-function-argument.ts +98 -0
- package/src/agent/no-unused-imports.ts +103 -0
- package/src/agent/no-unused-service-variable.ts +93 -0
- package/src/agent/response-reference.ts +129 -0
- package/src/agent/url.ts +32 -0
- package/src/index.ts +206 -50
- package/src/library/tree.ts +1 -0
- package/src/no-legacy-service-typing.ts +49 -0
- package/src/{agent/no-serve-runtime.ts → no-serve-runtime.ts} +2 -2
- package/src/require-fixed-services-import.ts +1 -1
- package/src/require-resolve-full-response.ts +38 -21
- package/dist-mjs/agent/no-full-response.mjs +0 -35
- package/dist-types/agent/no-full-response.d.ts +0 -4
- package/src/agent/no-full-response.ts +0 -41
- /package/dist-types/{agent/no-serve-runtime.d.ts → no-serve-runtime.d.ts} +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// agent/fix-function-call-arguments.ts
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2021-2024 Check Digit, LLC
|
|
5
|
+
*
|
|
6
|
+
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { strict as assert } from 'node:assert';
|
|
10
|
+
import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
|
|
11
|
+
import debug from 'debug';
|
|
12
|
+
import getDocumentationUrl from '../get-documentation-url';
|
|
13
|
+
|
|
14
|
+
export const ruleId = 'fix-function-call-arguments';
|
|
15
|
+
|
|
16
|
+
export interface FixFunctionCallArgumentsRuleOptions {
|
|
17
|
+
typesToCheck: string[];
|
|
18
|
+
}
|
|
19
|
+
const DEFAULT_OPTIONS = {
|
|
20
|
+
typesToCheck: [
|
|
21
|
+
'Configuration<ResolvedServices>',
|
|
22
|
+
'Fixture<ResolvedServices>',
|
|
23
|
+
'InboundContext',
|
|
24
|
+
'{ get: () => string; }',
|
|
25
|
+
'Api',
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
30
|
+
const log = debug('eslint-plugin:fix-function-call-arguments');
|
|
31
|
+
|
|
32
|
+
const rule: ESLintUtils.RuleModule<
|
|
33
|
+
'removeIncompatibleFunctionArguments' | 'unknownError',
|
|
34
|
+
[FixFunctionCallArgumentsRuleOptions]
|
|
35
|
+
> = createRule({
|
|
36
|
+
name: ruleId,
|
|
37
|
+
meta: {
|
|
38
|
+
type: 'suggestion',
|
|
39
|
+
docs: {
|
|
40
|
+
description: 'Remove incompatible function arguments.',
|
|
41
|
+
},
|
|
42
|
+
messages: {
|
|
43
|
+
removeIncompatibleFunctionArguments: 'Removing incompatible function arguments.',
|
|
44
|
+
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.',
|
|
45
|
+
},
|
|
46
|
+
fixable: 'code',
|
|
47
|
+
schema: [
|
|
48
|
+
{
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
typesToCheck: {
|
|
52
|
+
description: 'Text representation of the types of which the function call parameters will be examine',
|
|
53
|
+
type: 'array',
|
|
54
|
+
items: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
defaultOptions: [DEFAULT_OPTIONS],
|
|
64
|
+
create(context) {
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
66
|
+
const { typesToCheck } = context.options[0] ?? DEFAULT_OPTIONS;
|
|
67
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
68
|
+
const typeChecker = parserServices.program.getTypeChecker();
|
|
69
|
+
const sourceCode = context.sourceCode;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
CallExpression(callExpression) {
|
|
73
|
+
// ignore calls like `foo.bar()` which are likely to be 3rd party module calls
|
|
74
|
+
// we only focus on calls against local functions or functions imported from the same module
|
|
75
|
+
// if (callExpression.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression) {
|
|
76
|
+
// return;
|
|
77
|
+
// }
|
|
78
|
+
|
|
79
|
+
log('===== file name:', context.filename);
|
|
80
|
+
log('callExpression:', sourceCode.getText(callExpression));
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const actualParameters = callExpression.arguments;
|
|
84
|
+
if (
|
|
85
|
+
!actualParameters.some((actualParameter) => {
|
|
86
|
+
const actualType = typeChecker.getTypeAtLocation(
|
|
87
|
+
parserServices.esTreeNodeToTSNodeMap.get(actualParameter),
|
|
88
|
+
);
|
|
89
|
+
const actualTypeString = typeChecker.typeToString(actualType);
|
|
90
|
+
return typesToCheck.includes(actualTypeString) || actualTypeString.endsWith('RequestType');
|
|
91
|
+
})
|
|
92
|
+
) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const calleeTsNode = parserServices.esTreeNodeToTSNodeMap.get(callExpression.callee);
|
|
97
|
+
const calleeType = typeChecker.getTypeAtLocation(calleeTsNode);
|
|
98
|
+
|
|
99
|
+
const signatures = calleeType.getCallSignatures();
|
|
100
|
+
if (signatures.length > 1) {
|
|
101
|
+
// ignore complex signatures with overloads
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const signature = signatures[0];
|
|
106
|
+
assert(signature);
|
|
107
|
+
// if (
|
|
108
|
+
// signature === undefined ||
|
|
109
|
+
// (signature.typeParameters !== undefined && signature.typeParameters.length > 0)
|
|
110
|
+
// ) {
|
|
111
|
+
// // ignore complex signatures with type parameters
|
|
112
|
+
// return;
|
|
113
|
+
// }
|
|
114
|
+
|
|
115
|
+
log('signature:', signature.getDeclaration().getText());
|
|
116
|
+
const expectedParameters = signature.getParameters();
|
|
117
|
+
log(
|
|
118
|
+
'expected parameters:',
|
|
119
|
+
expectedParameters.map((expectedParameter) =>
|
|
120
|
+
typeChecker.typeToString(typeChecker.getTypeOfSymbol(expectedParameter)),
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
const expectedParametersCount = expectedParameters.length;
|
|
124
|
+
const actualParametersCount = actualParameters.length;
|
|
125
|
+
if (actualParametersCount === 0) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const parametersToKeep: TSESTree.CallExpressionArgument[] = [];
|
|
130
|
+
let expectedParameterIndex = 0;
|
|
131
|
+
for (const [actualParameterIndex, actualParameter] of actualParameters.entries()) {
|
|
132
|
+
if (expectedParameterIndex >= expectedParametersCount) {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const expectedParameter = expectedParameters[expectedParameterIndex];
|
|
137
|
+
assert.ok(expectedParameter, 'Expected parameter not found.');
|
|
138
|
+
|
|
139
|
+
const expectedType = typeChecker.getTypeOfSymbol(expectedParameter);
|
|
140
|
+
const actualType = typeChecker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(actualParameter));
|
|
141
|
+
const actualTypeString = typeChecker.typeToString(actualType);
|
|
142
|
+
log(
|
|
143
|
+
'expected type: #',
|
|
144
|
+
expectedParameterIndex,
|
|
145
|
+
expectedParameter.escapedName,
|
|
146
|
+
typeChecker.typeToString(expectedType),
|
|
147
|
+
);
|
|
148
|
+
log('actual type: #', actualParameterIndex, sourceCode.getText(actualParameter), actualTypeString);
|
|
149
|
+
|
|
150
|
+
if (
|
|
151
|
+
(typesToCheck.includes(actualTypeString) || actualTypeString.endsWith('RequestType')) &&
|
|
152
|
+
!typeChecker.isTypeAssignableTo(actualType, expectedType)
|
|
153
|
+
) {
|
|
154
|
+
log('removing un-matched parameter', sourceCode.getText(actualParameter));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
parametersToKeep.push(actualParameter);
|
|
158
|
+
expectedParameterIndex++;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (parametersToKeep.length === actualParametersCount) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const firstParameter = actualParameters[0];
|
|
166
|
+
const lastParameter = actualParameters.at(-1);
|
|
167
|
+
assert.ok(firstParameter !== undefined && lastParameter !== undefined);
|
|
168
|
+
const tokenAfterParameters = sourceCode.getTokenAfter(lastParameter);
|
|
169
|
+
|
|
170
|
+
context.report({
|
|
171
|
+
node: callExpression,
|
|
172
|
+
messageId: 'removeIncompatibleFunctionArguments',
|
|
173
|
+
fix(fixer) {
|
|
174
|
+
return fixer.replaceTextRange(
|
|
175
|
+
[
|
|
176
|
+
firstParameter.range[0],
|
|
177
|
+
tokenAfterParameters?.value === ',' ? tokenAfterParameters.range[1] : lastParameter.range[1],
|
|
178
|
+
],
|
|
179
|
+
parametersToKeep.map((arg) => sourceCode.getText(arg)).join(', '),
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
// eslint-disable-next-line no-console
|
|
185
|
+
console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
|
|
186
|
+
context.report({
|
|
187
|
+
node: callExpression,
|
|
188
|
+
messageId: 'unknownError',
|
|
189
|
+
data: {
|
|
190
|
+
fileName: context.filename,
|
|
191
|
+
error: error instanceof Error ? error.toString() : JSON.stringify(error),
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
export default rule;
|