@checkdigit/eslint-plugin 7.6.0-PR.75-7ee9 → 7.6.0-PR.75-a611
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/fetch-response-body-json.mjs +1 -1
- package/dist-mjs/agent/fetch-then.mjs +4 -72
- package/dist-mjs/agent/fetch.mjs +2 -2
- package/dist-mjs/agent/no-expect-assertion.mjs +394 -0
- package/dist-mjs/agent/no-fixture.mjs +34 -280
- package/dist-mjs/agent/response-reference.mjs +3 -6
- package/dist-mjs/index.mjs +15 -17
- package/dist-types/agent/fetch-then.d.ts +1 -1
- package/dist-types/agent/{no-supertest.d.ts → no-expect-assertion.d.ts} +1 -1
- package/dist-types/agent/response-reference.d.ts +0 -1
- package/package.json +1 -1
- package/src/agent/fetch-response-body-json.ts +1 -1
- package/src/agent/fetch-then.ts +87 -88
- package/src/agent/fetch.ts +4 -2
- package/src/agent/no-expect-assertion.ts +570 -0
- package/src/agent/no-fixture.ts +46 -424
- package/src/agent/response-reference.ts +11 -11
- package/src/index.ts +16 -16
- package/dist-mjs/agent/no-supertest.mjs +0 -346
- package/src/agent/no-supertest.ts +0 -517
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@checkdigit/eslint-plugin","version":"7.6.0-PR.75-
|
|
1
|
+
{"name":"@checkdigit/eslint-plugin","version":"7.6.0-PR.75-a611","description":"Check Digit eslint plugins","keywords":["eslint","eslintplugin"],"homepage":"https://github.com/checkdigit/eslint-plugin#readme","bugs":{"url":"https://github.com/checkdigit/eslint-plugin/issues"},"repository":{"type":"git","url":"https://github.com/checkdigit/eslint-plugin"},"license":"MIT","author":"Check Digit, LLC","sideEffects":false,"type":"module","exports":{".":{"types":"./dist-types/index.d.ts","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"files":["src","dist-types","dist-mjs","!src/**/test/**","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/test/**","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-mjs/**/test/**","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"scripts":{"build:dist-mjs":"rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs","build:dist-types":"rimraf dist-types && npx builder --type=types --outDir=dist-types","ci:compile":"tsc --noEmit","ci:coverage":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=true","ci:lint":"npm run lint","ci:style":"npm run prettier","ci:test":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","lint":"eslint --max-warnings 0 .","lint:fix":"eslint --max-warnings 0 --fix .","prepare":"","prepublishOnly":"npm run build:dist-types && npm run build:dist-mjs","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style"},"prettier":"@checkdigit/prettier-config","jest":{"preset":"@checkdigit/jest-config"},"dependencies":{"@typescript-eslint/type-utils":"^8.15.0","@typescript-eslint/utils":"^8.15.0","debug":"^4.3.7","ts-api-utils":"^1.4.0"},"devDependencies":{"@checkdigit/jest-config":"^6.0.2","@checkdigit/prettier-config":"^5.5.1","@checkdigit/typescript-config":"^8.0.0","@eslint/js":"^9.15.0","@types/debug":"^4.1.12","@types/eslint":"^9.6.1","@types/eslint-config-prettier":"^6.11.3","@typescript-eslint/parser":"^8.15.0","@typescript-eslint/rule-tester":"^8.15.0","eslint":"^9.15.0","eslint-config-prettier":"^9.1.0","eslint-import-resolver-typescript":"^3.6.3","eslint-plugin-eslint-plugin":"^6.3.2","eslint-plugin-import":"^2.31.0","eslint-plugin-no-only-tests":"^3.3.0","eslint-plugin-no-secrets":"^1.1.2","eslint-plugin-node":"^11.1.0","eslint-plugin-sonarjs":"1.0.4","http-status-codes":"^2.3.0","rimraf":"^6.0.1","typescript-eslint":"^8.15.0"},"peerDependencies":{"eslint":">=9 <10"},"engines":{"node":">=20.17"}}
|
|
@@ -38,7 +38,7 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'replaceBodyWithJson' | 'ref
|
|
|
38
38
|
},
|
|
39
39
|
messages: {
|
|
40
40
|
refactorNeeded:
|
|
41
|
-
'Please extract the fetch call and check its
|
|
41
|
+
'Please extract the fetch call and check its response status code before accessing its response body.',
|
|
42
42
|
replaceBodyWithJson: 'Replace "response.body" with "await response.json()".',
|
|
43
43
|
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.',
|
|
44
44
|
},
|
package/src/agent/fetch-then.ts
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
import { strict as assert } from 'node:assert';
|
|
10
10
|
|
|
11
|
-
import { ScopeManager, Variable } from '@typescript-eslint/scope-manager';
|
|
11
|
+
// import { ScopeManager, Variable } from '@typescript-eslint/scope-manager';
|
|
12
12
|
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
|
|
13
13
|
import type { SourceCode } from '@typescript-eslint/utils/ts-eslint';
|
|
14
14
|
|
|
15
|
-
import { getEnclosingFunction,
|
|
15
|
+
import { getEnclosingFunction, getParent, isUsedInArrayOrAsArgument } from '../library/ts-tree';
|
|
16
16
|
import getDocumentationUrl from '../get-documentation-url';
|
|
17
17
|
import { getIndentation } from '../library/format';
|
|
18
18
|
import { isValidPropertyName } from '../library/variable';
|
|
19
|
-
import { hasAssertions
|
|
19
|
+
import { hasAssertions } from './fetch';
|
|
20
20
|
import { replaceEndpointUrlPrefixWithBasePath } from './url';
|
|
21
21
|
|
|
22
22
|
export const ruleId = 'fetch-then';
|
|
@@ -143,52 +143,52 @@ function createResponseAssertions(
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
function getResponseHeadersAccesses(responseVariables: Variable[], scopeManager: ScopeManager, sourceCode: SourceCode) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
// function getResponseHeadersAccesses(responseVariables: Variable[], scopeManager: ScopeManager, sourceCode: SourceCode) {
|
|
147
|
+
// const responseHeadersAccesses: TSESTree.MemberExpression[] = [];
|
|
148
|
+
// for (const responseVariable of responseVariables) {
|
|
149
|
+
// for (const responseReference of responseVariable.references) {
|
|
150
|
+
// const responseAccess = getParent(responseReference.identifier);
|
|
151
|
+
// if (!responseAccess || responseAccess.type !== AST_NODE_TYPES.MemberExpression) {
|
|
152
|
+
// continue;
|
|
153
|
+
// }
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
// const responseAccessParent = getParent(responseAccess);
|
|
156
|
+
// if (!responseAccessParent) {
|
|
157
|
+
// continue;
|
|
158
|
+
// }
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
160
|
+
// if (
|
|
161
|
+
// responseAccessParent.type === AST_NODE_TYPES.CallExpression &&
|
|
162
|
+
// responseAccessParent.arguments[0]?.type === AST_NODE_TYPES.ArrowFunctionExpression
|
|
163
|
+
// ) {
|
|
164
|
+
// // map-like operation against responses, e.g. responses.map((response) => response.headers.etag)
|
|
165
|
+
// responseHeadersAccesses.push(
|
|
166
|
+
// ...getResponseHeadersAccesses(
|
|
167
|
+
// scopeManager.getDeclaredVariables(responseAccessParent.arguments[0]),
|
|
168
|
+
// scopeManager,
|
|
169
|
+
// sourceCode,
|
|
170
|
+
// ),
|
|
171
|
+
// );
|
|
172
|
+
// continue;
|
|
173
|
+
// }
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
175
|
+
// if (
|
|
176
|
+
// responseAccess.computed &&
|
|
177
|
+
// responseAccess.property.type === AST_NODE_TYPES.Literal &&
|
|
178
|
+
// responseAccessParent.type === AST_NODE_TYPES.MemberExpression
|
|
179
|
+
// ) {
|
|
180
|
+
// // header access through indexed responses array, e.g. responses[0].headers, responses[1].get(...), etc.
|
|
181
|
+
// responseHeadersAccesses.push(responseAccessParent);
|
|
182
|
+
// } else {
|
|
183
|
+
// responseHeadersAccesses.push(responseAccess);
|
|
184
|
+
// }
|
|
185
|
+
// }
|
|
186
|
+
// }
|
|
187
|
+
// return responseHeadersAccesses;
|
|
188
|
+
// }
|
|
189
189
|
|
|
190
190
|
const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
191
|
-
const rule: ESLintUtils.RuleModule<'unknownError' | 'preferNativeFetch'
|
|
191
|
+
const rule: ESLintUtils.RuleModule<'unknownError' | 'preferNativeFetch'> = createRule({
|
|
192
192
|
name: ruleId,
|
|
193
193
|
meta: {
|
|
194
194
|
type: 'suggestion',
|
|
@@ -198,7 +198,7 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'preferNativeFetch' | 'shoul
|
|
|
198
198
|
},
|
|
199
199
|
messages: {
|
|
200
200
|
preferNativeFetch: 'Prefer native fetch API over customized fixture API.',
|
|
201
|
-
shouldUseHeaderGetter: 'Getter should be used to access response headers.',
|
|
201
|
+
// shouldUseHeaderGetter: 'Getter should be used to access response headers.',
|
|
202
202
|
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.',
|
|
203
203
|
},
|
|
204
204
|
fixable: 'code',
|
|
@@ -213,7 +213,6 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'preferNativeFetch' | 'shoul
|
|
|
213
213
|
return {
|
|
214
214
|
'CallExpression[callee.object.object.name="fixture"][callee.object.property.name="api"]': (
|
|
215
215
|
fixtureCall: TSESTree.CallExpression,
|
|
216
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
217
216
|
) => {
|
|
218
217
|
try {
|
|
219
218
|
if (!hasAssertions(fixtureCall)) {
|
|
@@ -294,51 +293,51 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'preferNativeFetch' | 'shoul
|
|
|
294
293
|
},
|
|
295
294
|
});
|
|
296
295
|
|
|
297
|
-
const responsesVariable = getEnclosingStatement(fixtureCallInformation.fixtureNode);
|
|
298
|
-
if (!responsesVariable) {
|
|
299
|
-
|
|
300
|
-
}
|
|
296
|
+
// const responsesVariable = getEnclosingStatement(fixtureCallInformation.fixtureNode);
|
|
297
|
+
// if (!responsesVariable) {
|
|
298
|
+
// return;
|
|
299
|
+
// }
|
|
301
300
|
|
|
302
|
-
const responseVariableReferences = scopeManager.getDeclaredVariables(responsesVariable);
|
|
303
|
-
const responseHeadersAccesses = getResponseHeadersAccesses(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
);
|
|
308
|
-
for (const responseHeadersAccess of responseHeadersAccesses) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
301
|
+
// const responseVariableReferences = scopeManager.getDeclaredVariables(responsesVariable);
|
|
302
|
+
// const responseHeadersAccesses = getResponseHeadersAccesses(
|
|
303
|
+
// responseVariableReferences,
|
|
304
|
+
// scopeManager,
|
|
305
|
+
// sourceCode,
|
|
306
|
+
// );
|
|
307
|
+
// for (const responseHeadersAccess of responseHeadersAccesses) {
|
|
308
|
+
// if (isInvalidResponseHeadersAccess(responseHeadersAccess)) {
|
|
309
|
+
// const headerAccess = getParent(responseHeadersAccess);
|
|
310
|
+
// if (headerAccess?.type === AST_NODE_TYPES.MemberExpression) {
|
|
311
|
+
// const headerNameNode = headerAccess.property;
|
|
312
|
+
// const headerName = headerAccess.computed
|
|
313
|
+
// ? sourceCode.getText(headerNameNode)
|
|
314
|
+
// : `'${sourceCode.getText(headerNameNode)}'`;
|
|
315
|
+
// const headerAccessReplacementText = `${sourceCode.getText(headerAccess.object)}.get(${headerName})`;
|
|
317
316
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
317
|
+
// context.report({
|
|
318
|
+
// node: headerAccess,
|
|
319
|
+
// messageId: 'shouldUseHeaderGetter',
|
|
320
|
+
// fix(fixer) {
|
|
321
|
+
// return fixer.replaceText(headerAccess, headerAccessReplacementText);
|
|
322
|
+
// },
|
|
323
|
+
// });
|
|
324
|
+
// } else if (
|
|
325
|
+
// headerAccess?.type === AST_NODE_TYPES.CallExpression &&
|
|
326
|
+
// responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier &&
|
|
327
|
+
// responseHeadersAccess.property.name === 'get'
|
|
328
|
+
// ) {
|
|
329
|
+
// const headerAccessReplacementText = `${sourceCode.getText(responseHeadersAccess.object)}.headers.get(${sourceCode.getText(headerAccess.arguments[0])})`;
|
|
331
330
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
331
|
+
// context.report({
|
|
332
|
+
// node: headerAccess,
|
|
333
|
+
// messageId: 'shouldUseHeaderGetter',
|
|
334
|
+
// fix(fixer) {
|
|
335
|
+
// return fixer.replaceText(headerAccess, headerAccessReplacementText);
|
|
336
|
+
// },
|
|
337
|
+
// });
|
|
338
|
+
// }
|
|
339
|
+
// }
|
|
340
|
+
// }
|
|
342
341
|
} catch (error) {
|
|
343
342
|
// eslint-disable-next-line no-console
|
|
344
343
|
console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
|
package/src/agent/fetch.ts
CHANGED
|
@@ -63,7 +63,9 @@ export function hasAssertions(fixtureCall: TSESTree.Node): boolean {
|
|
|
63
63
|
|
|
64
64
|
export function isFetchResponse(type: ts.Type): boolean {
|
|
65
65
|
return (
|
|
66
|
-
type.getProperties().some((symbol) => symbol.name === '
|
|
67
|
-
type.getProperties().some((symbol) => symbol.name === '
|
|
66
|
+
type.getProperties().some((symbol) => symbol.name === 'json') &&
|
|
67
|
+
type.getProperties().some((symbol) => symbol.name === 'status') &&
|
|
68
|
+
type.getProperties().some((symbol) => symbol.name === 'headers') &&
|
|
69
|
+
type.getProperties().some((symbol) => symbol.name === 'body')
|
|
68
70
|
);
|
|
69
71
|
}
|