eslint-plugin-effector 0.16.0 → 0.17.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 (53) hide show
  1. package/README.md +24 -37
  2. package/dist/index.cjs +1259 -0
  3. package/dist/index.d.cts +177 -0
  4. package/dist/index.d.mts +178 -0
  5. package/dist/index.mjs +1233 -0
  6. package/package.json +71 -17
  7. package/.nvmrc +0 -1
  8. package/config/future.js +0 -7
  9. package/config/patronum.js +0 -5
  10. package/config/react.js +0 -7
  11. package/config/recommended.js +0 -15
  12. package/config/scope.js +0 -6
  13. package/index.js +0 -31
  14. package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.js +0 -143
  15. package/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.js +0 -122
  16. package/rules/enforce-store-naming-convention/enforce-store-naming-convention.js +0 -205
  17. package/rules/keep-options-order/config.js +0 -3
  18. package/rules/keep-options-order/keep-options-order.js +0 -107
  19. package/rules/mandatory-scope-binding/mandatory-scope-binding.js +0 -81
  20. package/rules/no-ambiguity-target/no-ambiguity-target.js +0 -74
  21. package/rules/no-duplicate-clock-or-source-array-values/no-duplicate-clock-or-source-array-values.js +0 -124
  22. package/rules/no-duplicate-on/no-duplicate-on.js +0 -137
  23. package/rules/no-forward/no-forward.js +0 -73
  24. package/rules/no-getState/no-getState.js +0 -50
  25. package/rules/no-guard/no-guard.js +0 -78
  26. package/rules/no-patronum-debug/no-patronum-debug.js +0 -133
  27. package/rules/no-unnecessary-combination/no-unnecessary-combination.js +0 -88
  28. package/rules/no-unnecessary-duplication/no-unnecessary-duplication.js +0 -115
  29. package/rules/no-useless-methods/no-useless-methods.js +0 -93
  30. package/rules/no-watch/no-watch.js +0 -61
  31. package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.js +0 -111
  32. package/rules/prefer-useUnit/prefer-useUnit.js +0 -56
  33. package/rules/require-pickup-in-persist/require-pickup-in-persist.js +0 -47
  34. package/rules/strict-effect-handlers/strict-effect-handlers.js +0 -76
  35. package/utils/are-nodes-same-in-text.js +0 -22
  36. package/utils/builders.js +0 -19
  37. package/utils/create-link-to-rule.js +0 -5
  38. package/utils/extract-config.js +0 -26
  39. package/utils/extract-imported-from.js +0 -18
  40. package/utils/get-corrected-store-name.js +0 -45
  41. package/utils/get-nested-object-name.js +0 -18
  42. package/utils/get-store-name-convention.js +0 -6
  43. package/utils/is.js +0 -39
  44. package/utils/method.js +0 -23
  45. package/utils/naming.js +0 -47
  46. package/utils/node-is-type.js +0 -5
  47. package/utils/node-type-is.js +0 -106
  48. package/utils/react.js +0 -214
  49. package/utils/read-example.js +0 -63
  50. package/utils/replace-by-sample.js +0 -98
  51. package/utils/traverse-nested-object-node.js +0 -9
  52. package/utils/traverse-parent-by-type.js +0 -15
  53. package/utils/validate-store-name-convention.js +0 -13
