eslint-plugin-no-mistakes 0.11.1 → 0.12.0
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/package.json
CHANGED
package/src/helpers.js
CHANGED
|
@@ -134,14 +134,28 @@ function selectorValueNode(attribute) {
|
|
|
134
134
|
return expression && expression.type !== "JSXEmptyExpression" ? expression : null;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const LOCAL_BINDING_TYPES = new Set([
|
|
137
|
+
const LOCAL_BINDING_TYPES = new Set([
|
|
138
|
+
"Variable",
|
|
139
|
+
"Parameter",
|
|
140
|
+
"CatchClause",
|
|
141
|
+
"FunctionName",
|
|
142
|
+
"ImportBinding",
|
|
143
|
+
"ClassName",
|
|
144
|
+
]);
|
|
138
145
|
|
|
139
146
|
function isFetchShadowed(scope) {
|
|
140
147
|
while (scope) {
|
|
141
|
-
const
|
|
148
|
+
const get = scope.set?.get;
|
|
149
|
+
const variable = typeof get === "function" ? get.call(scope.set, "fetch") : null;
|
|
142
150
|
if (variable) {
|
|
143
151
|
return variable.defs.some((def) => LOCAL_BINDING_TYPES.has(def.type));
|
|
144
152
|
}
|
|
153
|
+
if (!scope.set || typeof get !== "function") {
|
|
154
|
+
const fallback = scope.variables?.find((item) => item.name === "fetch");
|
|
155
|
+
if (fallback) {
|
|
156
|
+
return fallback.defs.some((def) => LOCAL_BINDING_TYPES.has(def.type));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
145
159
|
scope = scope.upper;
|
|
146
160
|
}
|
|
147
161
|
return false;
|
package/src/returned-jsx.js
CHANGED
|
@@ -17,7 +17,9 @@ function collectReturnBranches(node, branches) {
|
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
if (node.type === "ReturnStatement") {
|
|
20
|
-
|
|
20
|
+
for (const branch of jsxBranches(node.argument)) {
|
|
21
|
+
branches.push(branch);
|
|
22
|
+
}
|
|
21
23
|
return;
|
|
22
24
|
}
|
|
23
25
|
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
|
|
@@ -101,7 +103,11 @@ function childNodes(node) {
|
|
|
101
103
|
continue;
|
|
102
104
|
}
|
|
103
105
|
if (Array.isArray(value)) {
|
|
104
|
-
|
|
106
|
+
for (const child of value) {
|
|
107
|
+
if (isAstNode(child)) {
|
|
108
|
+
children.push(child);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
105
111
|
} else if (isAstNode(value)) {
|
|
106
112
|
children.push(value);
|
|
107
113
|
}
|
|
@@ -25,7 +25,11 @@ function propertyName(node, computed = false) {
|
|
|
25
25
|
function isGlobalSetTimeout(node, context) {
|
|
26
26
|
let scope = context.sourceCode.getScope(node);
|
|
27
27
|
while (scope) {
|
|
28
|
-
const
|
|
28
|
+
const get = scope.set?.get;
|
|
29
|
+
const variable =
|
|
30
|
+
typeof get === "function"
|
|
31
|
+
? get.call(scope.set, "setTimeout")
|
|
32
|
+
: scope.variables.find((candidate) => candidate.name === "setTimeout");
|
|
29
33
|
if (variable) return variable.defs.length === 0;
|
|
30
34
|
scope = scope.upper;
|
|
31
35
|
}
|
|
@@ -23,17 +23,24 @@ function isInsideUncalledNestedFunction(node, testDepth, setupDepth) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function isModuleMutable(context, mutableTopLevel, node, name) {
|
|
26
|
+
function isModuleMutable({ context, mutableTopLevel, node, name }) {
|
|
27
27
|
let scope = context.sourceCode.getScope(node);
|
|
28
28
|
while (scope) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const get = scope.set?.get;
|
|
30
|
+
const variable = typeof get === "function" ? get.call(scope.set, name) : null;
|
|
31
|
+
const resolvedVariable =
|
|
32
|
+
variable ||
|
|
33
|
+
(typeof get !== "function" ? scope.variables?.find((item) => item.name === name) : null);
|
|
34
|
+
|
|
35
|
+
if (!resolvedVariable) {
|
|
36
|
+
scope = scope.upper;
|
|
37
|
+
continue;
|
|
35
38
|
}
|
|
36
|
-
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
mutableTopLevel.has(resolvedVariable.name) &&
|
|
42
|
+
(resolvedVariable.scope.type === "module" || resolvedVariable.scope.block.type === "Program")
|
|
43
|
+
);
|
|
37
44
|
}
|
|
38
45
|
return false;
|
|
39
46
|
}
|
|
@@ -112,7 +119,7 @@ function createRegistryReports(context, mutableTopLevel, cleanupTracker, isCaptu
|
|
|
112
119
|
testDepth > 0 &&
|
|
113
120
|
setupDepth === 0 &&
|
|
114
121
|
!isCaptured(name) &&
|
|
115
|
-
isModuleMutable(context, mutableTopLevel, node, name)
|
|
122
|
+
isModuleMutable({ context, mutableTopLevel, node, name })
|
|
116
123
|
) {
|
|
117
124
|
pending.push({ node, path, suiteKey: cleanupTracker.currentSuiteKey() });
|
|
118
125
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const { rule } = require("../helpers");
|
|
4
|
+
|
|
4
5
|
const {
|
|
5
6
|
createRegistryReports,
|
|
6
7
|
createViMockTracker,
|
|
@@ -9,6 +10,7 @@ const {
|
|
|
9
10
|
isResetAssignment,
|
|
10
11
|
walkSharedMutations,
|
|
11
12
|
} = require("./test-no-shared-state-analysis");
|
|
13
|
+
|
|
12
14
|
const {
|
|
13
15
|
calleeName,
|
|
14
16
|
collectPatternNames,
|
|
@@ -56,10 +58,9 @@ module.exports = rule(
|
|
|
56
58
|
testDepth > 0 &&
|
|
57
59
|
setupDepth === 0 &&
|
|
58
60
|
!viMockTracker.isCaptured(name) &&
|
|
59
|
-
isModuleMutable(context, mutableTopLevel, node, name)
|
|
60
|
-
)
|
|
61
|
+
isModuleMutable({ context, mutableTopLevel, node, name })
|
|
62
|
+
)
|
|
61
63
|
context.report({ node, messageId: "shared" });
|
|
62
|
-
}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
function reportAssignment(node) {
|
|
@@ -73,10 +74,9 @@ module.exports = rule(
|
|
|
73
74
|
name &&
|
|
74
75
|
setupDepth > 0 &&
|
|
75
76
|
mutableTopLevel.has(name) &&
|
|
76
|
-
isModuleMutable(context, mutableTopLevel, node, name)
|
|
77
|
-
)
|
|
77
|
+
isModuleMutable({ context, mutableTopLevel, node, name })
|
|
78
|
+
)
|
|
78
79
|
cleanupTracker.remember(path);
|
|
79
|
-
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function rememberCall(node) {
|
|
@@ -97,16 +97,36 @@ module.exports = rule(
|
|
|
97
97
|
rememberSetupCleanup(node, mutationRootName(node.left), mutationPath(node.left.object));
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
const mutationWalk = {
|
|
101
|
+
onAssignment: (assignment) => {
|
|
102
|
+
rememberAssignmentCleanup(assignment);
|
|
103
|
+
reportAssignment(assignment);
|
|
104
|
+
},
|
|
105
|
+
onCall: rememberCall,
|
|
106
|
+
onUpdate: (update) => reportIfShared(update, mutationRootName(update.argument)),
|
|
107
|
+
};
|
|
108
|
+
|
|
100
109
|
function resolveFunctionCallback(node, callback) {
|
|
101
110
|
let scope = context.sourceCode.getScope(node);
|
|
102
111
|
while (scope) {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
112
|
+
const get = scope.set?.get;
|
|
113
|
+
const resolvedVariable =
|
|
114
|
+
(typeof get === "function" ? get.call(scope.set, callback.name) : null) ||
|
|
115
|
+
(typeof get !== "function"
|
|
116
|
+
? scope.variables?.find((item) => item.name === callback.name)
|
|
117
|
+
: null);
|
|
118
|
+
|
|
119
|
+
if (!resolvedVariable) {
|
|
120
|
+
scope = scope.upper;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const declaration = resolvedVariable.defs[0]?.node;
|
|
105
125
|
if (declaration?.type === "FunctionDeclaration") return declaration;
|
|
106
126
|
if (declaration?.type === "VariableDeclarator" && isFunctionNode(declaration.init)) {
|
|
107
127
|
return declaration.init;
|
|
108
128
|
}
|
|
109
|
-
|
|
129
|
+
return null;
|
|
110
130
|
}
|
|
111
131
|
return null;
|
|
112
132
|
}
|
|
@@ -127,7 +147,7 @@ module.exports = rule(
|
|
|
127
147
|
const previousSetupDepth = setupDepth;
|
|
128
148
|
setupDepth = 1;
|
|
129
149
|
cleanupTracker.beginSetup(kind, suiteKey);
|
|
130
|
-
|
|
150
|
+
walkSharedMutations(declaration.body, mutationWalk);
|
|
131
151
|
setupDepth = previousSetupDepth;
|
|
132
152
|
cleanupTracker.endSetup();
|
|
133
153
|
}
|
|
@@ -135,7 +155,7 @@ module.exports = rule(
|
|
|
135
155
|
for (const { declaration, suiteKey } of pendingNamedCallbacks) {
|
|
136
156
|
if (!declaration) continue;
|
|
137
157
|
cleanupTracker.setReplaySuite(suiteKey);
|
|
138
|
-
|
|
158
|
+
walkSharedMutations(declaration.body, mutationWalk);
|
|
139
159
|
cleanupTracker.clearReplaySuite();
|
|
140
160
|
}
|
|
141
161
|
registryReports.flush();
|
|
@@ -154,15 +174,13 @@ module.exports = rule(
|
|
|
154
174
|
}
|
|
155
175
|
}
|
|
156
176
|
const setupKind = setupCallbackKind(node);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
});
|
|
165
|
-
}
|
|
177
|
+
const setupCallback = setupKind && firstNamedCallbackArgument(node.arguments);
|
|
178
|
+
if (setupCallback) {
|
|
179
|
+
pendingNamedSetupCallbacks.push({
|
|
180
|
+
declaration: resolveFunctionCallback(node, setupCallback),
|
|
181
|
+
suiteKey: cleanupTracker.currentSuiteKey(),
|
|
182
|
+
kind: setupKind,
|
|
183
|
+
});
|
|
166
184
|
}
|
|
167
185
|
if (setupKind) {
|
|
168
186
|
setupDepth += 1;
|
|
@@ -185,24 +203,9 @@ module.exports = rule(
|
|
|
185
203
|
cleanupTracker.endSetup();
|
|
186
204
|
}
|
|
187
205
|
const isInsideNested = isInsideUncalledNestedFunction(node, testDepth, setupDepth);
|
|
188
|
-
if (!isInsideNested)
|
|
189
|
-
rememberCall(node);
|
|
190
|
-
}
|
|
206
|
+
if (!isInsideNested) rememberCall(node);
|
|
191
207
|
if (calleeName(node.callee) === "describe") cleanupTracker.exitSuite();
|
|
192
208
|
},
|
|
193
209
|
};
|
|
194
|
-
|
|
195
|
-
function checkSharedMutations(node) {
|
|
196
|
-
walkSharedMutations(node, {
|
|
197
|
-
onAssignment: (assignment) => {
|
|
198
|
-
rememberAssignmentCleanup(assignment);
|
|
199
|
-
reportAssignment(assignment);
|
|
200
|
-
},
|
|
201
|
-
onCall: (call) => {
|
|
202
|
-
rememberCall(call);
|
|
203
|
-
},
|
|
204
|
-
onUpdate: (update) => reportIfShared(update, mutationRootName(update.argument)),
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
210
|
},
|
|
208
211
|
);
|