@checkdigit/eslint-plugin 6.6.0-PR.75-a513 → 6.6.0-PR.75-8f9f
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-cjs/index.cjs +519 -428
- package/dist-cjs/metafile.json +169 -178
- package/dist-mjs/{fixture → agent}/add-url-domain.mjs +2 -2
- package/dist-mjs/{fixture → agent}/fetch-response-body-json.mjs +2 -2
- package/dist-mjs/agent/fetch-response-header-getter.mjs +117 -0
- package/dist-mjs/{fixture → agent}/fetch-then.mjs +6 -6
- package/dist-mjs/{fixture → agent}/fetch.mjs +3 -3
- package/dist-mjs/{fixture → agent}/no-fixture.mjs +6 -6
- package/dist-mjs/{fixture → agent}/no-full-response.mjs +4 -4
- package/dist-mjs/{fixture → agent}/no-service-wrapper.mjs +4 -4
- package/dist-mjs/{fixture → agent}/no-status-code.mjs +2 -2
- package/dist-mjs/{fixture → agent}/response-reference.mjs +3 -3
- package/dist-mjs/{fixture → agent}/url.mjs +2 -2
- package/dist-mjs/index.mjs +21 -18
- package/dist-mjs/library/format.mjs +14 -0
- package/dist-mjs/{ast → library}/tree.mjs +2 -2
- package/dist-mjs/library/ts-tree.mjs +72 -0
- package/dist-mjs/{fixture → library}/variable.mjs +2 -2
- package/dist-mjs/require-resolve-full-response.mjs +156 -0
- package/dist-types/index.d.ts +3 -2
- package/dist-types/{ast → library}/ts-tree.d.ts +1 -0
- package/dist-types/require-resolve-full-response.d.ts +4 -0
- package/package.json +1 -1
- package/src/{fixture → agent}/add-url-domain.ts +2 -2
- package/src/{fixture/fetch-response-header-getter-ts.ts → agent/fetch-response-header-getter.ts} +32 -21
- package/src/{fixture → agent}/fetch-then.ts +4 -5
- package/src/{fixture → agent}/fetch.ts +1 -1
- package/src/{fixture → agent}/no-fixture.ts +4 -5
- package/src/{fixture → agent}/no-full-response.ts +2 -2
- package/src/{fixture → agent}/no-service-wrapper.ts +2 -2
- package/src/{fixture → agent}/response-reference.ts +1 -1
- package/src/index.ts +18 -15
- package/src/{ast → library}/ts-tree.ts +11 -0
- package/src/require-resolve-full-response.ts +199 -0
- package/dist-mjs/ast/format.mjs +0 -14
- package/dist-mjs/ast/ts-tree.mjs +0 -65
- package/dist-mjs/fixture/fetch-header-getter.mjs +0 -71
- package/dist-mjs/fixture/fetch-response-header-getter-ts.mjs +0 -110
- package/dist-mjs/fixture/ts-tree.mjs +0 -12
- package/dist-types/fixture/fetch-header-getter.d.ts +0 -4
- package/dist-types/fixture/ts-tree.d.ts +0 -2
- package/src/fixture/fetch-header-getter.ts +0 -91
- package/src/fixture/ts-tree.ts +0 -14
- /package/dist-types/{fixture → agent}/add-url-domain.d.ts +0 -0
- /package/dist-types/{fixture → agent}/fetch-response-body-json.d.ts +0 -0
- /package/dist-types/{fixture/fetch-response-header-getter-ts.d.ts → agent/fetch-response-header-getter.d.ts} +0 -0
- /package/dist-types/{fixture → agent}/fetch-then.d.ts +0 -0
- /package/dist-types/{fixture → agent}/fetch.d.ts +0 -0
- /package/dist-types/{fixture → agent}/no-fixture.d.ts +0 -0
- /package/dist-types/{fixture → agent}/no-full-response.d.ts +0 -0
- /package/dist-types/{fixture → agent}/no-service-wrapper.d.ts +0 -0
- /package/dist-types/{fixture → agent}/no-status-code.d.ts +0 -0
- /package/dist-types/{fixture → agent}/response-reference.d.ts +0 -0
- /package/dist-types/{fixture → agent}/url.d.ts +0 -0
- /package/dist-types/{ast → library}/format.d.ts +0 -0
- /package/dist-types/{ast → library}/tree.d.ts +0 -0
- /package/dist-types/{fixture → library}/variable.d.ts +0 -0
- /package/src/{fixture → agent}/fetch-response-body-json.ts +0 -0
- /package/src/{fixture → agent}/no-status-code.ts +0 -0
- /package/src/{fixture → agent}/url.ts +0 -0
- /package/src/{ast → library}/format.ts +0 -0
- /package/src/{ast → library}/tree.ts +0 -0
- /package/src/{fixture → library}/variable.ts +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/library/ts-tree.ts
|
|
2
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
3
|
+
function getParent(node) {
|
|
4
|
+
return node.parent;
|
|
5
|
+
}
|
|
6
|
+
function getAncestor(node, matcher, exitMatcher) {
|
|
7
|
+
const parent = getParent(node);
|
|
8
|
+
if (!parent) {
|
|
9
|
+
return void 0;
|
|
10
|
+
} else if (typeof matcher === "string" && parent.type === matcher) {
|
|
11
|
+
return parent;
|
|
12
|
+
} else if (typeof matcher === "function" && matcher(parent)) {
|
|
13
|
+
return parent;
|
|
14
|
+
} else if (typeof exitMatcher === "string" && parent.type === exitMatcher) {
|
|
15
|
+
return void 0;
|
|
16
|
+
} else if (typeof exitMatcher === "function" && exitMatcher(parent)) {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
return getAncestor(parent, matcher, exitMatcher);
|
|
20
|
+
}
|
|
21
|
+
function isBlockStatement(node) {
|
|
22
|
+
return node.type.endsWith("Statement") || node.type.endsWith("Declaration");
|
|
23
|
+
}
|
|
24
|
+
function getEnclosingStatement(node) {
|
|
25
|
+
return getAncestor(node, isBlockStatement);
|
|
26
|
+
}
|
|
27
|
+
function getEnclosingScopeNode(node) {
|
|
28
|
+
return getAncestor(
|
|
29
|
+
node,
|
|
30
|
+
(parentNode) => ["FunctionExpression", "FunctionDeclaration", "ArrowFunctionExpression", "Program"].includes(parentNode.type)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
function isUsedInArrayOrAsArgument(node) {
|
|
34
|
+
if (isBlockStatement(node)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const parent = getParent(node);
|
|
38
|
+
if (!parent) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (parent.type === AST_NODE_TYPES.ArrayExpression || parent.type === AST_NODE_TYPES.CallExpression && parent.arguments.includes(node)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return isUsedInArrayOrAsArgument(parent);
|
|
45
|
+
}
|
|
46
|
+
function getEnclosingFunction(node) {
|
|
47
|
+
if (node.type === AST_NODE_TYPES.FunctionDeclaration || node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
48
|
+
return node;
|
|
49
|
+
}
|
|
50
|
+
const parent = getParent(node);
|
|
51
|
+
if (!parent) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
return getEnclosingFunction(parent);
|
|
55
|
+
}
|
|
56
|
+
function getTypeParentNode(node) {
|
|
57
|
+
if (!node) {
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
return node.type === AST_NODE_TYPES.TSTypeAnnotation || node.type === AST_NODE_TYPES.TSAsExpression ? node : getTypeParentNode(node.parent);
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
getAncestor,
|
|
64
|
+
getEnclosingFunction,
|
|
65
|
+
getEnclosingScopeNode,
|
|
66
|
+
getEnclosingStatement,
|
|
67
|
+
getParent,
|
|
68
|
+
getTypeParentNode,
|
|
69
|
+
isBlockStatement,
|
|
70
|
+
isUsedInArrayOrAsArgument
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2xpYnJhcnkvdHMtdHJlZS50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFRQSxTQUFTLHNCQUFnQztBQVFsQyxTQUFTLFVBQVUsTUFBdUQ7QUFDL0UsU0FBUSxLQUF3QztBQUNsRDtBQUVPLFNBQVMsWUFDZCxNQUNBLFNBQ0EsYUFDMkI7QUFDM0IsUUFBTSxTQUFTLFVBQVUsSUFBSTtBQUM3QixNQUFJLENBQUMsUUFBUTtBQUNYLFdBQU87QUFBQSxFQUNULFdBQVcsT0FBTyxZQUFZLFlBQVksT0FBTyxTQUFTLFNBQVM7QUFDakUsV0FBTztBQUFBLEVBQ1QsV0FBVyxPQUFPLFlBQVksY0FBYyxRQUFRLE1BQU0sR0FBRztBQUMzRCxXQUFPO0FBQUEsRUFDVCxXQUFXLE9BQU8sZ0JBQWdCLFlBQVksT0FBTyxTQUFTLGFBQWE7QUFDekUsV0FBTztBQUFBLEVBQ1QsV0FBVyxPQUFPLGdCQUFnQixjQUFjLFlBQVksTUFBTSxHQUFHO0FBQ25FLFdBQU87QUFBQSxFQUNUO0FBQ0EsU0FBTyxZQUFZLFFBQVEsU0FBUyxXQUFXO0FBQ2pEO0FBRU8sU0FBUyxpQkFBaUIsTUFBcUI7QUFDcEQsU0FBTyxLQUFLLEtBQUssU0FBUyxXQUFXLEtBQUssS0FBSyxLQUFLLFNBQVMsYUFBYTtBQUM1RTtBQUVPLFNBQVMsc0JBQXNCLE1BQXFCO0FBQ3pELFNBQU8sWUFBWSxNQUFNLGdCQUFnQjtBQUMzQztBQUVPLFNBQVMsc0JBQXNCLE1BQXFCO0FBQ3pELFNBQU87QUFBQSxJQUFZO0FBQUEsSUFBTSxDQUFDLGVBQ3hCLENBQUMsc0JBQXNCLHVCQUF1QiwyQkFBMkIsU0FBUyxFQUFFLFNBQVMsV0FBVyxJQUFJO0FBQUEsRUFDOUc7QUFDRjtBQUVPLFNBQVMsMEJBQTBCLE1BQXFCO0FBQzdELE1BQUksaUJBQWlCLElBQUksR0FBRztBQUMxQixXQUFPO0FBQUEsRUFDVDtBQUVBLFFBQU0sU0FBUyxVQUFVLElBQUk7QUFDN0IsTUFBSSxDQUFDLFFBQVE7QUFDWCxXQUFPO0FBQUEsRUFDVDtBQUVBLE1BQ0UsT0FBTyxTQUFTLGVBQWUsbUJBQzlCLE9BQU8sU0FBUyxlQUFlLGtCQUFrQixPQUFPLFVBQVUsU0FBUyxJQUEyQixHQUN2RztBQUNBLFdBQU87QUFBQSxFQUNUO0FBR0EsU0FBTywwQkFBMEIsTUFBTTtBQUN6QztBQUVPLFNBQVMscUJBQXFCLE1BQXFCO0FBQ3hELE1BQ0UsS0FBSyxTQUFTLGVBQWUsdUJBQzdCLEtBQUssU0FBUyxlQUFlLHNCQUM3QixLQUFLLFNBQVMsZUFBZSx5QkFDN0I7QUFDQSxXQUFPO0FBQUEsRUFDVDtBQUVBLFFBQU0sU0FBUyxVQUFVLElBQUk7QUFDN0IsTUFBSSxDQUFDLFFBQVE7QUFDWDtBQUFBLEVBQ0Y7QUFDQSxTQUFPLHFCQUFxQixNQUFNO0FBQ3BDO0FBRU8sU0FBUyxrQkFDZCxNQUNpRTtBQUNqRSxNQUFJLENBQUMsTUFBTTtBQUNULFdBQU87QUFBQSxFQUNUO0FBQ0EsU0FBTyxLQUFLLFNBQVMsZUFBZSxvQkFBb0IsS0FBSyxTQUFTLGVBQWUsaUJBQ2pGLE9BQ0Esa0JBQWtCLEtBQUssTUFBTTtBQUNuQzsiLAogICJuYW1lcyI6IFtdCn0K
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/library/variable.ts
|
|
2
2
|
function isValidPropertyName(name) {
|
|
3
3
|
return typeof name === "string" && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/u.test(name);
|
|
4
4
|
}
|
|
5
5
|
export {
|
|
6
6
|
isValidPropertyName
|
|
7
7
|
};
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2xpYnJhcnkvdmFyaWFibGUudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRU8sU0FBUyxvQkFBb0IsTUFBZTtBQUNqRCxTQUFPLE9BQU8sU0FBUyxZQUFZLDhCQUE4QixLQUFLLElBQUk7QUFDNUU7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// src/require-resolve-full-response.ts
|
|
2
|
+
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
3
|
+
import { DefinitionType } from "@typescript-eslint/scope-manager";
|
|
4
|
+
import { PLAIN_URL_REGEXP, TOKENIZED_URL_REGEXP } from "./agent/url.mjs";
|
|
5
|
+
import { strict as assert } from "node:assert";
|
|
6
|
+
import getDocumentationUrl from "./get-documentation-url.mjs";
|
|
7
|
+
import { getEnclosingScopeNode } from "./library/ts-tree.mjs";
|
|
8
|
+
var ruleId = "require-resolve-full-response";
|
|
9
|
+
var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
10
|
+
var rule = createRule({
|
|
11
|
+
name: ruleId,
|
|
12
|
+
meta: {
|
|
13
|
+
type: "suggestion",
|
|
14
|
+
docs: {
|
|
15
|
+
description: "Prefer native fetch over customized service wrapper."
|
|
16
|
+
},
|
|
17
|
+
messages: {
|
|
18
|
+
invalidOptions: '"options" argument should be provided with "resolveWithFullResponse" property set as "true". Otherwise, it indicates that the response body will be obtained without status code assertion which could result in unexpected issue.',
|
|
19
|
+
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.'
|
|
20
|
+
},
|
|
21
|
+
schema: []
|
|
22
|
+
},
|
|
23
|
+
defaultOptions: [],
|
|
24
|
+
create(context) {
|
|
25
|
+
const sourceCode = context.sourceCode;
|
|
26
|
+
const scopeManager = sourceCode.scopeManager;
|
|
27
|
+
const parserService = ESLintUtils.getParserServices(context);
|
|
28
|
+
const typeChecker = parserService.program.getTypeChecker();
|
|
29
|
+
function isUrlArgumentValid(urlArgument, scope) {
|
|
30
|
+
if (urlArgument?.type === AST_NODE_TYPES.Literal && typeof urlArgument.value === "string" || urlArgument?.type === AST_NODE_TYPES.TemplateLiteral) {
|
|
31
|
+
const urlText = sourceCode.getText(urlArgument);
|
|
32
|
+
return PLAIN_URL_REGEXP.test(urlText) || TOKENIZED_URL_REGEXP.test(urlText);
|
|
33
|
+
}
|
|
34
|
+
if (urlArgument?.type === AST_NODE_TYPES.Identifier) {
|
|
35
|
+
const foundVariable = scope.variables.find((variable) => variable.name === urlArgument.name);
|
|
36
|
+
if (foundVariable) {
|
|
37
|
+
const variableDefinition = foundVariable.defs.find((def) => def.type === DefinitionType.Variable);
|
|
38
|
+
assert.ok(variableDefinition, `Variable "${urlArgument.name}" not defined in scope`);
|
|
39
|
+
const variableDefinitionNode = variableDefinition.node;
|
|
40
|
+
assert.ok(variableDefinitionNode.type === AST_NODE_TYPES.VariableDeclarator);
|
|
41
|
+
assert.ok(variableDefinitionNode.init, "Variable definition node has no init property");
|
|
42
|
+
return isUrlArgumentValid(variableDefinitionNode.init, scope);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
function getType(identifier) {
|
|
48
|
+
const variable = parserService.esTreeNodeToTSNodeMap.get(identifier);
|
|
49
|
+
const variableType = typeChecker.getTypeAtLocation(variable);
|
|
50
|
+
return typeChecker.typeToString(variableType);
|
|
51
|
+
}
|
|
52
|
+
function isServiceLikeName(name) {
|
|
53
|
+
return /.*[Ss]ervice$/u.test(name);
|
|
54
|
+
}
|
|
55
|
+
function isCalleeServiceWrapper(serviceCall) {
|
|
56
|
+
const callee = serviceCall.callee;
|
|
57
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const endpoint = callee.object;
|
|
61
|
+
if (endpoint.type === AST_NODE_TYPES.Identifier) {
|
|
62
|
+
return getType(endpoint) === "Endpoint" || isServiceLikeName(endpoint.name);
|
|
63
|
+
}
|
|
64
|
+
if (endpoint.type !== AST_NODE_TYPES.CallExpression) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const [contextArgument] = endpoint.arguments;
|
|
68
|
+
if (contextArgument?.type !== AST_NODE_TYPES.Identifier) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (contextArgument.name !== "EMPTY_CONTEXT" && getType(contextArgument) !== "InboundContext") {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const service = endpoint.callee;
|
|
75
|
+
if (service.type === AST_NODE_TYPES.Identifier) {
|
|
76
|
+
return getType(service) === "ResolvedService";
|
|
77
|
+
}
|
|
78
|
+
if (service.type !== AST_NODE_TYPES.MemberExpression) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const services = service.object;
|
|
82
|
+
if (services.type === AST_NODE_TYPES.Identifier) {
|
|
83
|
+
return getType(services) === "ResolvedServices";
|
|
84
|
+
}
|
|
85
|
+
if (services.type !== AST_NODE_TYPES.MemberExpression) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const configuration = services.object;
|
|
89
|
+
if (configuration.type === AST_NODE_TYPES.Identifier) {
|
|
90
|
+
return ["Configuration", "Configuration<ResolvedServices>"].includes(getType(configuration));
|
|
91
|
+
}
|
|
92
|
+
if (configuration.type !== AST_NODE_TYPES.MemberExpression) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const fixture = configuration.object;
|
|
96
|
+
if (fixture.type === AST_NODE_TYPES.Identifier) {
|
|
97
|
+
return fixture.name === "fixture" || getType(fixture) === "Fixture";
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
"CallExpression[callee.property.name=/^(head|get|put|post|del|patch)$/]": (serviceCall) => {
|
|
103
|
+
try {
|
|
104
|
+
if (!isCalleeServiceWrapper(serviceCall)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const enclosingScopeNode = getEnclosingScopeNode(serviceCall);
|
|
108
|
+
assert.ok(enclosingScopeNode, "enclosingScopeNode is undefined");
|
|
109
|
+
const scope = scopeManager?.acquire(enclosingScopeNode);
|
|
110
|
+
assert.ok(scope, "scope is undefined");
|
|
111
|
+
const urlArgument = serviceCall.arguments[0];
|
|
112
|
+
if (!isUrlArgumentValid(urlArgument, scope)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
assert.ok(serviceCall.callee.type === AST_NODE_TYPES.MemberExpression);
|
|
116
|
+
assert.ok(serviceCall.callee.property.type === AST_NODE_TYPES.Identifier);
|
|
117
|
+
const method = serviceCall.callee.property.name;
|
|
118
|
+
const optionsArgument = ["get", "head", "del"].includes(method) ? serviceCall.arguments[1] : serviceCall.arguments[2];
|
|
119
|
+
if (optionsArgument === void 0 || optionsArgument.type !== AST_NODE_TYPES.ObjectExpression) {
|
|
120
|
+
context.report({
|
|
121
|
+
node: serviceCall,
|
|
122
|
+
messageId: "invalidOptions"
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const resolveWithFullResponseProperty = optionsArgument.properties.find(
|
|
127
|
+
(property) => property.type === AST_NODE_TYPES.Property && property.key.type === AST_NODE_TYPES.Identifier && property.key.name === "resolveWithFullResponse"
|
|
128
|
+
);
|
|
129
|
+
if (resolveWithFullResponseProperty?.type !== AST_NODE_TYPES.Property || resolveWithFullResponseProperty.value.type !== AST_NODE_TYPES.Literal || resolveWithFullResponseProperty.value.value !== true) {
|
|
130
|
+
context.report({
|
|
131
|
+
node: optionsArgument,
|
|
132
|
+
messageId: "invalidOptions"
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
|
|
138
|
+
context.report({
|
|
139
|
+
node: serviceCall,
|
|
140
|
+
messageId: "unknownError",
|
|
141
|
+
data: {
|
|
142
|
+
fileName: context.filename,
|
|
143
|
+
error: error instanceof Error ? error.toString() : JSON.stringify(error)
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
var require_resolve_full_response_default = rule;
|
|
152
|
+
export {
|
|
153
|
+
require_resolve_full_response_default as default,
|
|
154
|
+
ruleId
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL3JlcXVpcmUtcmVzb2x2ZS1mdWxsLXJlc3BvbnNlLnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQVFBLFNBQVMsZ0JBQWdCLG1CQUE2QjtBQUN0RCxTQUFTLHNCQUFrQztBQUMzQyxTQUFTLGtCQUFrQiw0QkFBNEI7QUFDdkQsU0FBUyxVQUFVLGNBQWM7QUFDakMsT0FBTyx5QkFBeUI7QUFDaEMsU0FBUyw2QkFBNkI7QUFFL0IsSUFBTSxTQUFTO0FBRXRCLElBQU0sYUFBYSxZQUFZLFlBQVksQ0FBQyxTQUFTLG9CQUFvQixJQUFJLENBQUM7QUFFOUUsSUFBTSxPQUFPLFdBQVc7QUFBQSxFQUN0QixNQUFNO0FBQUEsRUFDTixNQUFNO0FBQUEsSUFDSixNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsTUFDSixhQUFhO0FBQUEsSUFDZjtBQUFBLElBQ0EsVUFBVTtBQUFBLE1BQ1IsZ0JBQ0U7QUFBQSxNQUNGLGNBQWM7QUFBQSxJQUNoQjtBQUFBLElBQ0EsUUFBUSxDQUFDO0FBQUEsRUFDWDtBQUFBLEVBQ0EsZ0JBQWdCLENBQUM7QUFBQSxFQUNqQixPQUFPLFNBQVM7QUFDZCxVQUFNLGFBQWEsUUFBUTtBQUMzQixVQUFNLGVBQWUsV0FBVztBQUNoQyxVQUFNLGdCQUFnQixZQUFZLGtCQUFrQixPQUFPO0FBQzNELFVBQU0sY0FBYyxjQUFjLFFBQVEsZUFBZTtBQUV6RCxhQUFTLG1CQUFtQixhQUF3QyxPQUFjO0FBQ2hGLFVBQ0csYUFBYSxTQUFTLGVBQWUsV0FBVyxPQUFPLFlBQVksVUFBVSxZQUM5RSxhQUFhLFNBQVMsZUFBZSxpQkFDckM7QUFDQSxjQUFNLFVBQVUsV0FBVyxRQUFRLFdBQVc7QUFDOUMsZUFBTyxpQkFBaUIsS0FBSyxPQUFPLEtBQUsscUJBQXFCLEtBQUssT0FBTztBQUFBLE1BQzVFO0FBRUEsVUFBSSxhQUFhLFNBQVMsZUFBZSxZQUFZO0FBQ25ELGNBQU0sZ0JBQWdCLE1BQU0sVUFBVSxLQUFLLENBQUMsYUFBYSxTQUFT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
|
package/dist-types/index.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ declare const _default: {
|
|
|
12
12
|
"invalid-json-stringify": import("eslint").Rule.RuleModule;
|
|
13
13
|
"no-promise-instance-method": import("eslint").Rule.RuleModule;
|
|
14
14
|
"no-fixture": import("eslint").Rule.RuleModule;
|
|
15
|
-
"fetch-header-getter": import("eslint").Rule.RuleModule;
|
|
16
15
|
"fetch-then": import("eslint").Rule.RuleModule;
|
|
17
16
|
"no-service-wrapper": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unknownError" | "preferNativeFetch" | "invalidOptions", never[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
18
17
|
"no-status-code": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unknownError" | "replaceStatusCode", never[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
@@ -20,6 +19,7 @@ declare const _default: {
|
|
|
20
19
|
"fetch-response-header-getter-ts": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unknownError" | "useGetter", never[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
21
20
|
"add-url-domain": import("@typescript-eslint/utils/ts-eslint").RuleModule<"addDomain" | "unknownError", never[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
22
21
|
"no-full-response": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unknownError" | "removeFullResponse", never[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
22
|
+
"require-resolve-full-response": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unknownError" | "invalidOptions", never[], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
23
23
|
};
|
|
24
24
|
configs: {
|
|
25
25
|
all: {
|
|
@@ -35,6 +35,8 @@ declare const _default: {
|
|
|
35
35
|
'@checkdigit/no-test-import': string;
|
|
36
36
|
"@checkdigit/invalid-json-stringify": string;
|
|
37
37
|
"@checkdigit/no-promise-instance-method": string;
|
|
38
|
+
"@checkdigit/no-full-response": string;
|
|
39
|
+
"@checkdigit/require-resolve-full-response": string;
|
|
38
40
|
};
|
|
39
41
|
};
|
|
40
42
|
recommended: {
|
|
@@ -55,7 +57,6 @@ declare const _default: {
|
|
|
55
57
|
agent: {
|
|
56
58
|
rules: {
|
|
57
59
|
"@checkdigit/no-fixture": string;
|
|
58
|
-
"@checkdigit/fetch-header-getter": string;
|
|
59
60
|
"@checkdigit/fetch-then": string;
|
|
60
61
|
"@checkdigit/no-service-wrapper": string;
|
|
61
62
|
"@checkdigit/no-status-code": string;
|
|
@@ -6,3 +6,4 @@ export declare function getEnclosingStatement(node: TSESTree.Node): TSESTree.Nod
|
|
|
6
6
|
export declare function getEnclosingScopeNode(node: TSESTree.Node): TSESTree.Node | undefined;
|
|
7
7
|
export declare function isUsedInArrayOrAsArgument(node: TSESTree.Node): boolean;
|
|
8
8
|
export declare function getEnclosingFunction(node: TSESTree.Node): TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclarationWithOptionalName | TSESTree.FunctionExpression | undefined;
|
|
9
|
+
export declare function getTypeParentNode(node: TSESTree.Node | undefined): TSESTree.TSTypeAnnotation | TSESTree.TSAsExpression | undefined;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@checkdigit/eslint-plugin","version":"6.6.0-PR.75-
|
|
1
|
+
{"name":"@checkdigit/eslint-plugin","version":"6.6.0-PR.75-8f9f","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","require":"./dist-cjs/index.cjs","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"files":["src","dist-types","dist-cjs","dist-mjs","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-cjs/**/*.test.cjs","!dist-cjs/**/*.spec.cjs","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"scripts":{"build:dist-cjs":"rimraf dist-cjs && npx builder --type=commonjs --sourceMap --entryPoint=index.ts --outDir=dist-cjs --outFile=index.cjs --external=espree && echo \"module.exports = module.exports.default;\" >> dist-cjs/index.cjs","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 --ignore-path .gitignore .","lint:fix":"eslint --ignore-path .gitignore . --fix","prepublishOnly":"npm run build:dist-types && npm run build:dist-cjs && 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":"7.18.0","@typescript-eslint/utils":"7.18.0","ts-api-utils":"^1.3.0"},"devDependencies":{"@checkdigit/jest-config":"^6.0.2","@checkdigit/prettier-config":"^5.5.0","@checkdigit/typescript-config":"6.0.0","@types/eslint":"^8.56.10","@typescript-eslint/eslint-plugin":"^7.18.0","@typescript-eslint/parser":"^7.18.0","@typescript-eslint/rule-tester":"7.18.0","eslint-config-prettier":"^9.1.0","eslint-plugin-eslint-plugin":"^6.2.0","eslint-plugin-import":"^2.29.1","eslint-plugin-no-only-tests":"^3.1.0","eslint-plugin-no-secrets":"^1.0.2","eslint-plugin-node":"^11.1.0","eslint-plugin-sonarjs":"0.24.0","http-status-codes":"^2.3.0"},"peerDependencies":{"eslint":">=8 <9"},"engines":{"node":">=20.14"}}
|
|
@@ -43,8 +43,8 @@ const rule = createRule({
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const urlText = sourceCode.getText(basePathDeclarator.init);
|
|
47
|
-
const replacement = addBasePathUrlDomain(urlText);
|
|
46
|
+
const urlText = sourceCode.getText(basePathDeclarator.init);
|
|
47
|
+
const replacement = addBasePathUrlDomain(urlText);
|
|
48
48
|
|
|
49
49
|
if (replacement !== urlText) {
|
|
50
50
|
context.report({
|
package/src/{fixture/fetch-response-header-getter-ts.ts → agent/fetch-response-header-getter.ts}
RENAMED
|
@@ -10,6 +10,7 @@ import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
|
|
|
10
10
|
import getDocumentationUrl from '../get-documentation-url';
|
|
11
11
|
|
|
12
12
|
export const ruleId = 'fetch-response-header-getter-ts';
|
|
13
|
+
const HEADER_BUILTIN_FUNCTIONS = Object.keys(Headers.prototype);
|
|
13
14
|
|
|
14
15
|
const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
15
16
|
|
|
@@ -34,37 +35,38 @@ const rule = createRule({
|
|
|
34
35
|
const sourceCode = context.sourceCode;
|
|
35
36
|
|
|
36
37
|
return {
|
|
37
|
-
|
|
38
|
+
MemberExpression: (responseHeadersAccess: TSESTree.MemberExpression) => {
|
|
38
39
|
try {
|
|
39
40
|
if (
|
|
40
41
|
responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier &&
|
|
41
|
-
responseHeadersAccess.property.name
|
|
42
|
+
HEADER_BUILTIN_FUNCTIONS.includes(responseHeadersAccess.property.name)
|
|
42
43
|
) {
|
|
43
|
-
//
|
|
44
|
+
// skip Headers's built-in function calls
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
const responseHeadersTsNode = parserServices.esTreeNodeToTSNodeMap.get(responseHeadersAccess.object);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
let responseHeadersType = typeChecker.getTypeAtLocation(responseHeadersTsNode);
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
51
|
+
responseHeadersType = responseHeadersType.isUnion() ? responseHeadersType.types[0]! : responseHeadersType;
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
53
|
+
const responseHeadersTypeName = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
54
|
+
(responseHeadersType.symbol ?? responseHeadersType.aliasSymbol)?.escapedName;
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
|
|
56
|
+
if (responseHeadersTypeName !== 'Headers' && responseHeadersTypeName !== 'HeaderGetter') {
|
|
52
57
|
return;
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
// let replacementText = 'xxx';
|
|
56
|
-
// if (responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier) {
|
|
57
|
-
// replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})`;
|
|
58
|
-
// }
|
|
59
60
|
let replacementText: string;
|
|
60
|
-
if (responseHeadersAccess.
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
if (!responseHeadersAccess.computed) {
|
|
62
|
+
// e.g. headers.etag
|
|
63
|
+
replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get('${sourceCode.getText(responseHeadersAccess.property)}')`;
|
|
64
|
+
} else if (
|
|
65
|
+
responseHeadersAccess.property.type === AST_NODE_TYPES.Identifier ||
|
|
66
|
+
responseHeadersAccess.property.type === AST_NODE_TYPES.Literal ||
|
|
67
|
+
responseHeadersAccess.property.type === AST_NODE_TYPES.TemplateLiteral
|
|
68
|
+
) {
|
|
63
69
|
replacementText = `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})`;
|
|
64
|
-
} else if (responseHeadersAccess.property.type === AST_NODE_TYPES.Literal) {
|
|
65
|
-
replacementText = responseHeadersAccess.computed
|
|
66
|
-
? `${sourceCode.getText(responseHeadersAccess.object)}.get(${sourceCode.getText(responseHeadersAccess.property)})`
|
|
67
|
-
: `${sourceCode.getText(responseHeadersAccess.object)}.get('${sourceCode.getText(responseHeadersAccess.property)}')`;
|
|
68
70
|
} else {
|
|
69
71
|
throw new Error(`Unexpected property type: ${responseHeadersAccess.property.type}`);
|
|
70
72
|
}
|
|
@@ -89,14 +91,21 @@ const rule = createRule({
|
|
|
89
91
|
});
|
|
90
92
|
}
|
|
91
93
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
) => {
|
|
94
|
+
|
|
95
|
+
// convert response.get() to response.headers.get()
|
|
96
|
+
'CallExpression[callee.property.name="get"]': (responseHeadersAccess: TSESTree.CallExpression) => {
|
|
95
97
|
try {
|
|
96
98
|
if (responseHeadersAccess.callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
97
99
|
return;
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
// skip request-like calls
|
|
103
|
+
if (
|
|
104
|
+
responseHeadersAccess.callee.object.type !== AST_NODE_TYPES.Identifier ||
|
|
105
|
+
responseHeadersAccess.callee.object.name === 'request'
|
|
106
|
+
) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
100
109
|
const responseNode = responseHeadersAccess.callee.object;
|
|
101
110
|
const responseHeadersTsNode = parserServices.esTreeNodeToTSNodeMap.get(responseNode);
|
|
102
111
|
const responseType = typeChecker.getTypeAtLocation(responseHeadersTsNode);
|
|
@@ -104,6 +113,8 @@ const rule = createRule({
|
|
|
104
113
|
if (typeName === 'InboundContext' || typeName.endsWith('RequestType')) {
|
|
105
114
|
return;
|
|
106
115
|
}
|
|
116
|
+
|
|
117
|
+
// make sure the response type has "headers" property
|
|
107
118
|
const hasHeadersProperty = responseType.getProperties().some((symbol) => symbol.name === 'headers');
|
|
108
119
|
if (!hasHeadersProperty) {
|
|
109
120
|
return;
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
import type { CallExpression, Expression, MemberExpression, SimpleCallExpression } from 'estree';
|
|
10
10
|
import { type Rule, type Scope, SourceCode } from 'eslint';
|
|
11
|
-
import { getEnclosingFunction, getEnclosingStatement, getParent, isUsedInArrayOrAsArgument } from '../
|
|
11
|
+
import { getEnclosingFunction, getEnclosingStatement, getParent, isUsedInArrayOrAsArgument } from '../library/tree';
|
|
12
12
|
import { hasAssertions, isInvalidResponseHeadersAccess } from './fetch';
|
|
13
13
|
import { strict as assert } from 'node:assert';
|
|
14
14
|
import getDocumentationUrl from '../get-documentation-url';
|
|
15
|
-
import { getIndentation } from '../
|
|
16
|
-
import { isValidPropertyName } from '
|
|
15
|
+
import { getIndentation } from '../library/format';
|
|
16
|
+
import { isValidPropertyName } from '../library/variable';
|
|
17
17
|
import { replaceEndpointUrlPrefixWithBasePath } from './url';
|
|
18
18
|
|
|
19
19
|
export const ruleId = 'fetch-then';
|
|
@@ -193,8 +193,7 @@ const rule: Rule.RuleModule = {
|
|
|
193
193
|
messages: {
|
|
194
194
|
preferNativeFetch: 'Prefer native fetch API over customized fixture API.',
|
|
195
195
|
shouldUseHeaderGetter: 'Getter should be used to access response headers.',
|
|
196
|
-
unknownError:
|
|
197
|
-
'Unknown error occurred in file "{{fileName}}": {{ error }}. Please manually convert the fixture API call to fetch API call.',
|
|
196
|
+
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.',
|
|
198
197
|
},
|
|
199
198
|
fixable: 'code',
|
|
200
199
|
schema: [],
|
|
@@ -23,13 +23,13 @@ import {
|
|
|
23
23
|
getEnclosingStatement,
|
|
24
24
|
getParent,
|
|
25
25
|
isUsedInArrayOrAsArgument,
|
|
26
|
-
} from '../
|
|
26
|
+
} from '../library/tree';
|
|
27
27
|
import { getResponseBodyRetrievalText, hasAssertions } from './fetch';
|
|
28
28
|
import { analyzeResponseReferences } from './response-reference';
|
|
29
29
|
import { strict as assert } from 'node:assert';
|
|
30
30
|
import getDocumentationUrl from '../get-documentation-url';
|
|
31
|
-
import { getIndentation } from '../
|
|
32
|
-
import { isValidPropertyName } from '
|
|
31
|
+
import { getIndentation } from '../library/format';
|
|
32
|
+
import { isValidPropertyName } from '../library/variable';
|
|
33
33
|
import { replaceEndpointUrlPrefixWithBasePath } from './url';
|
|
34
34
|
|
|
35
35
|
export const ruleId = 'no-fixture';
|
|
@@ -228,8 +228,7 @@ const rule: Rule.RuleModule = {
|
|
|
228
228
|
},
|
|
229
229
|
messages: {
|
|
230
230
|
preferNativeFetch: 'Prefer native fetch API over customized fixture API.',
|
|
231
|
-
unknownError:
|
|
232
|
-
'Unknown error occurred in file "{{fileName}}": {{ error }}. Please manually convert the fixture API call to fetch API call.',
|
|
231
|
+
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.',
|
|
233
232
|
},
|
|
234
233
|
fixable: 'code',
|
|
235
234
|
schema: [],
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
|
|
10
10
|
import { strict as assert } from 'node:assert';
|
|
11
11
|
import getDocumentationUrl from '../get-documentation-url';
|
|
12
|
-
import { getTypeParentNode } from '
|
|
12
|
+
import { getTypeParentNode } from '../library/ts-tree';
|
|
13
13
|
|
|
14
14
|
export const ruleId = 'no-full-response';
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ const rule = createRule({
|
|
|
23
23
|
description: 'Remove the usage of FullResponse type.',
|
|
24
24
|
},
|
|
25
25
|
messages: {
|
|
26
|
-
removeFullResponse: '
|
|
26
|
+
removeFullResponse: 'Removing the usage of FullResponse type.',
|
|
27
27
|
unknownError: 'Unknown error occurred in file "{{fileName}}": {{ error }}.',
|
|
28
28
|
},
|
|
29
29
|
fixable: 'code',
|
|
@@ -11,8 +11,8 @@ import { DefinitionType, type Scope } from '@typescript-eslint/scope-manager';
|
|
|
11
11
|
import { PLAIN_URL_REGEXP, TOKENIZED_URL_REGEXP, replaceEndpointUrlPrefixWithDomain } from './url';
|
|
12
12
|
import { strict as assert } from 'node:assert';
|
|
13
13
|
import getDocumentationUrl from '../get-documentation-url';
|
|
14
|
-
import { getEnclosingScopeNode } from '../
|
|
15
|
-
import { getIndentation } from '../
|
|
14
|
+
import { getEnclosingScopeNode } from '../library/ts-tree';
|
|
15
|
+
import { getIndentation } from '../library/format';
|
|
16
16
|
|
|
17
17
|
export const ruleId = 'no-service-wrapper';
|
|
18
18
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { MemberExpression, VariableDeclaration } from 'estree';
|
|
10
10
|
import { type Scope } from 'eslint';
|
|
11
11
|
import { strict as assert } from 'node:assert';
|
|
12
|
-
import { getParent } from '../
|
|
12
|
+
import { getParent } from '../library/tree';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* analyze response related variables and their references
|
package/src/index.ts
CHANGED
|
@@ -6,19 +6,21 @@
|
|
|
6
6
|
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import addUrlDomain, { ruleId as addUrlDomainRuleId } from './
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from './
|
|
15
|
-
import fetchThen, { ruleId as fetchThenRuleId } from './fixture/fetch-then';
|
|
9
|
+
import addUrlDomain, { ruleId as addUrlDomainRuleId } from './agent/add-url-domain';
|
|
10
|
+
import fetchResponseBodyJson, { ruleId as fetchResponseBodyJsonRuleId } from './agent/fetch-response-body-json';
|
|
11
|
+
import fetchResponseHeaderGetter, {
|
|
12
|
+
ruleId as fetchResponseHeaderGetterRuleId,
|
|
13
|
+
} from './agent/fetch-response-header-getter';
|
|
14
|
+
import fetchThen, { ruleId as fetchThenRuleId } from './agent/fetch-then';
|
|
16
15
|
import invalidJsonStringify, { ruleId as invalidJsonStringifyRuleId } from './invalid-json-stringify';
|
|
17
|
-
import noFixture, { ruleId as noFixtureRuleId } from './
|
|
18
|
-
import noFullResponse, { ruleId as noFullResponseRuleId } from './
|
|
16
|
+
import noFixture, { ruleId as noFixtureRuleId } from './agent/no-fixture';
|
|
17
|
+
import noFullResponse, { ruleId as noFullResponseRuleId } from './agent/no-full-response';
|
|
19
18
|
import noPromiseInstanceMethod, { ruleId as noPromiseInstanceMethodRuleId } from './no-promise-instance-method';
|
|
20
|
-
import noServiceWrapper, { ruleId as noServiceWrapperRuleId } from './
|
|
21
|
-
import noStatusCode, { ruleId as noStatusCodeRuleId } from './
|
|
19
|
+
import noServiceWrapper, { ruleId as noServiceWrapperRuleId } from './agent/no-service-wrapper';
|
|
20
|
+
import noStatusCode, { ruleId as noStatusCodeRuleId } from './agent/no-status-code';
|
|
21
|
+
import requireResolveFullResponse, {
|
|
22
|
+
ruleId as requireResolveFullResponseRuleId,
|
|
23
|
+
} from './require-resolve-full-response';
|
|
22
24
|
import filePathComment from './file-path-comment';
|
|
23
25
|
import noCardNumbers from './no-card-numbers';
|
|
24
26
|
import noTestImport from './no-test-import';
|
|
@@ -43,14 +45,14 @@ export default {
|
|
|
43
45
|
[invalidJsonStringifyRuleId]: invalidJsonStringify,
|
|
44
46
|
[noPromiseInstanceMethodRuleId]: noPromiseInstanceMethod,
|
|
45
47
|
[noFixtureRuleId]: noFixture,
|
|
46
|
-
[fetchHeaderGetterRuleId]: fetchHeaderGetter,
|
|
47
48
|
[fetchThenRuleId]: fetchThen,
|
|
48
49
|
[noServiceWrapperRuleId]: noServiceWrapper,
|
|
49
50
|
[noStatusCodeRuleId]: noStatusCode,
|
|
50
51
|
[fetchResponseBodyJsonRuleId]: fetchResponseBodyJson,
|
|
51
|
-
[
|
|
52
|
+
[fetchResponseHeaderGetterRuleId]: fetchResponseHeaderGetter,
|
|
52
53
|
[addUrlDomainRuleId]: addUrlDomain,
|
|
53
54
|
[noFullResponseRuleId]: noFullResponse,
|
|
55
|
+
[requireResolveFullResponseRuleId]: requireResolveFullResponse,
|
|
54
56
|
},
|
|
55
57
|
configs: {
|
|
56
58
|
all: {
|
|
@@ -66,6 +68,8 @@ export default {
|
|
|
66
68
|
'@checkdigit/no-test-import': 'error',
|
|
67
69
|
[`@checkdigit/${invalidJsonStringifyRuleId}`]: 'error',
|
|
68
70
|
[`@checkdigit/${noPromiseInstanceMethodRuleId}`]: 'error',
|
|
71
|
+
[`@checkdigit/${noFullResponseRuleId}`]: 'error',
|
|
72
|
+
[`@checkdigit/${requireResolveFullResponseRuleId}`]: 'error',
|
|
69
73
|
},
|
|
70
74
|
},
|
|
71
75
|
recommended: {
|
|
@@ -86,12 +90,11 @@ export default {
|
|
|
86
90
|
agent: {
|
|
87
91
|
rules: {
|
|
88
92
|
[`@checkdigit/${noFixtureRuleId}`]: 'error',
|
|
89
|
-
[`@checkdigit/${fetchHeaderGetterRuleId}`]: 'error',
|
|
90
93
|
[`@checkdigit/${fetchThenRuleId}`]: 'error',
|
|
91
94
|
[`@checkdigit/${noServiceWrapperRuleId}`]: 'error',
|
|
92
95
|
[`@checkdigit/${noStatusCodeRuleId}`]: 'error',
|
|
93
96
|
[`@checkdigit/${fetchResponseBodyJsonRuleId}`]: 'error',
|
|
94
|
-
[`@checkdigit/${
|
|
97
|
+
[`@checkdigit/${fetchResponseHeaderGetterRuleId}`]: 'error',
|
|
95
98
|
[`@checkdigit/${addUrlDomainRuleId}`]: 'error',
|
|
96
99
|
[`@checkdigit/${noFullResponseRuleId}`]: 'error',
|
|
97
100
|
},
|
|
@@ -88,3 +88,14 @@ export function getEnclosingFunction(node: TSESTree.Node) {
|
|
|
88
88
|
}
|
|
89
89
|
return getEnclosingFunction(parent);
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
export function getTypeParentNode(
|
|
93
|
+
node: TSESTree.Node | undefined,
|
|
94
|
+
): TSESTree.TSTypeAnnotation | TSESTree.TSAsExpression | undefined {
|
|
95
|
+
if (!node) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return node.type === AST_NODE_TYPES.TSTypeAnnotation || node.type === AST_NODE_TYPES.TSAsExpression
|
|
99
|
+
? node
|
|
100
|
+
: getTypeParentNode(node.parent);
|
|
101
|
+
}
|