eslint-plugin-effector 0.4.2 → 0.6.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/README.md +25 -6
- package/config/react.js +5 -0
- package/config/recommended.js +2 -0
- package/config/scope.js +5 -0
- package/index.js +7 -0
- package/package.json +4 -1
- package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.js +16 -17
- package/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.js +114 -0
- package/rules/enforce-store-naming-convention/enforce-store-naming-convention.js +25 -25
- package/rules/keep-options-order/config.js +3 -0
- package/rules/keep-options-order/keep-options-order.js +107 -0
- package/rules/no-ambiguity-target/no-ambiguity-target.js +38 -39
- package/rules/no-duplicate-on/no-duplicate-on.js +137 -0
- package/rules/no-forward/no-forward.js +77 -0
- package/rules/no-getState/no-getState.js +9 -36
- package/rules/no-unnecessary-combination/no-unnecessary-combination.js +39 -45
- package/rules/no-unnecessary-duplication/no-unnecessary-duplication.js +41 -45
- package/rules/no-useless-methods/no-useless-methods.js +56 -57
- package/rules/no-watch/no-watch.js +6 -12
- package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.js +31 -13
- package/rules/strict-effect-handlers/strict-effect-handlers.js +76 -0
- package/utils/builders.js +19 -0
- package/utils/extract-imported-from.js +10 -0
- package/utils/get-corrected-store-name.js +12 -14
- package/utils/get-nested-object-name.js +18 -0
- package/utils/get-store-name-convention.js +3 -3
- package/utils/is.js +30 -0
- package/utils/method.js +23 -0
- package/utils/naming.js +47 -0
- package/utils/node-type-is.js +59 -0
- package/utils/replace-by-sample.js +66 -0
- package/utils/validate-store-name-convention.js +7 -7
- package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.md +0 -11
- package/rules/enforce-store-naming-convention/enforce-store-naming-convention.md +0 -44
- package/rules/no-ambiguity-target/no-ambiguity-target.md +0 -12
- package/rules/no-getState/no-getState.md +0 -20
- package/rules/no-unnecessary-combination/no-unnecessary-combination.md +0 -14
- package/rules/no-unnecessary-duplication/no-unnecessary-duplication.md +0 -32
- package/rules/no-useless-methods/no-useless-methods.md +0 -14
- package/rules/no-watch/no-watch.md +0 -42
- package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.md +0 -27
- package/rules/tsconfig.json +0 -9
- package/utils/extract-imported-from-effector.js +0 -8
- package/utils/is-store-name-valid.js +0 -22
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
2
|
+
const { getNestedObjectName } = require("../../utils/get-nested-object-name");
|
|
3
|
+
const {
|
|
4
|
+
traverseNestedObjectNode,
|
|
5
|
+
} = require("../../utils/traverse-nested-object-node");
|
|
6
|
+
const { is } = require("../../utils/is");
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
meta: {
|
|
10
|
+
type: "problem",
|
|
11
|
+
docs: {
|
|
12
|
+
description: "Forbids duplicate `.on` calls on store",
|
|
13
|
+
category: "Quality",
|
|
14
|
+
recommended: true,
|
|
15
|
+
url: createLinkToRule("no-duplicate-on"),
|
|
16
|
+
},
|
|
17
|
+
messages: {
|
|
18
|
+
duplicateOn:
|
|
19
|
+
"Method `.on` is called on store `{{ storeName }}` more than once for {{ unitName }}.",
|
|
20
|
+
},
|
|
21
|
+
schema: [],
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
const usedOns = new Map();
|
|
25
|
+
|
|
26
|
+
function isEventUsedInStoreOn(scope, storeName, unitName) {
|
|
27
|
+
const usedOnsOnScope = usedOns.get(scope);
|
|
28
|
+
|
|
29
|
+
if (!usedOnsOnScope) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const usedUnits = usedOnsOnScope.get(storeName);
|
|
34
|
+
|
|
35
|
+
if (!usedUnits) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return usedUnits.has(unitName);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function markUnitAsUsedInStoreOn(scope, storeName, unitNames) {
|
|
43
|
+
let usedOnsOnScope = usedOns.get(scope);
|
|
44
|
+
|
|
45
|
+
if (!usedOnsOnScope) {
|
|
46
|
+
usedOnsOnScope = new Map();
|
|
47
|
+
usedOns.set(scope, usedOnsOnScope);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let usedUnits = usedOnsOnScope.get(storeName);
|
|
51
|
+
|
|
52
|
+
if (!usedUnits) {
|
|
53
|
+
usedUnits = new Set();
|
|
54
|
+
usedOnsOnScope.set(storeName, usedUnits);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
usedUnits.add(...unitNames);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
'CallExpression[callee.property.name="on"]'(node) {
|
|
62
|
+
const storeObject = traverseNestedObjectNode(
|
|
63
|
+
getNestedCallee(node) ?? getAssignedVariable(node)
|
|
64
|
+
);
|
|
65
|
+
const storeName = getStoreName(storeObject);
|
|
66
|
+
|
|
67
|
+
if (!is.store({ context, node: storeObject })) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const triggerObjects = normalizePossibleArrayNode(node.arguments[0]);
|
|
72
|
+
const unitNames = triggerObjects.map(getNestedObjectName);
|
|
73
|
+
|
|
74
|
+
const scope = context.getScope();
|
|
75
|
+
|
|
76
|
+
for (const unitName of unitNames) {
|
|
77
|
+
const unitAlreadyUsed = isEventUsedInStoreOn(
|
|
78
|
+
scope,
|
|
79
|
+
storeName,
|
|
80
|
+
unitName
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (unitAlreadyUsed) {
|
|
84
|
+
context.report({
|
|
85
|
+
node,
|
|
86
|
+
messageId: "duplicateOn",
|
|
87
|
+
data: {
|
|
88
|
+
storeName,
|
|
89
|
+
unitName,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
markUnitAsUsedInStoreOn(scope, storeName, unitNames);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function normalizePossibleArrayNode(node) {
|
|
104
|
+
if (!node) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (node.type === "ArrayExpression") {
|
|
109
|
+
return node.elements;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return [node];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function getNestedCallee(node) {
|
|
116
|
+
const { callee } = node;
|
|
117
|
+
|
|
118
|
+
if (callee.object?.type === "CallExpression") {
|
|
119
|
+
return getNestedCallee(callee.object);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return callee.object;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getAssignedVariable(node) {
|
|
126
|
+
const { parent } = node;
|
|
127
|
+
|
|
128
|
+
if (parent.type === "VariableDeclarator") {
|
|
129
|
+
return parent;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return getAssignedVariable(parent);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getStoreName(node) {
|
|
136
|
+
return node?.name ?? node?.id?.name;
|
|
137
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
2
|
+
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
3
|
+
const { method } = require("../../utils/method");
|
|
4
|
+
const { replaceForwardBySample } = require("../../utils/replace-by-sample");
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Prefer `sample` over `forward`",
|
|
11
|
+
category: "Quality",
|
|
12
|
+
recommended: true,
|
|
13
|
+
url: createLinkToRule("no-forward"),
|
|
14
|
+
},
|
|
15
|
+
messages: {
|
|
16
|
+
noForward:
|
|
17
|
+
"Instead of `forward` you can use `sample`, it is more extendable.",
|
|
18
|
+
replaceWithSample: "Repalce `forward` with `sample`.",
|
|
19
|
+
},
|
|
20
|
+
schema: [],
|
|
21
|
+
hasSuggestions: true,
|
|
22
|
+
},
|
|
23
|
+
create(context) {
|
|
24
|
+
const importNodes = new Map();
|
|
25
|
+
const importedFromEffector = new Map();
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
ImportDeclaration(node) {
|
|
29
|
+
extractImportedFrom({
|
|
30
|
+
importMap: importedFromEffector,
|
|
31
|
+
nodeMap: importNodes,
|
|
32
|
+
node,
|
|
33
|
+
packageName: "effector",
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
CallExpression(node) {
|
|
37
|
+
if (
|
|
38
|
+
method.isNot("forward", {
|
|
39
|
+
node,
|
|
40
|
+
importMap: importedFromEffector,
|
|
41
|
+
})
|
|
42
|
+
) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const forwardConfig = {
|
|
47
|
+
from: node.arguments?.[0]?.properties.find(
|
|
48
|
+
(n) => n.key?.name === "from"
|
|
49
|
+
),
|
|
50
|
+
to: node.arguments?.[0]?.properties.find((n) => n.key?.name === "to"),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (!forwardConfig.from || !forwardConfig.to) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
context.report({
|
|
58
|
+
messageId: "noForward",
|
|
59
|
+
node,
|
|
60
|
+
suggest: [
|
|
61
|
+
{
|
|
62
|
+
messageId: "replaceWithSample",
|
|
63
|
+
*fix(fixer) {
|
|
64
|
+
yield* replaceForwardBySample(forwardConfig, {
|
|
65
|
+
fixer,
|
|
66
|
+
node,
|
|
67
|
+
context,
|
|
68
|
+
importNodes,
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const {
|
|
2
2
|
traverseNestedObjectNode,
|
|
3
3
|
} = require("../../utils/traverse-nested-object-node");
|
|
4
|
-
const { isStoreNameValid } = require("../../utils/is-store-name-valid");
|
|
5
4
|
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
5
|
+
const { is } = require("../../utils/is");
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
meta: {
|
|
@@ -20,47 +20,20 @@ module.exports = {
|
|
|
20
20
|
schema: [],
|
|
21
21
|
},
|
|
22
22
|
create(context) {
|
|
23
|
-
const { parserServices } = context;
|
|
24
|
-
|
|
25
23
|
return {
|
|
26
|
-
CallExpression(node) {
|
|
27
|
-
const
|
|
28
|
-
if (methodName !== "getState") {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
24
|
+
'CallExpression[callee.property.name="getState"]'(node) {
|
|
25
|
+
const storeNode = traverseNestedObjectNode(node.callee?.object);
|
|
31
26
|
|
|
32
|
-
const
|
|
33
|
-
|
|
27
|
+
const isEffectorStore = is.store({
|
|
28
|
+
context,
|
|
29
|
+
node: storeNode,
|
|
30
|
+
});
|
|
34
31
|
|
|
35
|
-
if (!
|
|
32
|
+
if (!isEffectorStore) {
|
|
36
33
|
return;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
if (parserServices.hasFullTypeInformation) {
|
|
41
|
-
const checker = parserServices.program.getTypeChecker();
|
|
42
|
-
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(object);
|
|
43
|
-
const type = checker.getTypeAtLocation(originalNode);
|
|
44
|
-
|
|
45
|
-
const isEffectorStore =
|
|
46
|
-
type?.symbol?.escapedName === "Store" &&
|
|
47
|
-
type?.symbol?.parent?.escapedName?.includes("effector");
|
|
48
|
-
|
|
49
|
-
if (!isEffectorStore) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
reportGetStateCall({ context, node, storeName: objectName });
|
|
54
|
-
}
|
|
55
|
-
// JavaScript-way
|
|
56
|
-
else {
|
|
57
|
-
const isEffectorStore = isStoreNameValid(objectName, context);
|
|
58
|
-
if (!isEffectorStore) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
reportGetStateCall({ context, node, storeName: objectName });
|
|
63
|
-
}
|
|
36
|
+
reportGetStateCall({ context, node, storeName: storeNode.name });
|
|
64
37
|
},
|
|
65
38
|
};
|
|
66
39
|
},
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
const {
|
|
2
|
-
extractImportedFromEffector,
|
|
3
|
-
} = require("../../utils/extract-imported-from-effector");
|
|
1
|
+
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
4
2
|
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
3
|
+
const { method } = require("../../utils/method");
|
|
5
4
|
|
|
6
5
|
module.exports = {
|
|
7
6
|
meta: {
|
|
@@ -24,19 +23,17 @@ module.exports = {
|
|
|
24
23
|
|
|
25
24
|
return {
|
|
26
25
|
ImportDeclaration(node) {
|
|
27
|
-
|
|
26
|
+
extractImportedFrom({
|
|
27
|
+
importMap: importedFromEffector,
|
|
28
|
+
node,
|
|
29
|
+
packageName: "effector",
|
|
30
|
+
});
|
|
28
31
|
},
|
|
29
32
|
CallExpression(node) {
|
|
30
|
-
const METHODS_WITH_POSSIBLE_UNNECESSARY_COMBINATION = [
|
|
31
|
-
"sample",
|
|
32
|
-
"guard",
|
|
33
|
-
"forward",
|
|
34
|
-
];
|
|
35
|
-
|
|
36
33
|
const CONFIG_ARG_PROPERTIES = ["source", "clock", "from"];
|
|
37
34
|
|
|
38
|
-
function toLocalMethod(
|
|
39
|
-
return importedFromEffector.get(
|
|
35
|
+
function toLocalMethod(methodName) {
|
|
36
|
+
return importedFromEffector.get(methodName);
|
|
40
37
|
}
|
|
41
38
|
|
|
42
39
|
const UNNECESSARY_METHODS = {
|
|
@@ -45,48 +42,45 @@ module.exports = {
|
|
|
45
42
|
from: ["merge"].map(toLocalMethod).filter(Boolean),
|
|
46
43
|
};
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
45
|
+
if (
|
|
46
|
+
method.isNot(["sample", "guard", "forward"], {
|
|
47
|
+
node,
|
|
48
|
+
importMap: importedFromEffector,
|
|
49
|
+
})
|
|
50
|
+
) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const candidates =
|
|
55
|
+
node?.arguments?.[0]?.properties?.filter((n) =>
|
|
56
|
+
CONFIG_ARG_PROPERTIES.includes(n.key.name)
|
|
57
|
+
) ?? [];
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
) ?? [];
|
|
59
|
+
if (candidates.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
for (const candidate of candidates) {
|
|
64
|
+
const candidateName = candidate?.value?.callee?.name;
|
|
65
|
+
const argProp = candidate?.key?.name;
|
|
66
|
+
if (!candidateName || !argProp) {
|
|
65
67
|
continue;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
const candidateName = candidate?.value?.callee?.name;
|
|
70
|
-
const argProp = candidate?.key?.name;
|
|
71
|
-
if (!candidateName || !argProp) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const localUnnecessaryMethods = UNNECESSARY_METHODS[argProp];
|
|
76
|
-
|
|
77
|
-
const UnnecessaryMethodIsEffectorMethod =
|
|
78
|
-
localUnnecessaryMethods.some((m) => m === candidateName);
|
|
70
|
+
const localUnnecessaryMethods = UNNECESSARY_METHODS[argProp];
|
|
79
71
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
72
|
+
const UnnecessaryMethodIsEffectorMethod =
|
|
73
|
+
localUnnecessaryMethods.some((m) => m === candidateName);
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
messageId: "unnecessaryCombination",
|
|
87
|
-
data: { methodName: candidateName },
|
|
88
|
-
});
|
|
75
|
+
if (!UnnecessaryMethodIsEffectorMethod) {
|
|
76
|
+
continue;
|
|
89
77
|
}
|
|
78
|
+
|
|
79
|
+
context.report({
|
|
80
|
+
node: candidate?.value,
|
|
81
|
+
messageId: "unnecessaryCombination",
|
|
82
|
+
data: { methodName: candidateName },
|
|
83
|
+
});
|
|
90
84
|
}
|
|
91
85
|
},
|
|
92
86
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const {
|
|
2
|
-
extractImportedFromEffector,
|
|
3
|
-
} = require("../../utils/extract-imported-from-effector");
|
|
1
|
+
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
4
2
|
const { areNodesSameInText } = require("../../utils/are-nodes-same-in-text");
|
|
5
3
|
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
4
|
+
const { buildObjectInText } = require("../../utils/builders");
|
|
5
|
+
const { method } = require("../../utils/method");
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
meta: {
|
|
@@ -24,52 +24,51 @@ module.exports = {
|
|
|
24
24
|
},
|
|
25
25
|
create(context) {
|
|
26
26
|
const importedFromEffector = new Map();
|
|
27
|
-
const sourceCode = context.getSourceCode();
|
|
28
27
|
|
|
29
28
|
return {
|
|
30
29
|
ImportDeclaration(node) {
|
|
31
|
-
|
|
30
|
+
extractImportedFrom({
|
|
31
|
+
importMap: importedFromEffector,
|
|
32
|
+
node,
|
|
33
|
+
packageName: "effector",
|
|
34
|
+
});
|
|
32
35
|
},
|
|
33
36
|
CallExpression(node) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!isEffectorMethod) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const params = {
|
|
47
|
-
source: node?.arguments?.[0]?.properties?.find(
|
|
48
|
-
(n) => n.key.name === "source"
|
|
49
|
-
),
|
|
50
|
-
clock: node?.arguments?.[0]?.properties?.find(
|
|
51
|
-
(n) => n.key.name === "clock"
|
|
52
|
-
),
|
|
53
|
-
};
|
|
54
|
-
if (!params.source || !params.clock) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
37
|
+
if (
|
|
38
|
+
method.isNot(["sample", "guard"], {
|
|
39
|
+
node,
|
|
40
|
+
importMap: importedFromEffector,
|
|
41
|
+
})
|
|
42
|
+
) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
const params = {
|
|
47
|
+
source: node?.arguments?.[0]?.properties?.find(
|
|
48
|
+
(n) => n.key.name === "source"
|
|
49
|
+
),
|
|
50
|
+
clock: node?.arguments?.[0]?.properties?.find(
|
|
51
|
+
(n) => n.key.name === "clock"
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
if (!params.source || !params.clock) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
const sameSourceAndClock = areNodesSameInText({
|
|
59
|
+
context,
|
|
60
|
+
nodes: [params.source?.value, params.clock?.value],
|
|
61
|
+
});
|
|
62
|
+
if (!sameSourceAndClock) {
|
|
63
|
+
return;
|
|
72
64
|
}
|
|
65
|
+
|
|
66
|
+
reportUnnecessaryDuplication({
|
|
67
|
+
context,
|
|
68
|
+
node,
|
|
69
|
+
params,
|
|
70
|
+
firstArgument: node?.arguments?.[0],
|
|
71
|
+
});
|
|
73
72
|
},
|
|
74
73
|
};
|
|
75
74
|
},
|
|
@@ -85,11 +84,8 @@ function reportUnnecessaryDuplication({
|
|
|
85
84
|
const properties = objectNode?.properties?.filter?.(
|
|
86
85
|
(p) => p !== paramToExcludeNode
|
|
87
86
|
);
|
|
88
|
-
const newPropertiesText = properties
|
|
89
|
-
.map((p) => context.getSourceCode().getText(p))
|
|
90
|
-
.join(", ");
|
|
91
87
|
|
|
92
|
-
return
|
|
88
|
+
return buildObjectInText.fromArrayOfNodes({ properties, context });
|
|
93
89
|
}
|
|
94
90
|
|
|
95
91
|
context.report({
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
const {
|
|
2
|
-
extractImportedFromEffector,
|
|
3
|
-
} = require("../../utils/extract-imported-from-effector");
|
|
1
|
+
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
4
2
|
const { traverseParentByType } = require("../../utils/traverse-parent-by-type");
|
|
5
3
|
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
4
|
+
const { method } = require("../../utils/method");
|
|
6
5
|
|
|
7
6
|
module.exports = {
|
|
8
7
|
meta: {
|
|
@@ -24,70 +23,70 @@ module.exports = {
|
|
|
24
23
|
|
|
25
24
|
return {
|
|
26
25
|
ImportDeclaration(node) {
|
|
27
|
-
|
|
26
|
+
extractImportedFrom({
|
|
27
|
+
importMap: importedFromEffector,
|
|
28
|
+
node,
|
|
29
|
+
packageName: "effector",
|
|
30
|
+
});
|
|
28
31
|
},
|
|
29
32
|
CallExpression(node) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const localMethod = importedFromEffector.get(method);
|
|
33
|
-
if (!localMethod) {
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const isEffectorMethod = node?.callee?.name === localMethod;
|
|
38
|
-
if (!isEffectorMethod) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const resultAssignedInVariable = traverseParentByType(
|
|
33
|
+
if (
|
|
34
|
+
method.isNot(["sample", "guard"], {
|
|
43
35
|
node,
|
|
44
|
-
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
importMap: importedFromEffector,
|
|
37
|
+
})
|
|
38
|
+
) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
42
|
+
const resultAssignedInVariable = traverseParentByType(
|
|
43
|
+
node,
|
|
44
|
+
"VariableDeclarator"
|
|
45
|
+
);
|
|
46
|
+
if (resultAssignedInVariable) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
const resultReturnedFromFactory = traverseParentByType(
|
|
51
|
+
node,
|
|
52
|
+
"ReturnStatement"
|
|
53
|
+
);
|
|
54
|
+
if (resultReturnedFromFactory) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
const resultPartOfChain = traverseParentByType(
|
|
59
|
+
node,
|
|
60
|
+
"ObjectExpression"
|
|
61
|
+
);
|
|
62
|
+
if (resultPartOfChain) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
const configHasTarget = node?.arguments?.[0]?.properties?.some(
|
|
67
|
+
(prop) => prop?.key.name === "target"
|
|
68
|
+
);
|
|
69
|
+
if (configHasTarget) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
77
72
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
const resultIsWatched = node?.parent?.property?.name === "watch";
|
|
74
|
+
if (resultIsWatched) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
82
77
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
data: {
|
|
87
|
-
methodName: node?.callee?.name,
|
|
88
|
-
},
|
|
89
|
-
});
|
|
78
|
+
const resultIsArgument = node?.parent?.type === "CallExpression";
|
|
79
|
+
if (resultIsArgument) {
|
|
80
|
+
return;
|
|
90
81
|
}
|
|
82
|
+
|
|
83
|
+
context.report({
|
|
84
|
+
node,
|
|
85
|
+
messageId: "uselessMethod",
|
|
86
|
+
data: {
|
|
87
|
+
methodName: node?.callee?.name,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
91
90
|
},
|
|
92
91
|
};
|
|
93
92
|
},
|
|
@@ -2,6 +2,7 @@ const {
|
|
|
2
2
|
traverseNestedObjectNode,
|
|
3
3
|
} = require("../../utils/traverse-nested-object-node");
|
|
4
4
|
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
5
|
+
const { nodeTypeIs } = require("../../utils/node-type-is");
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
7
8
|
meta: {
|
|
@@ -24,22 +25,15 @@ module.exports = {
|
|
|
24
25
|
// JavaScript-way https://github.com/effector/eslint-plugin/issues/48#issuecomment-931107829
|
|
25
26
|
return {};
|
|
26
27
|
}
|
|
27
|
-
const checker = parserServices.program.getTypeChecker();
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
|
-
CallExpression(node) {
|
|
31
|
-
const methodName = node.callee?.property?.name;
|
|
32
|
-
if (methodName !== "watch") {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
30
|
+
'CallExpression[callee.property.name="watch"]'(node) {
|
|
36
31
|
const object = traverseNestedObjectNode(node.callee?.object);
|
|
37
|
-
const originalNode = parserServices.esTreeNodeToTSNodeMap.get(object);
|
|
38
|
-
const type = checker.getTypeAtLocation(originalNode);
|
|
39
32
|
|
|
40
|
-
const isEffectorUnit =
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
const isEffectorUnit = nodeTypeIs.unit({
|
|
34
|
+
node: object,
|
|
35
|
+
context,
|
|
36
|
+
});
|
|
43
37
|
|
|
44
38
|
if (!isEffectorUnit) {
|
|
45
39
|
return;
|