eslint-plugin-effector 0.2.0 → 0.3.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/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/config/recommended.js +2 -0
- package/index.js +3 -1
- package/package.json +1 -1
- package/rules/enforce-store-naming-convention/enforce-store-naming-convention.js +44 -22
- package/rules/enforce-store-naming-convention/enforce-store-naming-convention.md +34 -1
- package/rules/no-ambiguity-target/no-ambiguity-target.js +71 -0
- package/rules/no-ambiguity-target/no-ambiguity-target.md +12 -0
- package/rules/no-getState/no-getState.js +3 -2
- package/rules/no-useless-methods/no-useless-methods.js +82 -0
- package/rules/no-useless-methods/no-useless-methods.md +14 -0
- package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.md +3 -3
- package/utils/get-corrected-store-name.js +23 -0
- package/utils/get-store-name-convention.js +6 -0
- package/utils/is-store-name-valid.js +22 -0
- package/utils/traverse-parent-by-type.js +13 -0
- package/utils/validate-store-name-convention.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.3.0
|
|
4
|
+
|
|
5
|
+
- Add new rule: `no-useless-methods` ([PR #41](https://github.com/effector/eslint-plugin/pull/41))
|
|
6
|
+
- Add new rule: `no-ambiguity-target` ([PR #42](https://github.com/effector/eslint-plugin/pull/42))
|
|
7
|
+
- Add possibility to configure store's naming convention — suffix of prefix ([PR #37](https://github.com/effector/eslint-plugin/pull/37) by @ilyaryabchinski)
|
|
8
|
+
|
|
3
9
|
## v0.2.0
|
|
4
10
|
|
|
5
11
|
- Add tests against Effector 22
|
package/README.md
CHANGED
|
@@ -45,4 +45,6 @@ To configure individual rules:
|
|
|
45
45
|
- [effector/enforce-effect-naming-convention](/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.md)
|
|
46
46
|
- [effector/no-getState](/rules/no-getState/no-getState.md)
|
|
47
47
|
- [effector/no-unnecessary-duplication](/rules/no-unnecessary-duplication/no-unnecessary-duplication.md)
|
|
48
|
+
- [effector/no-useless-methods](/rules/no-useless-methods/no-useless-methods.md)
|
|
49
|
+
- [effector/no-ambiguity-target](/rules/no-ambiguity-target/no-ambiguity-target.md)
|
|
48
50
|
- [effector/prefer-sample-over-forward-with-mapping](/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.md)
|
package/config/recommended.js
CHANGED
|
@@ -3,7 +3,9 @@ module.exports = {
|
|
|
3
3
|
"effector/enforce-store-naming-convention": "error",
|
|
4
4
|
"effector/enforce-effect-naming-convention": "error",
|
|
5
5
|
"effector/no-getState": "error",
|
|
6
|
+
"effector/no-useless-methods": "error",
|
|
6
7
|
"effector/no-unnecessary-duplication": "warn",
|
|
7
8
|
"effector/prefer-sample-over-forward-with-mapping": "warn",
|
|
9
|
+
"effector/no-ambiguity-target": "warn",
|
|
8
10
|
},
|
|
9
11
|
};
|
package/index.js
CHANGED
|
@@ -5,8 +5,10 @@ module.exports = {
|
|
|
5
5
|
"no-getState": require("./rules/no-getState/no-getState"),
|
|
6
6
|
"no-unnecessary-duplication": require("./rules/no-unnecessary-duplication/no-unnecessary-duplication"),
|
|
7
7
|
"prefer-sample-over-forward-with-mapping": require("./rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping"),
|
|
8
|
+
"no-useless-methods": require("./rules/no-useless-methods/no-useless-methods"),
|
|
9
|
+
"no-ambiguity-target": require("./rules/no-ambiguity-target/no-ambiguity-target"),
|
|
8
10
|
},
|
|
9
11
|
configs: {
|
|
10
12
|
recommended: require("./config/recommended"),
|
|
11
|
-
}
|
|
13
|
+
}
|
|
12
14
|
};
|
package/package.json
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
const {
|
|
2
2
|
extractImportedFromEffector,
|
|
3
3
|
} = require("../../utils/extract-imported-from-effector");
|
|
4
|
+
const { isStoreNameValid } = require("../../utils/is-store-name-valid");
|
|
5
|
+
const { validateStoreNameConvention } = require("../../utils/validate-store-name-convention");
|
|
6
|
+
const { getStoreNameConvention } = require("../../utils/get-store-name-convention");
|
|
7
|
+
const { getCorrectedStoreName } = require("../../utils/get-corrected-store-name");
|
|
4
8
|
|
|
5
9
|
module.exports = {
|
|
6
10
|
meta: {
|
|
7
11
|
type: "problem",
|
|
8
12
|
docs: {
|
|
9
13
|
description:
|
|
10
|
-
|
|
14
|
+
"Enforce $ as a prefix or postfix for any store created by Effector methods",
|
|
11
15
|
category: "Naming",
|
|
12
16
|
recommended: true,
|
|
13
17
|
},
|
|
14
18
|
messages: {
|
|
15
19
|
invalidName:
|
|
16
|
-
|
|
17
|
-
renameStore: 'Rename "{{ storeName }}" to "
|
|
20
|
+
'Store "{{ storeName }}" should be named with {{ storeNameConvention }}, rename it to "{{ correctedStoreName }}"',
|
|
21
|
+
renameStore: 'Rename "{{ storeName }}" to "{{ correctedStoreName }}"',
|
|
18
22
|
},
|
|
19
23
|
schema: [],
|
|
20
24
|
},
|
|
21
25
|
create(context) {
|
|
22
|
-
const parserServices = context
|
|
26
|
+
const { parserServices } = context;
|
|
27
|
+
|
|
28
|
+
validateStoreNameConvention(context);
|
|
29
|
+
|
|
23
30
|
// TypeScript-way
|
|
24
31
|
if (parserServices.hasFullTypeInformation) {
|
|
25
32
|
return {
|
|
@@ -29,8 +36,8 @@ module.exports = {
|
|
|
29
36
|
const type = checker.getTypeAtLocation(originalNode.initializer);
|
|
30
37
|
|
|
31
38
|
const isEffectorStore =
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
type?.symbol?.escapedName === "Store" &&
|
|
40
|
+
type?.symbol?.parent?.escapedName?.includes("effector");
|
|
34
41
|
|
|
35
42
|
if (!isEffectorStore) {
|
|
36
43
|
return;
|
|
@@ -38,11 +45,15 @@ module.exports = {
|
|
|
38
45
|
|
|
39
46
|
const storeName = node.id.name;
|
|
40
47
|
|
|
41
|
-
if (storeName
|
|
48
|
+
if (isStoreNameValid(storeName, context)) {
|
|
42
49
|
return;
|
|
43
50
|
}
|
|
44
51
|
|
|
45
|
-
reportStoreNameConventionViolation({
|
|
52
|
+
reportStoreNameConventionViolation({
|
|
53
|
+
context,
|
|
54
|
+
node,
|
|
55
|
+
storeName
|
|
56
|
+
});
|
|
46
57
|
},
|
|
47
58
|
};
|
|
48
59
|
}
|
|
@@ -68,47 +79,50 @@ module.exports = {
|
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
const resultSavedInVariable =
|
|
71
|
-
|
|
82
|
+
node.parent.type === "VariableDeclarator";
|
|
72
83
|
if (!resultSavedInVariable) {
|
|
73
84
|
continue;
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
const storeName = node.parent.id.name;
|
|
77
|
-
|
|
88
|
+
|
|
89
|
+
if (isStoreNameValid(storeName, context)) {
|
|
78
90
|
continue;
|
|
79
91
|
}
|
|
80
92
|
|
|
81
93
|
reportStoreNameConventionViolation({
|
|
82
94
|
context,
|
|
83
95
|
node: node.parent,
|
|
84
|
-
storeName
|
|
96
|
+
storeName
|
|
85
97
|
});
|
|
86
98
|
return;
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
// Store creation with .map
|
|
90
102
|
if (node.callee?.property?.name === "map") {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
if (!
|
|
103
|
+
const storeNameCreatedFromMap = node.callee?.object?.name;
|
|
104
|
+
|
|
105
|
+
if (!isStoreNameValid(storeNameCreatedFromMap, context)) {
|
|
94
106
|
return;
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
const resultSavedInVariable =
|
|
98
|
-
|
|
110
|
+
node.parent.type === "VariableDeclarator";
|
|
99
111
|
if (!resultSavedInVariable) {
|
|
100
112
|
return;
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
const storeName = node.parent.id.name;
|
|
104
|
-
|
|
116
|
+
|
|
117
|
+
if (isStoreNameValid(storeName, context)) {
|
|
105
118
|
return;
|
|
106
119
|
}
|
|
107
120
|
|
|
121
|
+
|
|
108
122
|
reportStoreNameConventionViolation({
|
|
109
123
|
context,
|
|
110
124
|
node: node.parent,
|
|
111
|
-
storeName
|
|
125
|
+
storeName
|
|
112
126
|
});
|
|
113
127
|
return;
|
|
114
128
|
}
|
|
@@ -116,23 +130,24 @@ module.exports = {
|
|
|
116
130
|
// Store creation in domain
|
|
117
131
|
const STORE_IN_DOMAIN_CREATION_METHODS = ["createStore", "store"];
|
|
118
132
|
if (
|
|
119
|
-
|
|
133
|
+
STORE_IN_DOMAIN_CREATION_METHODS.includes(node.callee?.property?.name)
|
|
120
134
|
) {
|
|
121
135
|
const resultSavedInVariable =
|
|
122
|
-
|
|
136
|
+
node.parent.type === "VariableDeclarator";
|
|
123
137
|
if (!resultSavedInVariable) {
|
|
124
138
|
return;
|
|
125
139
|
}
|
|
126
140
|
|
|
127
141
|
const storeName = node.parent.id.name;
|
|
128
|
-
|
|
142
|
+
|
|
143
|
+
if (isStoreNameValid(storeName, context)) {
|
|
129
144
|
return;
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
reportStoreNameConventionViolation({
|
|
133
148
|
context,
|
|
134
149
|
node: node.parent,
|
|
135
|
-
storeName
|
|
150
|
+
storeName
|
|
136
151
|
});
|
|
137
152
|
return;
|
|
138
153
|
}
|
|
@@ -142,20 +157,27 @@ module.exports = {
|
|
|
142
157
|
};
|
|
143
158
|
|
|
144
159
|
function reportStoreNameConventionViolation({ context, node, storeName }) {
|
|
160
|
+
|
|
161
|
+
const storeNameConvention = getStoreNameConvention(context);
|
|
162
|
+
const correctedStoreName = getCorrectedStoreName(storeName, context);
|
|
163
|
+
|
|
145
164
|
context.report({
|
|
146
165
|
node,
|
|
147
166
|
messageId: "invalidName",
|
|
148
167
|
data: {
|
|
149
168
|
storeName,
|
|
169
|
+
correctedStoreName,
|
|
170
|
+
storeNameConvention
|
|
150
171
|
},
|
|
151
172
|
suggest: [
|
|
152
173
|
{
|
|
153
174
|
messageId: "renameStore",
|
|
154
175
|
data: { storeName },
|
|
155
176
|
fix(fixer) {
|
|
156
|
-
return fixer.
|
|
177
|
+
return fixer.replaceTextRange(node.id.range, correctedStoreName);
|
|
157
178
|
},
|
|
158
179
|
},
|
|
159
180
|
],
|
|
160
181
|
});
|
|
161
182
|
}
|
|
183
|
+
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
# effector/enforce-store-naming-convention
|
|
2
2
|
|
|
3
|
-
Enforcing naming conventions helps keep the codebase consistent, and reduces overhead when thinking about how to name a variable with store.
|
|
3
|
+
Enforcing naming conventions helps keep the codebase consistent, and reduces overhead when thinking about how to name a variable with store. Depending on the configuration your stores should be distinguished by a prefix or a postfix $. Enforces prefix convention by default.
|
|
4
4
|
|
|
5
|
+
## Prefix convention
|
|
6
|
+
When configured as:
|
|
7
|
+
```js
|
|
8
|
+
module.exports = {
|
|
9
|
+
rules: {
|
|
10
|
+
"effector/enforce-store-naming-convention": "error",
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
```
|
|
14
|
+
Prefix convention will be enforced:
|
|
5
15
|
```ts
|
|
6
16
|
// 👍 nice name
|
|
7
17
|
const $name = createStore(null);
|
|
@@ -9,3 +19,26 @@ const $name = createStore(null);
|
|
|
9
19
|
// 👎 bad name
|
|
10
20
|
const name = createStrore(null);
|
|
11
21
|
```
|
|
22
|
+
## Postfix convention
|
|
23
|
+
|
|
24
|
+
When configured as:
|
|
25
|
+
```js
|
|
26
|
+
module.exports = {
|
|
27
|
+
rules: {
|
|
28
|
+
"effector/enforce-store-naming-convention": "error",
|
|
29
|
+
},
|
|
30
|
+
settings: {
|
|
31
|
+
effector: {
|
|
32
|
+
storeNameConvention: "postfix"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
Postfix convention will be enforced:
|
|
38
|
+
```ts
|
|
39
|
+
// 👍 nice name
|
|
40
|
+
const name$ = createStore(null);
|
|
41
|
+
|
|
42
|
+
// 👎 bad name
|
|
43
|
+
const name = createStrore(null);
|
|
44
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const {
|
|
2
|
+
extractImportedFromEffector,
|
|
3
|
+
} = require("../../utils/extract-imported-from-effector");
|
|
4
|
+
const { traverseParentByType } = require("../../utils/traverse-parent-by-type");
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Forbids ambiguity targets in `sample` and `guard`",
|
|
11
|
+
category: "Quality",
|
|
12
|
+
recommended: true,
|
|
13
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
ambiguityTarget:
|
|
16
|
+
"Method `{{ methodName }}` returns `target` and assigns the result to a variable. Consider removing one of them.",
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
const importedFromEffector = new Map();
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
ImportDeclaration(node) {
|
|
25
|
+
extractImportedFromEffector(importedFromEffector, node);
|
|
26
|
+
},
|
|
27
|
+
CallExpression(node) {
|
|
28
|
+
const POSSIBLE_USELESS_METHODS = ["sample", "guard"];
|
|
29
|
+
for (const method of POSSIBLE_USELESS_METHODS) {
|
|
30
|
+
const localMethod = importedFromEffector.get(method);
|
|
31
|
+
if (!localMethod) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const isEffectorMethod = node?.callee?.name === localMethod;
|
|
36
|
+
if (!isEffectorMethod) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const configHasTarget = node?.arguments?.[0]?.properties?.some(
|
|
41
|
+
(prop) => prop?.key.name === "target"
|
|
42
|
+
);
|
|
43
|
+
if (!configHasTarget) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const resultAssignedInVariable = traverseParentByType(
|
|
48
|
+
node,
|
|
49
|
+
"VariableDeclarator"
|
|
50
|
+
);
|
|
51
|
+
const resultPartOfChain = traverseParentByType(
|
|
52
|
+
node,
|
|
53
|
+
"ObjectExpression"
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (resultAssignedInVariable || resultPartOfChain) {
|
|
57
|
+
context.report({
|
|
58
|
+
node,
|
|
59
|
+
messageId: "ambiguityTarget",
|
|
60
|
+
data: {
|
|
61
|
+
methodName: node?.callee?.name,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# effector/no-ambiguity-target
|
|
2
|
+
|
|
3
|
+
Call of `gaurd`/`sample` with `target` and variable assignment is ambiguity. One of them should be omitted from source code.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// 👎 should be rewritten
|
|
7
|
+
const result = guard({ clock: trigger, filter: Boolean, target });
|
|
8
|
+
|
|
9
|
+
// 👍 makes sense
|
|
10
|
+
guard({ clock: trigger, filter: Boolean, target });
|
|
11
|
+
const result = target;
|
|
12
|
+
```
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const {
|
|
2
2
|
traverseNestedObjectNode,
|
|
3
3
|
} = require("../../utils/traverse-nested-object-node");
|
|
4
|
+
const { isStoreNameValid } = require("../../utils/is-store-name-valid");
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
meta: {
|
|
@@ -17,7 +18,7 @@ module.exports = {
|
|
|
17
18
|
schema: [],
|
|
18
19
|
},
|
|
19
20
|
create(context) {
|
|
20
|
-
const parserServices = context
|
|
21
|
+
const { parserServices } = context;
|
|
21
22
|
|
|
22
23
|
return {
|
|
23
24
|
CallExpression(node) {
|
|
@@ -51,7 +52,7 @@ module.exports = {
|
|
|
51
52
|
}
|
|
52
53
|
// JavaScript-way
|
|
53
54
|
else {
|
|
54
|
-
const isEffectorStore = objectName
|
|
55
|
+
const isEffectorStore = isStoreNameValid(objectName, context);
|
|
55
56
|
if (!isEffectorStore) {
|
|
56
57
|
return;
|
|
57
58
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const {
|
|
2
|
+
extractImportedFromEffector,
|
|
3
|
+
} = require("../../utils/extract-imported-from-effector");
|
|
4
|
+
const { traverseParentByType } = require("../../utils/traverse-parent-by-type");
|
|
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
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
uselessMethod:
|
|
16
|
+
"Method `{{ methodName }}` does nothing in this case. You should assign the result to variable or pass `target` to it.",
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
},
|
|
20
|
+
create(context) {
|
|
21
|
+
const importedFromEffector = new Map();
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
ImportDeclaration(node) {
|
|
25
|
+
extractImportedFromEffector(importedFromEffector, node);
|
|
26
|
+
},
|
|
27
|
+
CallExpression(node) {
|
|
28
|
+
const POSSIBLE_USELESS_METHODS = ["sample", "guard"];
|
|
29
|
+
for (const method of POSSIBLE_USELESS_METHODS) {
|
|
30
|
+
const localMethod = importedFromEffector.get(method);
|
|
31
|
+
if (!localMethod) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const isEffectorMethod = node?.callee?.name === localMethod;
|
|
36
|
+
if (!isEffectorMethod) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const resultAssignedInVariable = traverseParentByType(
|
|
41
|
+
node,
|
|
42
|
+
"VariableDeclarator"
|
|
43
|
+
);
|
|
44
|
+
if (resultAssignedInVariable) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resultReturnedFromFactory = traverseParentByType(
|
|
49
|
+
node,
|
|
50
|
+
"ReturnStatement"
|
|
51
|
+
);
|
|
52
|
+
if (resultReturnedFromFactory) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const resultPartOfChain = traverseParentByType(
|
|
57
|
+
node,
|
|
58
|
+
"ObjectExpression"
|
|
59
|
+
);
|
|
60
|
+
if (resultPartOfChain) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const configHasTarget = node?.arguments?.[0]?.properties?.some(
|
|
65
|
+
(prop) => prop?.key.name === "target"
|
|
66
|
+
);
|
|
67
|
+
if (configHasTarget) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
context.report({
|
|
72
|
+
node,
|
|
73
|
+
messageId: "uselessMethod",
|
|
74
|
+
data: {
|
|
75
|
+
methodName: node?.callee?.name,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# effector/no-useless-methods
|
|
2
|
+
|
|
3
|
+
Call of `gaurd`/`sample` without `target` or variable assignment is useless. It can be omitted from source code.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// 👎 can be omitted
|
|
7
|
+
guard({ clock: trigger, filter: Boolean });
|
|
8
|
+
|
|
9
|
+
// 👍 makes sense
|
|
10
|
+
const target1 = guard({ clock: trigger, filter: Boolean });
|
|
11
|
+
|
|
12
|
+
// 👍 make sense too
|
|
13
|
+
guard({ clock: trigger, filter: Boolean, target: target2 });
|
|
14
|
+
```
|
package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.md
CHANGED
|
@@ -8,20 +8,20 @@ const eventTwo = createEvent();
|
|
|
8
8
|
|
|
9
9
|
// 👎 looks weird
|
|
10
10
|
forward({
|
|
11
|
-
from: eventOne.map((items) =>
|
|
11
|
+
from: eventOne.map((items) => items.length),
|
|
12
12
|
to: eventTwo,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
// 👎 weird too
|
|
16
16
|
forward({
|
|
17
17
|
from: eventOne,
|
|
18
|
-
to: eventTwo.prepend((items) =>
|
|
18
|
+
to: eventTwo.prepend((items) => items.length),
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
// 👍 better
|
|
22
22
|
sample({
|
|
23
23
|
source: eventOne,
|
|
24
|
-
fn: (items) =>
|
|
24
|
+
fn: (items) => items.length,
|
|
25
25
|
target: eventTwo,
|
|
26
26
|
});
|
|
27
27
|
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { getStoreNameConvention } = require("./get-store-name-convention");
|
|
2
|
+
|
|
3
|
+
function getCorrectedStoreName(storeName, context) {
|
|
4
|
+
const storeNameConvention = getStoreNameConvention(context);
|
|
5
|
+
|
|
6
|
+
// handle edge case
|
|
7
|
+
if (storeName.startsWith("$") && storeName.endsWith("$")) {
|
|
8
|
+
|
|
9
|
+
if (storeNameConvention === "prefix") {
|
|
10
|
+
return `$${storeName.slice(0, -1)}`;
|
|
11
|
+
} else {
|
|
12
|
+
return `${storeName.slice(1)}$`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const correctedStoreName = storeNameConvention === "prefix"
|
|
17
|
+
? `$${storeName}`
|
|
18
|
+
: `${storeName}$`;
|
|
19
|
+
|
|
20
|
+
return correctedStoreName;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { getCorrectedStoreName };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { getStoreNameConvention } = require("./get-store-name-convention");
|
|
2
|
+
|
|
3
|
+
function isStoreNameValid(storeName, context) {
|
|
4
|
+
const storeNameConvention = getStoreNameConvention(context);
|
|
5
|
+
|
|
6
|
+
// validate edge case
|
|
7
|
+
if (storeName?.startsWith("$") && storeName?.endsWith("$")) {
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (storeNameConvention === "prefix" && storeName?.startsWith("$")) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (storeNameConvention === "postfix" && storeName?.endsWith("$")) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = { isStoreNameValid };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { getStoreNameConvention } = require("./get-store-name-convention");
|
|
2
|
+
|
|
3
|
+
function validateStoreNameConvention(context) {
|
|
4
|
+
const storeNameConvention = getStoreNameConvention(context);
|
|
5
|
+
|
|
6
|
+
if (storeNameConvention !== "prefix" && storeNameConvention !== "postfix") {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"Invalid Configuration of effector-plugin-eslint/enforce-store-naming-convention. The value should be equal to prefix or postfix."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = { validateStoreNameConvention };
|