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.
Files changed (44) hide show
  1. package/README.md +25 -6
  2. package/config/react.js +5 -0
  3. package/config/recommended.js +2 -0
  4. package/config/scope.js +5 -0
  5. package/index.js +7 -0
  6. package/package.json +4 -1
  7. package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.js +16 -17
  8. package/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.js +114 -0
  9. package/rules/enforce-store-naming-convention/enforce-store-naming-convention.js +25 -25
  10. package/rules/keep-options-order/config.js +3 -0
  11. package/rules/keep-options-order/keep-options-order.js +107 -0
  12. package/rules/no-ambiguity-target/no-ambiguity-target.js +38 -39
  13. package/rules/no-duplicate-on/no-duplicate-on.js +137 -0
  14. package/rules/no-forward/no-forward.js +77 -0
  15. package/rules/no-getState/no-getState.js +9 -36
  16. package/rules/no-unnecessary-combination/no-unnecessary-combination.js +39 -45
  17. package/rules/no-unnecessary-duplication/no-unnecessary-duplication.js +41 -45
  18. package/rules/no-useless-methods/no-useless-methods.js +56 -57
  19. package/rules/no-watch/no-watch.js +6 -12
  20. package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.js +31 -13
  21. package/rules/strict-effect-handlers/strict-effect-handlers.js +76 -0
  22. package/utils/builders.js +19 -0
  23. package/utils/extract-imported-from.js +10 -0
  24. package/utils/get-corrected-store-name.js +12 -14
  25. package/utils/get-nested-object-name.js +18 -0
  26. package/utils/get-store-name-convention.js +3 -3
  27. package/utils/is.js +30 -0
  28. package/utils/method.js +23 -0
  29. package/utils/naming.js +47 -0
  30. package/utils/node-type-is.js +59 -0
  31. package/utils/replace-by-sample.js +66 -0
  32. package/utils/validate-store-name-convention.js +7 -7
  33. package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.md +0 -11
  34. package/rules/enforce-store-naming-convention/enforce-store-naming-convention.md +0 -44
  35. package/rules/no-ambiguity-target/no-ambiguity-target.md +0 -12
  36. package/rules/no-getState/no-getState.md +0 -20
  37. package/rules/no-unnecessary-combination/no-unnecessary-combination.md +0 -14
  38. package/rules/no-unnecessary-duplication/no-unnecessary-duplication.md +0 -32
  39. package/rules/no-useless-methods/no-useless-methods.md +0 -14
  40. package/rules/no-watch/no-watch.md +0 -42
  41. package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.md +0 -27
  42. package/rules/tsconfig.json +0 -9
  43. package/utils/extract-imported-from-effector.js +0 -8
  44. 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 methodName = node.callee?.property?.name;
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 object = traverseNestedObjectNode(node.callee?.object);
33
- const objectName = object?.name;
27
+ const isEffectorStore = is.store({
28
+ context,
29
+ node: storeNode,
30
+ });
34
31
 
