@checkdigit/eslint-plugin 7.2.0 → 7.3.0-PR.75-aa6d
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-mjs/agent/add-url-domain.mjs +61 -0
- package/dist-mjs/agent/agent-test-wiring.mjs +170 -0
- package/dist-mjs/agent/fetch-response-body-json.mjs +63 -0
- package/dist-mjs/agent/fetch-response-header-getter.mjs +117 -0
- package/dist-mjs/agent/fetch-then.mjs +267 -0
- package/dist-mjs/agent/fetch.mjs +34 -0
- package/dist-mjs/agent/fix-function-call-arguments.mjs +153 -0
- package/dist-mjs/agent/no-fixture.mjs +326 -0
- package/dist-mjs/agent/no-mapped-response.mjs +75 -0
- package/dist-mjs/agent/no-service-wrapper.mjs +183 -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 +56 -0
- package/dist-mjs/agent/url.mjs +26 -0
- package/dist-mjs/index.mjs +104 -7
- 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-then.d.ts +4 -0
- package/dist-types/agent/fetch.d.ts +4 -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 +5 -0
- package/dist-types/index.d.ts +4 -2
- package/package.json +1 -96
- package/src/agent/add-url-domain.ts +76 -0
- package/src/agent/agent-test-wiring.ts +204 -0
- package/src/agent/fetch-response-body-json.ts +77 -0
- package/src/agent/fetch-response-header-getter.ts +148 -0
- package/src/agent/fetch-then.ts +355 -0
- package/src/agent/fetch.ts +53 -0
- package/src/agent/fix-function-call-arguments.ts +184 -0
- package/src/agent/no-fixture.ts +455 -0
- package/src/agent/no-mapped-response.ts +84 -0
- package/src/agent/no-service-wrapper.ts +239 -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 +109 -0
- package/src/agent/url.ts +25 -0
- package/src/index.ts +105 -6
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// agent/response-reference.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 type { MemberExpression, VariableDeclaration } from 'estree';
|
|
11
|
+
import { type Scope } from 'eslint';
|
|
12
|
+
|
|
13
|
+
import { getParent } from '../library/tree';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* analyze response related variables and their references
|
|
17
|
+
* the implementation is for fixture API, but it can be used for fetch API as well since the tree structure is similar
|
|
18
|
+
* @param variableDeclaration - variable declaration node
|
|
19
|
+
*/
|
|
20
|
+
export function analyzeResponseReferences(
|
|
21
|
+
variableDeclaration: VariableDeclaration | undefined,
|
|
22
|
+
scopeManager: Scope.ScopeManager,
|
|
23
|
+
): {
|
|
24
|
+
variable?: Scope.Variable;
|
|
25
|
+
bodyReferences: MemberExpression[];
|
|
26
|
+
headersReferences: MemberExpression[];
|
|
27
|
+
statusReferences: MemberExpression[];
|
|
28
|
+
destructuringBodyVariable?: Scope.Variable;
|
|
29
|
+
destructuringHeadersVariable?: Scope.Variable;
|
|
30
|
+
destructuringHeadersReferences?: MemberExpression[] | undefined;
|
|
31
|
+
} {
|
|
32
|
+
const results: {
|
|
33
|
+
variable?: Scope.Variable;
|
|
34
|
+
bodyReferences: MemberExpression[];
|
|
35
|
+
headersReferences: MemberExpression[];
|
|
36
|
+
statusReferences: MemberExpression[];
|
|
37
|
+
destructuringBodyVariable?: Scope.Variable;
|
|
38
|
+
destructuringHeadersVariable?: Scope.Variable;
|
|
39
|
+
destructuringHeadersReferences?: MemberExpression[] | undefined;
|
|
40
|
+
} = {
|
|
41
|
+
bodyReferences: [],
|
|
42
|
+
headersReferences: [],
|
|
43
|
+
statusReferences: [],
|
|
44
|
+
};
|
|
45
|
+
if (!variableDeclaration) {
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const responseVariables = scopeManager.getDeclaredVariables(variableDeclaration);
|
|
50
|
+
for (const responseVariable of responseVariables) {
|
|
51
|
+
const identifier = responseVariable.identifiers[0];
|
|
52
|
+
assert.ok(identifier);
|
|
53
|
+
const identifierParent = getParent(identifier);
|
|
54
|
+
assert.ok(identifierParent);
|
|
55
|
+
if (identifierParent.type === 'VariableDeclarator') {
|
|
56
|
+
// e.g. const response = ...
|
|
57
|
+
results.variable = responseVariable;
|
|
58
|
+
const responseReferences = responseVariable.references.map((responseReference) =>
|
|
59
|
+
getParent(responseReference.identifier),
|
|
60
|
+
);
|
|
61
|
+
// e.g. response.body
|
|
62
|
+
results.bodyReferences = responseReferences.filter(
|
|
63
|
+
(node): node is MemberExpression =>
|
|
64
|
+
node?.type === 'MemberExpression' && node.property.type === 'Identifier' && node.property.name === 'body',
|
|
65
|
+
);
|
|
66
|
+
// e.g. response.headers / response.header / response.get()
|
|
67
|
+
results.headersReferences = responseReferences.filter(
|
|
68
|
+
(node): node is MemberExpression =>
|
|
69
|
+
node?.type === 'MemberExpression' &&
|
|
70
|
+
node.property.type === 'Identifier' &&
|
|
71
|
+
(node.property.name === 'header' || node.property.name === 'headers' || node.property.name === 'get'),
|
|
72
|
+
);
|
|
73
|
+
// e.g. response.status / response.statusCode
|
|
74
|
+
results.statusReferences = responseReferences.filter(
|
|
75
|
+
(node): node is MemberExpression =>
|
|
76
|
+
node?.type === 'MemberExpression' &&
|
|
77
|
+
node.property.type === 'Identifier' &&
|
|
78
|
+
(node.property.name === 'status' || node.property.name === 'statusCode'),
|
|
79
|
+
);
|
|
80
|
+
} else if (
|
|
81
|
+
// body reference through destruction/renaming, e.g. "const { body } = ..."
|
|
82
|
+
identifierParent.type === 'Property' &&
|
|
83
|
+
identifierParent.key.type === 'Identifier' &&
|
|
84
|
+
identifierParent.key.name === 'body'
|
|
85
|
+
) {
|
|
86
|
+
results.destructuringBodyVariable = responseVariable;
|
|
87
|
+
} else if (
|
|
88
|
+
// header reference through destruction/renaming, e.g. "const { headers } = ..."
|
|
89
|
+
identifierParent.type === 'Property' &&
|
|
90
|
+
identifierParent.key.type === 'Identifier' &&
|
|
91
|
+
identifierParent.key.name === 'headers'
|
|
92
|
+
) {
|
|
93
|
+
results.destructuringHeadersVariable = responseVariable;
|
|
94
|
+
results.destructuringHeadersReferences = responseVariable.references
|
|
95
|
+
.map((reference) => reference.identifier)
|
|
96
|
+
.map(getParent)
|
|
97
|
+
.filter(
|
|
98
|
+
(parent): parent is MemberExpression =>
|
|
99
|
+
parent?.type === 'MemberExpression' &&
|
|
100
|
+
parent.property.type === 'Identifier' &&
|
|
101
|
+
parent.property.name !== 'get' &&
|
|
102
|
+
getParent(parent)?.type !== 'CallExpression',
|
|
103
|
+
);
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error(`Unknown response variable reference: ${responseVariable.name}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
}
|
package/src/agent/url.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// agent/url.ts
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
|
4
|
+
export const PLAIN_URL_REGEXP: RegExp = /^[`']\/\w+(?<serviceNamePart>-\w+)*\/v\d+\/(?<any>.|\r|\n)+[`']$/u;
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
|
6
|
+
export const TOKENIZED_URL_REGEXP: RegExp = /^`\$\{(?<serviceNamePart>[A-Z]+_)*BASE_PATH\}\/(?<any>.|\r|\n)+`$/u;
|
|
7
|
+
|
|
8
|
+
export function replaceEndpointUrlPrefixWithBasePath(url: string): string {
|
|
9
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
10
|
+
return url.replace(/^`\/\w+(?<parts>-\w+)*\/v\d+\//u, '`${BASE_PATH}/');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function replaceEndpointUrlPrefixWithDomain(url: string): string {
|
|
14
|
+
return url.replace(
|
|
15
|
+
/^(?<quotStart>[`'])\/(?<servicename>\w+(?<parts>-\w+)*)(?<path>\/v\d+\/(?<any>.|\r|\n)+(?<quotEnd>[`'])$)/u,
|
|
16
|
+
'$1https://$2.checkdigit/$2$4',
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function addBasePathUrlDomain(url: string): string {
|
|
21
|
+
return url.replace(
|
|
22
|
+
/^(?<quotStart>[`'])\/(?<servicename>\w+(?<parts>-\w+)*)(?<path>\/v\d+(?<quotEnd>[`'])$)/u,
|
|
23
|
+
'$1https://$2.checkdigit/$2$4',
|
|
24
|
+
);
|
|
25
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,10 +8,29 @@
|
|
|
8
8
|
|
|
9
9
|
import type { TSESLint } from '@typescript-eslint/utils';
|
|
10
10
|
|
|
11
|
+
import addUrlDomain, { ruleId as addUrlDomainRuleId } from './agent/add-url-domain';
|
|
12
|
+
import agentTestWiring, { ruleId as agentTestWiringRuleId } from './agent/agent-test-wiring';
|
|
13
|
+
import fetchResponseBodyJson, { ruleId as fetchResponseBodyJsonRuleId } from './agent/fetch-response-body-json';
|
|
14
|
+
import fetchResponseHeaderGetter, {
|
|
15
|
+
ruleId as fetchResponseHeaderGetterRuleId,
|
|
16
|
+
} from './agent/fetch-response-header-getter';
|
|
17
|
+
import fetchThen, { ruleId as fetchThenRuleId } from './agent/fetch-then';
|
|
18
|
+
import fixFunctionCallArguments, {
|
|
19
|
+
ruleId as fixFunctionCallArgumentsRuleId,
|
|
20
|
+
} from './agent/fix-function-call-arguments';
|
|
11
21
|
import invalidJsonStringify, { ruleId as invalidJsonStringifyRuleId } from './invalid-json-stringify';
|
|
12
22
|
import noDuplicatedImports, { ruleId as noDuplicatedImportsRuleId } from './no-duplicated-imports';
|
|
23
|
+
import noFixture, { ruleId as noFixtureRuleId } from './agent/no-fixture';
|
|
13
24
|
import noFullResponse, { ruleId as noFullResponseRuleId } from './agent/no-full-response';
|
|
25
|
+
import noMappedResponse, { ruleId as noMappedResponseRuleId } from './agent/no-mapped-response';
|
|
14
26
|
import noPromiseInstanceMethod, { ruleId as noPromiseInstanceMethodRuleId } from './no-promise-instance-method';
|
|
27
|
+
import noServiceWrapper, { ruleId as noServiceWrapperRuleId } from './agent/no-service-wrapper';
|
|
28
|
+
import noStatusCode, { ruleId as noStatusCodeRuleId } from './agent/no-status-code';
|
|
29
|
+
import noUnusedFunctionArguments, {
|
|
30
|
+
ruleId as noUnusedFunctionArgumentsRuleId,
|
|
31
|
+
} from './agent/no-unused-function-argument';
|
|
32
|
+
import noUnusedImports, { ruleId as noUnusedImportsRuleId } from './agent/no-unused-imports';
|
|
33
|
+
import noUnusedServiceVariables, { ruleId as noUnusedServiceVariablesRuleId } from './agent/no-unused-service-variable';
|
|
15
34
|
import requireFixedServicesImport, {
|
|
16
35
|
ruleId as requireFixedServicesImportRuleId,
|
|
17
36
|
} from './require-fixed-services-import';
|
|
@@ -44,19 +63,32 @@ const rules: Record<string, TSESLint.LooseRuleDefinition> = {
|
|
|
44
63
|
'object-literal-response': objectLiteralResponse,
|
|
45
64
|
[invalidJsonStringifyRuleId]: invalidJsonStringify,
|
|
46
65
|
[noPromiseInstanceMethodRuleId]: noPromiseInstanceMethod,
|
|
66
|
+
[noFixtureRuleId]: noFixture,
|
|
67
|
+
[fetchThenRuleId]: fetchThen,
|
|
68
|
+
[noServiceWrapperRuleId]: noServiceWrapper,
|
|
69
|
+
[noStatusCodeRuleId]: noStatusCode,
|
|
70
|
+
[fetchResponseBodyJsonRuleId]: fetchResponseBodyJson,
|
|
71
|
+
[fetchResponseHeaderGetterRuleId]: fetchResponseHeaderGetter,
|
|
72
|
+
[addUrlDomainRuleId]: addUrlDomain,
|
|
47
73
|
[noFullResponseRuleId]: noFullResponse,
|
|
74
|
+
[noMappedResponseRuleId]: noMappedResponse,
|
|
48
75
|
[requireResolveFullResponseRuleId]: requireResolveFullResponse,
|
|
49
|
-
[requireTypeOutOfTypeOnlyImportsRuleId]: requireTypeOutOfTypeOnlyImports,
|
|
50
76
|
[noDuplicatedImportsRuleId]: noDuplicatedImports,
|
|
51
|
-
[requireFixedServicesImportRuleId]: requireFixedServicesImport,
|
|
52
77
|
[noServeRuntimeRuleId]: noServeRuntime,
|
|
78
|
+
[requireFixedServicesImportRuleId]: requireFixedServicesImport,
|
|
79
|
+
[requireTypeOutOfTypeOnlyImportsRuleId]: requireTypeOutOfTypeOnlyImports,
|
|
80
|
+
[noUnusedFunctionArgumentsRuleId]: noUnusedFunctionArguments,
|
|
81
|
+
[noUnusedServiceVariablesRuleId]: noUnusedServiceVariables,
|
|
82
|
+
[noUnusedImportsRuleId]: noUnusedImports,
|
|
83
|
+
[fixFunctionCallArgumentsRuleId]: fixFunctionCallArguments,
|
|
84
|
+
[agentTestWiringRuleId]: agentTestWiring,
|
|
53
85
|
};
|
|
54
86
|
|
|
55
87
|
const plugin: TSESLint.FlatConfig.Plugin = {
|
|
56
88
|
rules,
|
|
57
89
|
};
|
|
58
90
|
|
|
59
|
-
const configs: Record<string, TSESLint.FlatConfig.Config> = {
|
|
91
|
+
const configs: Record<string, TSESLint.FlatConfig.Config | TSESLint.FlatConfig.Config[]> = {
|
|
60
92
|
all: {
|
|
61
93
|
plugins: {
|
|
62
94
|
'@checkdigit': plugin,
|
|
@@ -75,10 +107,25 @@ const configs: Record<string, TSESLint.FlatConfig.Config> = {
|
|
|
75
107
|
[`@checkdigit/${noPromiseInstanceMethodRuleId}`]: 'error',
|
|
76
108
|
[`@checkdigit/${noFullResponseRuleId}`]: 'error',
|
|
77
109
|
[`@checkdigit/${requireResolveFullResponseRuleId}`]: 'error',
|
|
78
|
-
[`@checkdigit/${requireTypeOutOfTypeOnlyImportsRuleId}`]: 'error',
|
|
79
110
|
[`@checkdigit/${noDuplicatedImportsRuleId}`]: 'error',
|
|
80
111
|
[`@checkdigit/${requireFixedServicesImportRuleId}`]: 'error',
|
|
112
|
+
[`@checkdigit/${requireTypeOutOfTypeOnlyImportsRuleId}`]: 'error',
|
|
81
113
|
[`@checkdigit/${noServeRuntimeRuleId}`]: 'error',
|
|
114
|
+
// --- agent rules BEGIN ---
|
|
115
|
+
[`@checkdigit/${noMappedResponseRuleId}`]: 'off',
|
|
116
|
+
[`@checkdigit/${addUrlDomainRuleId}`]: 'off',
|
|
117
|
+
[`@checkdigit/${noFixtureRuleId}`]: 'off',
|
|
118
|
+
[`@checkdigit/${noServiceWrapperRuleId}`]: 'off',
|
|
119
|
+
[`@checkdigit/${noStatusCodeRuleId}`]: 'off',
|
|
120
|
+
[`@checkdigit/${fetchResponseBodyJsonRuleId}`]: 'off',
|
|
121
|
+
[`@checkdigit/${fetchResponseHeaderGetterRuleId}`]: 'off',
|
|
122
|
+
[`@checkdigit/${fetchThenRuleId}`]: 'off',
|
|
123
|
+
[`@checkdigit/${noUnusedFunctionArgumentsRuleId}`]: 'off',
|
|
124
|
+
[`@checkdigit/${noUnusedServiceVariablesRuleId}`]: 'off',
|
|
125
|
+
[`@checkdigit/${noUnusedImportsRuleId}`]: 'off',
|
|
126
|
+
[`@checkdigit/${fixFunctionCallArgumentsRuleId}`]: 'off',
|
|
127
|
+
[`@checkdigit/${agentTestWiringRuleId}`]: 'off',
|
|
128
|
+
// --- agent rules END ---
|
|
82
129
|
},
|
|
83
130
|
},
|
|
84
131
|
recommended: {
|
|
@@ -99,10 +146,62 @@ const configs: Record<string, TSESLint.FlatConfig.Config> = {
|
|
|
99
146
|
[`@checkdigit/${noPromiseInstanceMethodRuleId}`]: 'error',
|
|
100
147
|
},
|
|
101
148
|
},
|
|
149
|
+
'agent-phase-1-test': [
|
|
150
|
+
{
|
|
151
|
+
files: ['*.spec.ts', '*.test.ts', 'src/api/v*/index.ts'],
|
|
152
|
+
plugins: {
|
|
153
|
+
'@checkdigit': plugin,
|
|
154
|
+
},
|
|
155
|
+
rules: {
|
|
156
|
+
[`@checkdigit/${noMappedResponseRuleId}`]: 'error',
|
|
157
|
+
[`@checkdigit/${addUrlDomainRuleId}`]: 'error',
|
|
158
|
+
[`@checkdigit/${noFixtureRuleId}`]: 'error',
|
|
159
|
+
[`@checkdigit/${noServiceWrapperRuleId}`]: 'error',
|
|
160
|
+
[`@checkdigit/${noStatusCodeRuleId}`]: 'error',
|
|
161
|
+
[`@checkdigit/${fetchResponseBodyJsonRuleId}`]: 'error',
|
|
162
|
+
[`@checkdigit/${fetchResponseHeaderGetterRuleId}`]: 'error',
|
|
163
|
+
[`@checkdigit/${fetchThenRuleId}`]: 'error',
|
|
164
|
+
[`@checkdigit/${noUnusedFunctionArgumentsRuleId}`]: 'error',
|
|
165
|
+
[`@checkdigit/${noUnusedServiceVariablesRuleId}`]: 'error',
|
|
166
|
+
[`@checkdigit/${noUnusedImportsRuleId}`]: 'error',
|
|
167
|
+
[`@checkdigit/${fixFunctionCallArgumentsRuleId}`]: 'error',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
files: ['*.spec.ts'],
|
|
172
|
+
plugins: {
|
|
173
|
+
'@checkdigit': plugin,
|
|
174
|
+
},
|
|
175
|
+
rules: {
|
|
176
|
+
[`@checkdigit/${agentTestWiringRuleId}`]: 'error',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
'agent-phase-2-production': {
|
|
181
|
+
plugins: {
|
|
182
|
+
'@checkdigit': plugin,
|
|
183
|
+
},
|
|
184
|
+
rules: {
|
|
185
|
+
[`@checkdigit/${noMappedResponseRuleId}`]: 'error',
|
|
186
|
+
[`@checkdigit/${addUrlDomainRuleId}`]: 'error',
|
|
187
|
+
[`@checkdigit/${noFixtureRuleId}`]: 'off',
|
|
188
|
+
[`@checkdigit/${noServiceWrapperRuleId}`]: 'error',
|
|
189
|
+
[`@checkdigit/${noStatusCodeRuleId}`]: 'error',
|
|
190
|
+
[`@checkdigit/${fetchResponseBodyJsonRuleId}`]: 'error',
|
|
191
|
+
[`@checkdigit/${fetchResponseHeaderGetterRuleId}`]: 'error',
|
|
192
|
+
[`@checkdigit/${fetchThenRuleId}`]: 'error',
|
|
193
|
+
[`@checkdigit/${noUnusedFunctionArgumentsRuleId}`]: 'error',
|
|
194
|
+
[`@checkdigit/${noUnusedServiceVariablesRuleId}`]: 'error',
|
|
195
|
+
[`@checkdigit/${noUnusedImportsRuleId}`]: 'error',
|
|
196
|
+
[`@checkdigit/${fixFunctionCallArgumentsRuleId}`]: 'error',
|
|
197
|
+
},
|
|
198
|
+
},
|
|
102
199
|
};
|
|
103
200
|
|
|
104
|
-
const
|
|
201
|
+
const defaultToExport: Exclude<TSESLint.FlatConfig.Plugin, 'config'> & {
|
|
202
|
+
configs: Record<string, TSESLint.FlatConfig.Config | TSESLint.FlatConfig.Config[]>;
|
|
203
|
+
} = {
|
|
105
204
|
...plugin,
|
|
106
205
|
configs,
|
|
107
206
|
};
|
|
108
|
-
export default
|
|
207
|
+
export default defaultToExport;
|