@@ -1,78 +0,0 @@
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 { replaceGuardBySample } = require("../../utils/replace-by-sample");
5
- const { extractConfig } = require("../../utils/extract-config");
6
-
7
- module.exports = {
8
- meta: {
9
- type: "problem",
10
- docs: {
11
- description: "Prefer `sample` over `guard`",
12
- category: "Quality",
13
- recommended: true,
14
- url: createLinkToRule("no-guard"),
15
- },
16
- messages: {
17
- noGuard:
18
- "Instead of `guard` you can use `sample`, it is more extendable.",
19
- replaceWithSample: "Replace `guard` with `sample`.",
20
- },
21
- schema: [],
22
- hasSuggestions: true,
23
- },
24
- create(context) {
25
- const importNodes = new Map();
26
- const importedFromEffector = new Map();
27
-
28
- return {
29
- ImportDeclaration(node) {
30
- extractImportedFrom({
31
- importMap: importedFromEffector,
32
- nodeMap: importNodes,
33
- node,
34
- packageName: "effector",
35
- });
36
- },
37
- CallExpression(node) {
38
- if (
39
- method.isNot("guard", {
40
- node,
41
- importMap: importedFromEffector,
42
- })
43
- ) {
44
- return;
45
- }
46
-
47
- const guardConfig = extractConfig(
48
- ["source", "clock", "target", "filter"],
49
- {
50
- node,
51
- }
52
- );
53
-
54
- if (!guardConfig.clock || !guardConfig.filter) {
55
- return;
56
- }
57
-
58
- context.report({
59
- messageId: "noGuard",
60
- node,
61
- suggest: [
62
- {
63
- messageId: "replaceWithSample",
64
- *fix(fixer) {
65
- yield* replaceGuardBySample(guardConfig, {
66
- fixer,
67
- node,
68
- context,
69
- importNodes,
70
- });
71
- },
72
- },
73
- ],
74
- });
75
- },
76
- };
77
- },
78
- };
@@ -1,133 +0,0 @@
1
- const { createLinkToRule } = require("../../utils/create-link-to-rule");
2
- const { extractImportedFrom } = require("../../utils/extract-imported-from");
3
-
4
- module.exports = {
5
- meta: {
6
- type: "suggestion",
7
- docs: {
8
- description: "Disallow the use of patronum `debug`",
9
- category: "Quality",
10
- recommended: false,
11
- url: createLinkToRule("no-patronum-debug"),
12
- },
13
- messages: {
14
- noPatronumDebug: "Unexpected patronum `debug` statement",
15
- removePatronumDebug: "Remove this `debug` from patronum",
16
- },
17
- schema: [],
18
- hasSuggestions: true,
19
- },
20
- create(context) {
21
- const importedFromPatronum = new Map();
22
- const importNodes = new Map();
23
-
24
- return {
25
- ImportDeclaration(node) {
26
- extractImportedFrom({
27
- packageName: ["patronum", "patronum/debug"],
28
- importMap: importedFromPatronum,
29
- nodeMap: importNodes,
30
- node,
31
- });
32
- },
33
- CallExpression(node) {
34
- const currentMethod = node?.callee?.name ?? node?.callee?.object?.name;
35
- const importedDebugFromPatronum = importedFromPatronum.get("debug");
36
-
37
- if (
38
- !importedDebugFromPatronum ||
39
- currentMethod !== importedDebugFromPatronum
40
- ) {
41
- return;
42
- }
43
-
44
- context.report({
45
- messageId: "noPatronumDebug",
46
- node,
47
- suggest: [
48
- {
49
- messageId: "removePatronumDebug",
50
- *fix(fixer) {
51
- yield* removeDebugFromPatronum({
52
- fixer,
53
- node,
54
- context,
55
- importNodes,
56
- });
57
- },
58
- },
59
- ],
60
- });
61
- },
62
- };
63
- },
64
- };
65
-
66
- function* removeDebugFromPatronum({
67
- fixer,
68
- node,
69
- context,
70
- importNodes,
71
- targetMethod = "debug",
72
- }) {
73
- const sourceCode = context.getSourceCode();
74
- const startToken = sourceCode.getTokenBefore(node);
75
-
76
- // remove line with debug
77
- yield fixer.removeRange([startToken.range[1], node.range[1]]);
78
- const semi = sourceCode.getTokenBefore(node, {
79
- filter: (token) => token.value === ";",
80
- });
81
-
82
- if (semi) yield fixer.remove(semi);
83
-
84
- const importDebugNode = importNodes.get(targetMethod);
85
-
86
- if (!importDebugNode) {
87
- return null;
88
- }
89
-
90
- // remove import with debug
91
- const importParentNode = importDebugNode.parent;
92
- const amountImportFromPatronum = importParentNode.specifiers.length;
93
-
94
- /**
95
- * import { debug } from 'patronum'
96
- * import { debug } from 'patronum/debug'
97
- */
98
- if (amountImportFromPatronum === 1) {
99
- yield fixer.removeRange([
100
- importParentNode.range[0],
101
- importParentNode.range[1] + 1,
102
- ]);
103
-
104
- return null;
105
- }
106
-
107
- const importLast = importParentNode.specifiers[amountImportFromPatronum - 1];
108
- const filterTokenComma = { filter: (token) => token.value === "," };
109
-
110
- /**
111
- * import { debug, timeout } from 'patronum'
112
- * import { condition, debug, throttle } from 'patronum'
113
- */
114
- if (importDebugNode !== importLast) {
115
- const prevNode = sourceCode.getTokenBefore(importDebugNode);
116
- const comma = sourceCode.getTokenAfter(importDebugNode, filterTokenComma);
117
-
118
- yield fixer.removeRange([prevNode.range[1], importDebugNode.range[0]]);
119
- yield fixer.remove(importDebugNode);
120
- yield fixer.remove(comma);
121
-
122
- return null;
123
- }
124
-
125
- /**
126
- * import { condition, debug } from 'patronum'
127
- */
128
- const comma = sourceCode.getTokenBefore(importDebugNode, filterTokenComma);
129
-
130
- yield fixer.removeRange([comma.range[1], importDebugNode.range[0]]);
131
- yield fixer.remove(importDebugNode);
132
- yield fixer.remove(comma);
133
- }
@@ -1,88 +0,0 @@
1
- const { extractImportedFrom } = require("../../utils/extract-imported-from");
2
- const { createLinkToRule } = require("../../utils/create-link-to-rule");
3
- const { method } = require("../../utils/method");
4
-
5
- module.exports = {
6
- meta: {
7
- type: "problem",
8
- docs: {
9
- description:
10
- "Forbids unnecessary combinations in `clock`, `source` and `forward`",
11
- category: "Quality",
12
- recommended: true,
13
- url: createLinkToRule("no-unnecessary-combination"),
14
- },
15
- messages: {
16
- unnecessaryCombination:
17
- "Method {{ methodName }} is used under the hood, you can omit it.",
18
- },
19
- schema: [],
20
- },
21
- create(context) {
22
- const importedFromEffector = new Map();
23
-
24
- return {
25
- ImportDeclaration(node) {
26
- extractImportedFrom({
27
- importMap: importedFromEffector,
28
- node,
29
- packageName: "effector",
30
- });
31
- },
32
- CallExpression(node) {
33
- const CONFIG_ARG_PROPERTIES = ["source", "clock", "from"];
34
-
35
- function toLocalMethod(methodName) {
36
- return importedFromEffector.get(methodName);
37
- }
38
-
39
- const UNNECESSARY_METHODS = {
40
- source: ["combine", "merge"].map(toLocalMethod).filter(Boolean),
41
- clock: ["merge"].map(toLocalMethod).filter(Boolean),
42
- from: ["merge"].map(toLocalMethod).filter(Boolean),
43
- };
44
-
45
- if (
46
- method.isNot(["sample", "guard", "forward"], {
47
- node,
48
- importMap: importedFromEffector,
49
- })
50
- ) {
51
- return;
52
- }
53
-
54
- const candidates =
55
- node?.arguments?.[0]?.properties?.filter((n) =>
56
- CONFIG_ARG_PROPERTIES.includes(n.key.name)
57
- ) ?? [];
58
-
59
- if (candidates.length === 0) {
60
- return;
61
- }
62
-
63
- for (const candidate of candidates) {
64
- const candidateName = candidate?.value?.callee?.name;
65
- const argProp = candidate?.key?.name;
66
- if (!candidateName || !argProp) {
67
- continue;
68
- }
69
-
70
- const localUnnecessaryMethods = UNNECESSARY_METHODS[argProp];
71
-
72
- const UnnecessaryMethodIsEffectorMethod =
73
- localUnnecessaryMethods.some((m) => m === candidateName);
74
-
75
- if (!UnnecessaryMethodIsEffectorMethod) {
76
- continue;
77
- }
78
-
79
- context.report({
80
- node: candidate?.value,
81
- messageId: "unnecessaryCombination",
82
- data: { methodName: candidateName },
83
- });
84
- }
85
- },
86
- };
87
- },
88
- };
@@ -1,115 +0,0 @@
1
- const { extractImportedFrom } = require("../../utils/extract-imported-from");
2
- const { areNodesSameInText } = require("../../utils/are-nodes-same-in-text");
3
- const { createLinkToRule } = require("../../utils/create-link-to-rule");
4
- const { buildObjectInText } = require("../../utils/builders");
5
- const { method } = require("../../utils/method");
6
-
7
- module.exports = {
8
- meta: {
9
- type: "problem",
10
- docs: {
11
- description: "Forbids unnecessary duplication in `clock` and `source`",
12
- category: "Quality",
13
- recommended: true,
14
- url: createLinkToRule("no-unnecessary-duplication"),
15
- },
16
- messages: {
17
- unnecessaryDuplication:
18
- "Same `source` and `clock` can be replaced with only one of them.",
19
- removeClock: "Remove `clock`",
20
- removeSource: "Remove `source`",
21
- },
22
- schema: [],
23
- hasSuggestions: true,
24
- },
25
- create(context) {
26
- const importedFromEffector = new Map();
27
-
28
- return {
29
- ImportDeclaration(node) {
30
- extractImportedFrom({
31
- importMap: importedFromEffector,
32
- node,
33
- packageName: "effector",
34
- });
35
- },
36
- CallExpression(node) {
37
- if (
38
- method.isNot(["sample", "guard"], {
39
- node,
40
- importMap: importedFromEffector,
41
- })
42
- ) {
43
- return;
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
- }
57
-
58
- const sameSourceAndClock = areNodesSameInText({
59
- context,
60
- nodes: [params.source?.value, params.clock?.value],
61
- });
62
- if (!sameSourceAndClock) {
63
- return;
64
- }
65
-
66
- reportUnnecessaryDuplication({
67
- context,
68
- node,
69
- params,
70
- firstArgument: node?.arguments?.[0],
71
- });
72
- },
73
- };
74
- },
75
- };
76
-
77
- function reportUnnecessaryDuplication({
78
- context,
79
- node,
80
- params,
81
- firstArgument,
82
- }) {
83
- function excludeParamFromObjectInText(objectNode, paramToExcludeNode) {
84
- const properties = objectNode?.properties?.filter?.(
85
- (p) => p !== paramToExcludeNode
86
- );
87
-
88
- return buildObjectInText.fromArrayOfNodes({ properties, context });
89
- }
90
-
91
- context.report({
92
- node,
93
- messageId: "unnecessaryDuplication",
94
- suggest: [
95
- {
96
- messageId: "removeClock",
97
- fix(fixer) {
98
- return fixer.replaceText(
99
- firstArgument,
100
- excludeParamFromObjectInText(firstArgument, params.clock)
101
- );
102
- },
103
- },
104
- {
105
- messageId: "removeSource",
106
- fix(fixer) {
107
- return fixer.replaceText(
108
- firstArgument,
109
- excludeParamFromObjectInText(firstArgument, params.source)
110
- );
111
- },
112
- },
113
- ],
114
- });
115
- }
@@ -1,93 +0,0 @@
1
- const { extractImportedFrom } = require("../../utils/extract-imported-from");
2
- const { traverseParentByType } = require("../../utils/traverse-parent-by-type");
3
- const { createLinkToRule } = require("../../utils/create-link-to-rule");
4
- const { method } = require("../../utils/method");
5
-
6
- module.exports = {
7
- meta: {
8
- type: "problem",
9
- docs: {
10
- description: "Forbids useless calls of `sample` and `guard`",
11
- category: "Quality",
12
- recommended: true,
13
- url: createLinkToRule("no-useless-methods"),
14
- },
15
- messages: {
16
- uselessMethod:
17
- "Method `{{ methodName }}` does nothing in this case. You should assign the result to variable or pass `target` to it.",
18
- },
19
- schema: [],
20
- },
21
- create(context) {
22
- const importedFromEffector = new Map();
23
-
24
- return {
25
- ImportDeclaration(node) {
26
- extractImportedFrom({
27
- importMap: importedFromEffector,
28
- node,
29
- packageName: "effector",
30
- });
31
- },
32
- CallExpression(node) {
33
- if (
34
- method.isNot(["sample", "guard"], {
35
- node,
36
- importMap: importedFromEffector,
37
- })
38
- ) {
39
- return;
40
- }
41
-
42
- const resultAssignedInVariable = traverseParentByType(
43
- node,
44
- "VariableDeclarator"
45
- );
46
- if (resultAssignedInVariable) {
47
- return;
48
- }
49
-
50
- const resultReturnedFromFactory = traverseParentByType(
51
- node,
52
- "ReturnStatement"
53
- );
54
- if (resultReturnedFromFactory) {
55
- return;
56
- }
57
-
58
- const resultPartOfChain = traverseParentByType(
59
- node,
60
- "ObjectExpression"
61
- );
62
- if (resultPartOfChain) {
63
- return;
64
- }
65
-
66
- const configHasTarget = node?.arguments?.[0]?.properties?.some(
67
- (prop) => prop?.key.name === "target"
68
- );
69
- if (configHasTarget) {
70
- return;
71
- }
72
-
73
- const resultIsWatched = node?.parent?.property?.name === "watch";
74
- if (resultIsWatched) {
75
- return;
76
- }
77
-
78
- const resultIsArgument = node?.parent?.type === "CallExpression";
79
- if (resultIsArgument) {
80
- return;
81
- }
82
-
83
- context.report({
84
- node,
85
- messageId: "uselessMethod",
86
- data: {
87
- methodName: node?.callee?.name,
88
- },
89
- });
90
- },
91
- };
92
- },
93
- };
@@ -1,61 +0,0 @@
1
- const { ESLintUtils } = require("@typescript-eslint/utils");
2
-
3
- const {
4
- traverseNestedObjectNode,
5
- } = require("../../utils/traverse-nested-object-node");
6
- const { createLinkToRule } = require("../../utils/create-link-to-rule");
7
- const { nodeTypeIs } = require("../../utils/node-type-is");
8
-
9
- module.exports = {
10
- meta: {
11
- type: "suggestion",
12
- docs: {
13
- description: "Avoid `.watch` calls on any Effector unit or operator",
14
- category: "Quality",
15
- recommended: true,
16
- url: createLinkToRule("no-watch"),
17
- },
18
- messages: {
19
- abusiveCall:
20
- "Method `.watch` leads to imperative code. Try to replace it with operator (`sample`) or use the `target` parameter of the operator.",
21
- },
22
- schema: [],
23
- },
24
- create(context) {
25
- let parserServices;
26
- try {
27
- parserServices = ESLintUtils.getParserServices(context);
28
- } catch (err) {
29
- // no types information
30
- }
31
-
32
- if (!parserServices?.program) {
33
- // JavaScript-way https://github.com/effector/eslint-plugin/issues/48#issuecomment-931107829
34
- return {};
35
- }
36
-
37
- return {
38
- 'CallExpression[callee.property.name="watch"]'(node) {
39
- const object = traverseNestedObjectNode(node.callee?.object);
40
-
41
- const isEffectorUnit = nodeTypeIs.unit({
42
- node: object,
43
- context,
44
- });
45
-
46
- if (!isEffectorUnit) {
47
- return;
48
- }
49
-
50
- reportWatchCall({ context, node });
51
- },
52
- };
53
- },
54
- };
55
-
56
- function reportWatchCall({ context, node }) {
57
- context.report({
58
- node,
59
- messageId: "abusiveCall",
60
- });
61
- }
@@ -1,111 +0,0 @@
1
- const { extractImportedFrom } = require("../../utils/extract-imported-from");
2
- const {
3
- traverseNestedObjectNode,
4
- } = require("../../utils/traverse-nested-object-node");
5
- const { createLinkToRule } = require("../../utils/create-link-to-rule");
6
- const { method } = require("../../utils/method");
7
- const { replaceForwardBySample } = require("../../utils/replace-by-sample");
8
- const { extractConfig } = require("../../utils/extract-config");
9
-
10
- module.exports = {
11
- meta: {
12
- type: "problem",
13
- docs: {
14
- description: "Prefer `sample` over `forward` with `.map`/`.prepend`",
15
- category: "Quality",
16
- recommended: true,
17
- url: createLinkToRule("prefer-sample-over-forward-with-mapping"),
18
- },
19
- messages: {
20
- overMap:
21
- "Instead of `forward` with `{{ eventName }}.map` you can use `sample`",
22
- overPrepend:
23
- "Instead of `forward` with `{{ eventName }}.prepend` you can use `sample`",
24
- replaceWithSample: "Replace `forward` with `sample`.",
25
- },
26
- schema: [],
27
- hasSuggestions: true,
28
- },
29
- create(context) {
30
- const importNodes = new Map();
31
- const importedFromEffector = new Map();
32
-
33
- return {
34
- ImportDeclaration(node) {
35
- extractImportedFrom({
36
- importMap: importedFromEffector,
37
- nodeMap: importNodes,
38
- node,
39
- packageName: "effector",
40
- });
41
- },
42
- CallExpression(node) {
43
- if (
44
- method.isNot("forward", {
45
- node,
46
- importMap: importedFromEffector,
47
- })
48
- ) {
49
- return;
50
- }
51
-
52
- const forwardConfig = extractConfig(["from", "to"], { node });
53
-
54
- if (!forwardConfig.from || !forwardConfig.to) {
55
- return;
56
- }
57
-
58
- function checkForMapping({ paramNode, methodName, messageId }) {
59
- if (paramNode.value?.type !== "CallExpression") {
60
- return;
61
- }
62
-
63
- if (paramNode.value?.callee?.property?.name !== methodName) {
64
- return;
65
- }
66
-
67
- const eventNode = traverseNestedObjectNode(
68
- paramNode.value?.callee?.object
69
- );
70
- const eventName = eventNode?.name;
71
-
72
- if (!eventName) {
73
- return;
74
- }
75
-
76
- context.report({
77
- node,
78
- messageId,
79
- data: {
80
- eventName,
81
- },
82
- suggest: [
83
- {
84
- messageId: "replaceWithSample",
85
- *fix(fixer) {
86
- yield* replaceForwardBySample(forwardConfig, {
87
- fixer,
88
- node,
89
- context,
90
- importNodes,
91
- });
92
- },
93
- },
94
- ],
95
- });
96
- }
97
-
98
- checkForMapping({
99
- paramNode: forwardConfig.from,
100
- methodName: "map",
101
- messageId: "overMap",
102
- });
103
- checkForMapping({
104
- paramNode: forwardConfig.to,
105
- methodName: "prepend",
106
- messageId: "overPrepend",
107
- });
108
- },
109
- };
110
- },
111
- };