35
- if (!objectName) {
32
+ if (!isEffectorStore) {
36
33
  return;
37
34
  }
38
35
 
39
- // TypeScript-way
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
- extractImportedFromEffector(importedFromEffector, node);
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(method) {
39
- return importedFromEffector.get(method);
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
- for (const method of METHODS_WITH_POSSIBLE_UNNECESSARY_COMBINATION) {
49
- const localMethod = importedFromEffector.get(method);
50
- if (!localMethod) {
51
- continue;
52
- }
45
+ if (
46
+ method.isNot(["sample", "guard", "forward"], {
47
+ node,
48
+ importMap: importedFromEffector,
49
+ })
50
+ ) {
51
+ return;
52
+ }
53
53
 
54
- const isEffectorMethod = node?.callee?.name === localMethod;
55
- if (!isEffectorMethod) {
56
- continue;
57
- }
54
+ const candidates =
55
+ node?.arguments?.[0]?.properties?.filter((n) =>
56
+ CONFIG_ARG_PROPERTIES.includes(n.key.name)
57
+ ) ?? [];
58
58
 
59
- const candidates =
60
- node?.arguments?.[0]?.properties?.filter((n) =>
61
- CONFIG_ARG_PROPERTIES.includes(n.key.name)
62
- ) ?? [];
59
+ if (candidates.length === 0) {
60
+ return;
61
+ }
63
62
 
64
- if (candidates.length === 0) {
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
- for (const candidate of candidates) {
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
- if (!UnnecessaryMethodIsEffectorMethod) {
81
- continue;
82
- }
72
+ const UnnecessaryMethodIsEffectorMethod =
73
+ localUnnecessaryMethods.some((m) => m === candidateName);
83
74
 
84
- context.report({
85
- node: candidate?.value,
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
- extractImportedFromEffector(importedFromEffector, node);
30
+ extractImportedFrom({
31
+ importMap: importedFromEffector,
32
+ node,
33
+ packageName: "effector",
34
+ });
32
35
  },
33
36
  CallExpression(node) {
34
- const METHODS_WITH_POSSIBLE_DUPLCATION = ["sample", "guard"];
35
- for (const method of METHODS_WITH_POSSIBLE_DUPLCATION) {
36
- const localMethod = importedFromEffector.get(method);
37
- if (!localMethod) {
38
- continue;
39
- }
40
-
41
- const isEffectorMethod = node?.callee?.name === localMethod;
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
- const sameSourceAndClock = areNodesSameInText({
59
- context,
60
- nodes: [params.source?.value, params.clock?.value],
61
- });
62
- if (!sameSourceAndClock) {
63
- return;
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
- reportUnnecessaryDuplication({
67
- context,
68
- node,
69
- params,
70
- firstArgument: node?.arguments?.[0],
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 `{ ${newPropertiesText} }`;
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
- extractImportedFromEffector(importedFromEffector, node);
26
+ extractImportedFrom({
27
+ importMap: importedFromEffector,
28
+ node,
29
+ packageName: "effector",
30
+ });
28
31
  },
29
32
  CallExpression(node) {
30
- const POSSIBLE_USELESS_METHODS = ["sample", "guard"];
31
- for (const method of POSSIBLE_USELESS_METHODS) {
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
- "VariableDeclarator"
45
- );
46
- if (resultAssignedInVariable) {
47
- continue;
48
- }
36
+ importMap: importedFromEffector,
37
+ })
38
+ ) {
39
+ return;
40
+ }
49
41
 
50
- const resultReturnedFromFactory = traverseParentByType(
51
- node,
52
- "ReturnStatement"
53
- );
54
- if (resultReturnedFromFactory) {
55
- continue;
56
- }
42
+ const resultAssignedInVariable = traverseParentByType(
43
+ node,
44
+ "VariableDeclarator"
45
+ );
46
+ if (resultAssignedInVariable) {
47
+ return;
48
+ }
57
49
 
58
- const resultPartOfChain = traverseParentByType(
59
- node,
60
- "ObjectExpression"
61
- );
62
- if (resultPartOfChain) {
63
- continue;
64
- }
50
+ const resultReturnedFromFactory = traverseParentByType(
51
+ node,
52
+ "ReturnStatement"
53
+ );
54
+ if (resultReturnedFromFactory) {
55
+ return;
56
+ }
65
57
 
66
- const configHasTarget = node?.arguments?.[0]?.properties?.some(
67
- (prop) => prop?.key.name === "target"
68
- );
69
- if (configHasTarget) {
70
- continue;
71
- }
58
+ const resultPartOfChain = traverseParentByType(
59
+ node,
60
+ "ObjectExpression"
61
+ );
62
+ if (resultPartOfChain) {
63
+ return;
64
+ }
72
65
 
73
- const resultIsWatched = node?.parent?.property?.name === "watch";
74
- if (resultIsWatched) {
75
- continue;
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
- const resultIsArgument = node?.parent?.type === "CallExpression";
79
- if (resultIsArgument) {
80
- continue;
81
- }
73
+ const resultIsWatched = node?.parent?.property?.name === "watch";
74
+ if (resultIsWatched) {
75
+ return;
76
+ }
82
77
 
83
- context.report({
84
- node,
85
- messageId: "uselessMethod",
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
- ["Effect", "Event", "Store"].includes(type?.symbol?.escapedName) &&
42
- type?.symbol?.parent?.escapedName?.includes("effector");
33
+ const isEffectorUnit = nodeTypeIs.unit({
34
+ node: object,
35
+ context,
36
+ });
43
37
 
44
38
  if (!isEffectorUnit) {
45
39
  return;