@checkdigit/eslint-plugin 7.3.0-PR.75-f90a → 7.3.0-PR.75-4919
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
CHANGED
|
@@ -15,7 +15,7 @@ Copyright (c) 2021-2024 [Check Digit, LLC](https://checkdigit.com)
|
|
|
15
15
|
- `@checkdigit/no-test-import`
|
|
16
16
|
- `@checkdigit/no-promise-instance-method`
|
|
17
17
|
- `@checkdigit/invalid-json-stringify`
|
|
18
|
-
- `@checkdigit/no-
|
|
18
|
+
- `@checkdigit/no-legacy-service-typing`
|
|
19
19
|
- `@checkdigit/require-resolve-full-response`
|
|
20
20
|
- `@checkdigit/require-type-out-of-type-only-imports`
|
|
21
21
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/agent/fetch-response-body-json.ts
|
|
2
|
+
import { strict as assert } from "node:assert";
|
|
2
3
|
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
3
4
|
import getDocumentationUrl from "../get-documentation-url.mjs";
|
|
5
|
+
import { getAncestor } from "../library/ts-tree.mjs";
|
|
4
6
|
var ruleId = "fetch-response-body-json";
|
|
5
7
|
var createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
6
8
|
var rule = createRule({
|
|
@@ -21,29 +23,47 @@ var rule = createRule({
|
|
|
21
23
|
create(context) {
|
|
22
24
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
23
25
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
24
|
-
const
|
|
26
|
+
const allChanges = /* @__PURE__ */ new Map();
|
|
25
27
|
return {
|
|
26
|
-
'MemberExpression[property.name="body"]': (
|
|
28
|
+
'MemberExpression[property.name="body"]': (responseBodyNode) => {
|
|
27
29
|
try {
|
|
28
|
-
const responseNode = parserServices.esTreeNodeToTSNodeMap.get(
|
|
30
|
+
const responseNode = parserServices.esTreeNodeToTSNodeMap.get(responseBodyNode.object);
|
|
29
31
|
const responseType = typeChecker.getTypeAtLocation(responseNode);
|
|
30
32
|
const shouldReplace = responseType.getProperties().some((symbol) => symbol.name === "body") && responseType.getProperties().some((symbol) => symbol.name === "json");
|
|
31
33
|
if (shouldReplace) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
const enclosingFunction = getAncestor(
|
|
35
|
+
responseBodyNode,
|
|
36
|
+
(node) => node.type === AST_NODE_TYPES.ArrowFunctionExpression || node.type === AST_NODE_TYPES.FunctionExpression || node.type === AST_NODE_TYPES.FunctionDeclaration
|
|
37
|
+
);
|
|
38
|
+
const enclosingStatement = getAncestor(
|
|
39
|
+
responseBodyNode,
|
|
40
|
+
(node) => (node.type === AST_NODE_TYPES.VariableDeclaration || node.type === AST_NODE_TYPES.ExpressionStatement || node.type === AST_NODE_TYPES.ReturnStatement) && node.parent.type === AST_NODE_TYPES.BlockStatement
|
|
41
|
+
);
|
|
42
|
+
const enclosingStatementIndex = enclosingFunction.body.body.indexOf(
|
|
43
|
+
enclosingStatement
|
|
44
|
+
);
|
|
45
|
+
const responseVariableName = responseBodyNode.object.name;
|
|
46
|
+
const isResponseBodyVariableDeclared = enclosingStatement.type === AST_NODE_TYPES.VariableDeclaration && enclosingStatement.declarations.some((declaration) => declaration.init === responseBodyNode);
|
|
47
|
+
const responseBodyVariableName = isResponseBodyVariableDeclared ? enclosingStatement.declarations.find((declaration) => declaration.init === responseBodyNode)?.id : `${responseBodyNode.object.name}Body`;
|
|
48
|
+
const change = {
|
|
49
|
+
enclosingFunction,
|
|
50
|
+
enclosingStatement,
|
|
51
|
+
enclosingStatementIndex,
|
|
52
|
+
responseVariableName,
|
|
53
|
+
responseBodyNode,
|
|
54
|
+
responseBodyVariableName,
|
|
55
|
+
isResponseBodyVariableDeclared
|
|
56
|
+
};
|
|
57
|
+
const changesByFunction = allChanges.get(enclosingFunction) ?? /* @__PURE__ */ new Map();
|
|
58
|
+
const changesByResponse = changesByFunction.get(responseVariableName) ?? [];
|
|
59
|
+
changesByResponse.push(change);
|
|
60
|
+
changesByFunction.set(responseVariableName, changesByResponse);
|
|
61
|
+
allChanges.set(enclosingFunction, changesByFunction);
|
|
42
62
|
}
|
|
43
63
|
} catch (error) {
|
|
44
64
|
console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
|
|
45
65
|
context.report({
|
|
46
|
-
node:
|
|
66
|
+
node: responseBodyNode,
|
|
47
67
|
messageId: "unknownError",
|
|
48
68
|
data: {
|
|
49
69
|
fileName: context.filename,
|
|
@@ -51,6 +71,57 @@ var rule = createRule({
|
|
|
51
71
|
}
|
|
52
72
|
});
|
|
53
73
|
}
|
|
74
|
+
},
|
|
75
|
+
"Program:exit": () => {
|
|
76
|
+
if (allChanges.size === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const fixes = [];
|
|
80
|
+
for (const changesByFunction of allChanges.values()) {
|
|
81
|
+
for (const changesByResponse of changesByFunction.values()) {
|
|
82
|
+
const orderedChanges = changesByResponse.sort(
|
|
83
|
+
(changeA, changeB) => changeA.enclosingStatementIndex - changeB.enclosingStatementIndex
|
|
84
|
+
);
|
|
85
|
+
const firstChange = orderedChanges[0];
|
|
86
|
+
assert(firstChange);
|
|
87
|
+
const {
|
|
88
|
+
responseBodyNode,
|
|
89
|
+
responseVariableName,
|
|
90
|
+
responseBodyVariableName,
|
|
91
|
+
isResponseBodyVariableDeclared,
|
|
92
|
+
enclosingStatement
|
|
93
|
+
} = firstChange;
|
|
94
|
+
let remainingChanges;
|
|
95
|
+
if (!isResponseBodyVariableDeclared) {
|
|
96
|
+
fixes.push({
|
|
97
|
+
node: enclosingStatement,
|
|
98
|
+
text: `const ${responseBodyVariableName} = await ${responseVariableName}.json();
|
|
99
|
+
`,
|
|
100
|
+
insert: true
|
|
101
|
+
});
|
|
102
|
+
remainingChanges = orderedChanges;
|
|
103
|
+
} else {
|
|
104
|
+
fixes.push({
|
|
105
|
+
node: responseBodyNode,
|
|
106
|
+
text: `await ${responseVariableName}.json()`,
|
|
107
|
+
insert: false
|
|
108
|
+
});
|
|
109
|
+
remainingChanges = orderedChanges.slice(1);
|
|
110
|
+
}
|
|
111
|
+
for (const change of remainingChanges) {
|
|
112
|
+
fixes.push({ node: change.responseBodyNode, text: responseBodyVariableName, insert: false });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
for (const fix of fixes) {
|
|
117
|
+
context.report({
|
|
118
|
+
node: fix.node,
|
|
119
|
+
messageId: "replaceBodyWithJson",
|
|
120
|
+
fix(fixer) {
|
|
121
|
+
return fix.insert ? fixer.insertTextBefore(fix.node, fix.text) : fixer.replaceText(fix.node, fix.text);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
54
125
|
}
|
|
55
126
|
};
|
|
56
127
|
}
|
|
@@ -60,4 +131,4 @@ export {
|
|
|
60
131
|
fetch_response_body_json_default as default,
|
|
61
132
|
ruleId
|
|
62
133
|
};
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
134
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL2FnZW50L2ZldGNoLXJlc3BvbnNlLWJvZHktanNvbi50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFRQSxTQUFTLFVBQVUsY0FBYztBQUVqQyxTQUFTLGdCQUFnQixtQkFBNkI7QUFFdEQsT0FBTyx5QkFBeUI7QUFDaEMsU0FBUyxtQkFBbUI7QUFFckIsSUFBTSxTQUFTO0FBRXRCLElBQU0sYUFBYSxZQUFZLFlBQVksQ0FBQyxTQUFTLG9CQUFvQixJQUFJLENBQUM7QUFhOUUsSUFBTSxPQUF1RSxXQUFXO0FBQUEsRUFDdEYsTUFBTTtBQUFBLEVBQ04sTUFBTTtBQUFBLElBQ0osTUFBTTtBQUFBLElBQ04sTUFBTTtBQUFBLE1BQ0osYUFBYTtBQUFBLElBQ2Y7QUFBQSxJQUNBLFVBQVU7QUFBQSxNQUNSLHFCQUFxQjtBQUFBLE1BQ3JCLGNBQWM7QUFBQSxJQUNoQjtBQUFBLElBQ0EsU0FBUztBQUFBLElBQ1QsUUFBUSxDQUFDO0FBQUEsRUFDWDtBQUFBLEVBQ0EsZ0JBQWdCLENBQUM7QUFBQSxFQUNqQixPQUFPLFNBQVM7QUFDZCxVQUFNLGlCQUFpQixZQUFZLGtCQUFrQixPQUFPO0FBQzVELFVBQU0sY0FBYyxlQUFlLFFBQVEsZUFBZTtBQUMxRCxVQUFNLGFBQWEsb0JBQUksSUFBMEM7QUFFakUsV0FBTztBQUFBLE1BQ0wsMENBQTBDLENBQUMscUJBQWdEO0FBQ3pGLFlBQUk7QUFDRixnQkFBTSxlQUFlLGVBQWUsc0JBQXNCLElBQUksaUJBQWlCLE1BQU07QUFDckYsZ0JBQU0sZUFBZSxZQUFZLGtCQUFrQixZQUFZO0FBRS9ELGdCQUFNLGdCQUNKLGFBQWEsY0FBYyxFQUFFLEtBQUssQ0FBQyxXQUFXLE9BQU8sU0FBUyxNQUFNLEtBQ3BFLGFBQWEsY0FBYyxFQUFFLEtBQUssQ0FBQyxXQUFXLE9BQU8sU0FBUyxNQUFNO0FBRXRFLGNBQUksZUFBZTtBQUNqQixrQkFBTSxvQkFBb0I7QUFBQSxjQUN4QjtBQUFBLGNBQ0EsQ0FBQyxT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
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@checkdigit/eslint-plugin","version":"7.3.0-PR.75-
|
|
1
|
+
{"name":"@checkdigit/eslint-plugin","version":"7.3.0-PR.75-4919","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.13.0","@typescript-eslint/utils":"8.13.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.14.0","@types/debug":"^4.1.12","@types/eslint":"^9.6.1","@types/eslint-config-prettier":"^6.11.3","@typescript-eslint/parser":"8.13.0","@typescript-eslint/rule-tester":"8.13.0","eslint":"^9.14.0","eslint-config-prettier":"^9.1.0","eslint-import-resolver-typescript":"^3.6.3","eslint-plugin-eslint-plugin":"^6.3.1","eslint-plugin-import":"^2.31.0","eslint-plugin-no-only-tests":"^3.3.0","eslint-plugin-no-secrets":"^1.0.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.13.0"},"peerDependencies":{"eslint":">=9 <10"},"engines":{"node":">=20.17"}}
|
|
@@ -6,14 +6,28 @@
|
|
|
6
6
|
* This code is licensed under the MIT license (see LICENSE.txt for details).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { strict as assert } from 'node:assert';
|
|
10
|
+
|
|
9
11
|
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils';
|
|
10
12
|
|
|
11
13
|
import getDocumentationUrl from '../get-documentation-url';
|
|
14
|
+
import { getAncestor } from '../library/ts-tree';
|
|
12
15
|
|
|
13
16
|
export const ruleId = 'fetch-response-body-json';
|
|
14
17
|
|
|
15
18
|
const createRule = ESLintUtils.RuleCreator((name) => getDocumentationUrl(name));
|
|
16
19
|
|
|
20
|
+
interface Change {
|
|
21
|
+
enclosingFunction: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration;
|
|
22
|
+
enclosingStatement: TSESTree.VariableDeclaration | TSESTree.ExpressionStatement | TSESTree.ReturnStatement;
|
|
23
|
+
enclosingStatementIndex: number;
|
|
24
|
+
responseBodyNode: TSESTree.MemberExpression;
|
|
25
|
+
responseVariableName: string;
|
|
26
|
+
responseBodyVariableName: string;
|
|
27
|
+
isResponseBodyVariableDeclared: boolean;
|
|
28
|
+
// replacementText: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
const rule: ESLintUtils.RuleModule<'unknownError' | 'replaceBodyWithJson'> = createRule({
|
|
18
32
|
name: ruleId,
|
|
19
33
|
meta: {
|
|
@@ -32,12 +46,12 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'replaceBodyWithJson'> = cre
|
|
|
32
46
|
create(context) {
|
|
33
47
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
34
48
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
35
|
-
const
|
|
49
|
+
const allChanges = new Map<TSESTree.Node, Map<string, Change[]>>();
|
|
36
50
|
|
|
37
51
|
return {
|
|
38
|
-
'MemberExpression[property.name="body"]': (
|
|
52
|
+
'MemberExpression[property.name="body"]': (responseBodyNode: TSESTree.MemberExpression) => {
|
|
39
53
|
try {
|
|
40
|
-
const responseNode = parserServices.esTreeNodeToTSNodeMap.get(
|
|
54
|
+
const responseNode = parserServices.esTreeNodeToTSNodeMap.get(responseBodyNode.object);
|
|
41
55
|
const responseType = typeChecker.getTypeAtLocation(responseNode);
|
|
42
56
|
|
|
43
57
|
const shouldReplace =
|
|
@@ -45,23 +59,54 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'replaceBodyWithJson'> = cre
|
|
|
45
59
|
responseType.getProperties().some((symbol) => symbol.name === 'json');
|
|
46
60
|
|
|
47
61
|
if (shouldReplace) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
const enclosingFunction = getAncestor(
|
|
63
|
+
responseBodyNode,
|
|
64
|
+
(node: TSESTree.Node) =>
|
|
65
|
+
node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
66
|
+
node.type === AST_NODE_TYPES.FunctionExpression ||
|
|
67
|
+
node.type === AST_NODE_TYPES.FunctionDeclaration,
|
|
68
|
+
) as TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration;
|
|
69
|
+
const enclosingStatement = getAncestor(
|
|
70
|
+
responseBodyNode,
|
|
71
|
+
(node: TSESTree.Node) =>
|
|
72
|
+
(node.type === AST_NODE_TYPES.VariableDeclaration ||
|
|
73
|
+
node.type === AST_NODE_TYPES.ExpressionStatement ||
|
|
74
|
+
node.type === AST_NODE_TYPES.ReturnStatement) &&
|
|
75
|
+
node.parent.type === AST_NODE_TYPES.BlockStatement,
|
|
76
|
+
) as TSESTree.VariableDeclaration | TSESTree.ExpressionStatement | TSESTree.ReturnStatement;
|
|
77
|
+
const enclosingStatementIndex = (enclosingFunction.body as TSESTree.BlockStatement).body.indexOf(
|
|
78
|
+
enclosingStatement,
|
|
79
|
+
);
|
|
80
|
+
const responseVariableName = (responseBodyNode.object as TSESTree.Identifier).name;
|
|
81
|
+
const isResponseBodyVariableDeclared =
|
|
82
|
+
enclosingStatement.type === AST_NODE_TYPES.VariableDeclaration &&
|
|
83
|
+
enclosingStatement.declarations.some((declaration) => declaration.init === responseBodyNode);
|
|
84
|
+
const responseBodyVariableName = isResponseBodyVariableDeclared
|
|
85
|
+
? (enclosingStatement.declarations.find((declaration) => declaration.init === responseBodyNode)
|
|
86
|
+
?.id as unknown as string)
|
|
87
|
+
: `${(responseBodyNode.object as TSESTree.Identifier).name}Body`;
|
|
88
|
+
|
|
89
|
+
const change: Change = {
|
|
90
|
+
enclosingFunction,
|
|
91
|
+
enclosingStatement,
|
|
92
|
+
enclosingStatementIndex,
|
|
93
|
+
responseVariableName,
|
|
94
|
+
responseBodyNode,
|
|
95
|
+
responseBodyVariableName,
|
|
96
|
+
isResponseBodyVariableDeclared,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const changesByFunction = allChanges.get(enclosingFunction) ?? new Map<string, Change[]>();
|
|
100
|
+
const changesByResponse = changesByFunction.get(responseVariableName) ?? [];
|
|
101
|
+
changesByResponse.push(change);
|
|
102
|
+
changesByFunction.set(responseVariableName, changesByResponse);
|
|
103
|
+
allChanges.set(enclosingFunction, changesByFunction);
|
|
59
104
|
}
|
|
60
105
|
} catch (error) {
|
|
61
106
|
// eslint-disable-next-line no-console
|
|
62
107
|
console.error(`Failed to apply ${ruleId} rule for file "${context.filename}":`, error);
|
|
63
108
|
context.report({
|
|
64
|
-
node:
|
|
109
|
+
node: responseBodyNode,
|
|
65
110
|
messageId: 'unknownError',
|
|
66
111
|
data: {
|
|
67
112
|
fileName: context.filename,
|
|
@@ -70,6 +115,62 @@ const rule: ESLintUtils.RuleModule<'unknownError' | 'replaceBodyWithJson'> = cre
|
|
|
70
115
|
});
|
|
71
116
|
}
|
|
72
117
|
},
|
|
118
|
+
|
|
119
|
+
'Program:exit': () => {
|
|
120
|
+
if (allChanges.size === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const fixes: { node: TSESTree.Node; text: string; insert: boolean }[] = [];
|
|
125
|
+
for (const changesByFunction of allChanges.values()) {
|
|
126
|
+
for (const changesByResponse of changesByFunction.values()) {
|
|
127
|
+
const orderedChanges = changesByResponse.sort(
|
|
128
|
+
(changeA, changeB) => changeA.enclosingStatementIndex - changeB.enclosingStatementIndex,
|
|
129
|
+
);
|
|
130
|
+
const firstChange = orderedChanges[0];
|
|
131
|
+
assert(firstChange);
|
|
132
|
+
|
|
133
|
+
const {
|
|
134
|
+
responseBodyNode,
|
|
135
|
+
responseVariableName,
|
|
136
|
+
responseBodyVariableName,
|
|
137
|
+
isResponseBodyVariableDeclared,
|
|
138
|
+
enclosingStatement,
|
|
139
|
+
} = firstChange;
|
|
140
|
+
|
|
141
|
+
let remainingChanges;
|
|
142
|
+
if (!isResponseBodyVariableDeclared) {
|
|
143
|
+
fixes.push({
|
|
144
|
+
node: enclosingStatement,
|
|
145
|
+
text: `const ${responseBodyVariableName} = await ${responseVariableName}.json();\n`,
|
|
146
|
+
insert: true,
|
|
147
|
+
});
|
|
148
|
+
remainingChanges = orderedChanges;
|
|
149
|
+
} else {
|
|
150
|
+
fixes.push({
|
|
151
|
+
node: responseBodyNode,
|
|
152
|
+
text: `await ${responseVariableName}.json()`,
|
|
153
|
+
insert: false,
|
|
154
|
+
});
|
|
155
|
+
remainingChanges = orderedChanges.slice(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (const change of remainingChanges) {
|
|
159
|
+
fixes.push({ node: change.responseBodyNode, text: responseBodyVariableName, insert: false });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const fix of fixes) {
|
|
165
|
+
context.report({
|
|
166
|
+
node: fix.node,
|
|
167
|
+
messageId: 'replaceBodyWithJson',
|
|
168
|
+
fix(fixer) {
|
|
169
|
+
return fix.insert ? fixer.insertTextBefore(fix.node, fix.text) : fixer.replaceText(fix.node, fix.text);
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
},
|
|
73
174
|
};
|
|
74
175
|
},
|
|
75
176
|
});
|