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.
- package/README.md +24 -37
- package/dist/index.cjs +1259 -0
- package/dist/index.d.cts +177 -0
- package/dist/index.d.mts +178 -0
- package/dist/index.mjs +1233 -0
- package/package.json +71 -17
- package/.nvmrc +0 -1
- package/config/future.js +0 -7
- package/config/patronum.js +0 -5
- package/config/react.js +0 -7
- package/config/recommended.js +0 -15
- package/config/scope.js +0 -6
- package/index.js +0 -31
- package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.js +0 -143
- package/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.js +0 -122
- package/rules/enforce-store-naming-convention/enforce-store-naming-convention.js +0 -205
- package/rules/keep-options-order/config.js +0 -3
- package/rules/keep-options-order/keep-options-order.js +0 -107
- package/rules/mandatory-scope-binding/mandatory-scope-binding.js +0 -81
- package/rules/no-ambiguity-target/no-ambiguity-target.js +0 -74
- package/rules/no-duplicate-clock-or-source-array-values/no-duplicate-clock-or-source-array-values.js +0 -124
- package/rules/no-duplicate-on/no-duplicate-on.js +0 -137
- package/rules/no-forward/no-forward.js +0 -73
- package/rules/no-getState/no-getState.js +0 -50
- package/rules/no-guard/no-guard.js +0 -78
- package/rules/no-patronum-debug/no-patronum-debug.js +0 -133
- package/rules/no-unnecessary-combination/no-unnecessary-combination.js +0 -88
- package/rules/no-unnecessary-duplication/no-unnecessary-duplication.js +0 -115
- package/rules/no-useless-methods/no-useless-methods.js +0 -93
- package/rules/no-watch/no-watch.js +0 -61
- package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.js +0 -111
- package/rules/prefer-useUnit/prefer-useUnit.js +0 -56
- package/rules/require-pickup-in-persist/require-pickup-in-persist.js +0 -47
- package/rules/strict-effect-handlers/strict-effect-handlers.js +0 -76
- package/utils/are-nodes-same-in-text.js +0 -22
- package/utils/builders.js +0 -19
- package/utils/create-link-to-rule.js +0 -5
- package/utils/extract-config.js +0 -26
- package/utils/extract-imported-from.js +0 -18
- package/utils/get-corrected-store-name.js +0 -45
- package/utils/get-nested-object-name.js +0 -18
- package/utils/get-store-name-convention.js +0 -6
- package/utils/is.js +0 -39
- package/utils/method.js +0 -23
- package/utils/naming.js +0 -47
- package/utils/node-is-type.js +0 -5
- package/utils/node-type-is.js +0 -106
- package/utils/react.js +0 -214
- package/utils/read-example.js +0 -63
- package/utils/replace-by-sample.js +0 -98
- package/utils/traverse-nested-object-node.js +0 -9
- package/utils/traverse-parent-by-type.js +0 -15
- package/utils/validate-store-name-convention.js +0 -13
package/package.json
CHANGED
|
@@ -1,32 +1,86 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-effector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Enforcing best practices for Effector",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
7
|
-
"eslint-plugin",
|
|
8
7
|
"eslintplugin",
|
|
8
|
+
"eslint-plugin",
|
|
9
9
|
"effector"
|
|
10
10
|
],
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
11
|
+
"homepage": "https://eslint.effector.dev",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/effector/eslint-plugin"
|
|
15
|
+
},
|
|
14
16
|
"license": "MIT",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
17
|
+
"author": "Igor Kamyshev <igor@kamyshev.me>",
|
|
18
|
+
"contributors": [
|
|
19
|
+
{
|
|
20
|
+
"name": "Mikhail Kireev",
|
|
21
|
+
"url": "https://github.com/kireevmp"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/index.d.mts",
|
|
29
|
+
"default": "./dist/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"require": {
|
|
32
|
+
"types": "./dist/index.d.cts",
|
|
33
|
+
"default": "./dist/index.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
18
37
|
},
|
|
19
|
-
"
|
|
20
|
-
|
|
38
|
+
"main": "dist/index.cjs",
|
|
39
|
+
"module": "./dist/index.mjs",
|
|
40
|
+
"types": "./dist/index.d.cts",
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@typescript-eslint/type-utils": "^8.52.0",
|
|
46
|
+
"@typescript-eslint/utils": "^8.52.0",
|
|
47
|
+
"esquery": "^1.7.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@eslint/js": "^9.39.2",
|
|
51
|
+
"@types/esquery": "^1.5.4",
|
|
52
|
+
"@types/estree": "^1.0.8",
|
|
53
|
+
"@types/node": "^25.0.6",
|
|
54
|
+
"@types/react": "^19.2.8",
|
|
55
|
+
"@typescript-eslint/rule-tester": "^8.52.0",
|
|
56
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
57
|
+
"effector": "^23.4.4",
|
|
58
|
+
"effector-react": "^23.3.0",
|
|
59
|
+
"eslint": "^9.39.2",
|
|
60
|
+
"eslint-config-prettier": "^10.1.8",
|
|
61
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
62
|
+
"eslint-plugin-import-x": "^4.16.1",
|
|
63
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
64
|
+
"prettier": "^3.7.4",
|
|
65
|
+
"prettier-plugin-embed": "^0.5.1",
|
|
66
|
+
"tsdown": "0.19.0",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"typescript-eslint": "^8.52.0",
|
|
69
|
+
"vitepress": "2.0.0-alpha.15",
|
|
70
|
+
"vitest": "^4.0.16"
|
|
21
71
|
},
|
|
22
72
|
"peerDependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
73
|
+
"eslint": "^8.57.0 || ^9.0.0",
|
|
74
|
+
"typescript": ">=5.0.0"
|
|
25
75
|
},
|
|
26
|
-
"
|
|
27
|
-
"
|
|
76
|
+
"engines": {
|
|
77
|
+
"node": "^20.9.0 || >=21.1.0"
|
|
28
78
|
},
|
|
29
|
-
"
|
|
30
|
-
"
|
|
79
|
+
"scripts": {
|
|
80
|
+
"build": "tsdown",
|
|
81
|
+
"docs:build": "vitepress build docs",
|
|
82
|
+
"docs:dev": "vitepress dev docs",
|
|
83
|
+
"lint": "eslint",
|
|
84
|
+
"test": "vitest run"
|
|
31
85
|
}
|
|
32
|
-
}
|
|
86
|
+
}
|
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
18.12
|
package/config/future.js
DELETED
package/config/patronum.js
DELETED
package/config/react.js
DELETED
package/config/recommended.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
rules: {
|
|
3
|
-
"effector/enforce-store-naming-convention": "error",
|
|
4
|
-
"effector/enforce-effect-naming-convention": "error",
|
|
5
|
-
"effector/no-getState": "error",
|
|
6
|
-
"effector/no-useless-methods": "error",
|
|
7
|
-
"effector/no-unnecessary-duplication": "warn",
|
|
8
|
-
"effector/prefer-sample-over-forward-with-mapping": "warn",
|
|
9
|
-
"effector/no-ambiguity-target": "warn",
|
|
10
|
-
"effector/no-watch": "warn",
|
|
11
|
-
"effector/no-unnecessary-combination": "warn",
|
|
12
|
-
"effector/no-duplicate-on": "error",
|
|
13
|
-
"effector/keep-options-order": "warn",
|
|
14
|
-
},
|
|
15
|
-
};
|
package/config/scope.js
DELETED
package/index.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
rules: {
|
|
3
|
-
"enforce-store-naming-convention": require("./rules/enforce-store-naming-convention/enforce-store-naming-convention"),
|
|
4
|
-
"enforce-effect-naming-convention": require("./rules/enforce-effect-naming-convention/enforce-effect-naming-convention"),
|
|
5
|
-
"no-getState": require("./rules/no-getState/no-getState"),
|
|
6
|
-
"no-unnecessary-duplication": require("./rules/no-unnecessary-duplication/no-unnecessary-duplication"),
|
|
7
|
-
"prefer-sample-over-forward-with-mapping": require("./rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping"),
|
|
8
|
-
"no-duplicate-clock-or-source-array-values": require("./rules/no-duplicate-clock-or-source-array-values/no-duplicate-clock-or-source-array-values"),
|
|
9
|
-
"no-useless-methods": require("./rules/no-useless-methods/no-useless-methods"),
|
|
10
|
-
"no-ambiguity-target": require("./rules/no-ambiguity-target/no-ambiguity-target"),
|
|
11
|
-
"no-watch": require("./rules/no-watch/no-watch"),
|
|
12
|
-
"no-unnecessary-combination": require("./rules/no-unnecessary-combination/no-unnecessary-combination"),
|
|
13
|
-
"no-duplicate-on": require("./rules/no-duplicate-on/no-duplicate-on"),
|
|
14
|
-
"strict-effect-handlers": require("./rules/strict-effect-handlers/strict-effect-handlers"),
|
|
15
|
-
"enforce-gate-naming-convention": require("./rules/enforce-gate-naming-convention/enforce-gate-naming-convention"),
|
|
16
|
-
"keep-options-order": require("./rules/keep-options-order/keep-options-order"),
|
|
17
|
-
"no-forward": require("./rules/no-forward/no-forward"),
|
|
18
|
-
"no-guard": require("./rules/no-guard/no-guard"),
|
|
19
|
-
"mandatory-scope-binding": require("./rules/mandatory-scope-binding/mandatory-scope-binding"),
|
|
20
|
-
"prefer-useUnit": require("./rules/prefer-useUnit/prefer-useUnit"),
|
|
21
|
-
"require-pickup-in-persist": require("./rules/require-pickup-in-persist/require-pickup-in-persist"),
|
|
22
|
-
"no-patronum-debug": require("./rules/no-patronum-debug/no-patronum-debug"),
|
|
23
|
-
},
|
|
24
|
-
configs: {
|
|
25
|
-
recommended: require("./config/recommended"),
|
|
26
|
-
scope: require("./config/scope"),
|
|
27
|
-
react: require("./config/react"),
|
|
28
|
-
future: require("./config/future"),
|
|
29
|
-
patronum: require("./config/patronum"),
|
|
30
|
-
},
|
|
31
|
-
};
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
const { ESLintUtils } = require("@typescript-eslint/utils");
|
|
2
|
-
|
|
3
|
-
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
4
|
-
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
5
|
-
const { nodeTypeIs } = require("../../utils/node-type-is");
|
|
6
|
-
const { namingOf } = require("../../utils/naming");
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
meta: {
|
|
10
|
-
type: "problem",
|
|
11
|
-
docs: {
|
|
12
|
-
description:
|
|
13
|
-
"Enforce Fx as a suffix for any effect created by Effector methods",
|
|
14
|
-
category: "Naming",
|
|
15
|
-
recommended: true,
|
|
16
|
-
url: createLinkToRule("enforce-effect-naming-convention"),
|
|
17
|
-
},
|
|
18
|
-
messages: {
|
|
19
|
-
invalidName:
|
|
20
|
-
'Effect "{{ effectName }}" should be named with suffix, rename it to "{{ effectName }}Fx"',
|
|
21
|
-
renameEffect: 'Rename "{{ effectName }}" to "{{ effectName }}Fx"',
|
|
22
|
-
},
|
|
23
|
-
schema: [],
|
|
24
|
-
hasSuggestions: true,
|
|
25
|
-
},
|
|
26
|
-
create(context) {
|
|
27
|
-
let parserServices;
|
|
28
|
-
try {
|
|
29
|
-
parserServices = ESLintUtils.getParserServices(context);
|
|
30
|
-
} catch (err) {
|
|
31
|
-
// no types information
|
|
32
|
-
}
|
|
33
|
-
// TypeScript-way
|
|
34
|
-
if (parserServices?.program) {
|
|
35
|
-
return {
|
|
36
|
-
VariableDeclarator(node) {
|
|
37
|
-
const isEffectorEffect = nodeTypeIs.effect({
|
|
38
|
-
node,
|
|
39
|
-
context,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!isEffectorEffect) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const effectName = node.id.name;
|
|
47
|
-
|
|
48
|
-
if (namingOf.effect.isInvalid({ name: effectName })) {
|
|
49
|
-
reportEffectNameConventionViolation({ context, node, effectName });
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// JavaScript-way
|
|
56
|
-
const importedFromEffector = new Map();
|
|
57
|
-
return {
|
|
58
|
-
ImportDeclaration(node) {
|
|
59
|
-
extractImportedFrom({
|
|
60
|
-
importMap: importedFromEffector,
|
|
61
|
-
node,
|
|
62
|
-
packageName: "effector",
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
CallExpression(node) {
|
|
66
|
-
// Effect creation with method
|
|
67
|
-
const EFFECT_CREATION_METHODS = ["createEffect", "attach"];
|
|
68
|
-
for (const method of EFFECT_CREATION_METHODS) {
|
|
69
|
-
const localMethod = importedFromEffector.get(method);
|
|
70
|
-
if (!localMethod) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const isEffectorEffectCreation = node.callee.name === localMethod;
|
|
75
|
-
if (!isEffectorEffectCreation) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const resultSavedInVariable =
|
|
80
|
-
node.parent.type === "VariableDeclarator";
|
|
81
|
-
if (!resultSavedInVariable) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const effectName = node.parent.id.name;
|
|
86
|
-
if (namingOf.effect.isValid({ name: effectName })) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
reportEffectNameConventionViolation({
|
|
91
|
-
context,
|
|
92
|
-
node: node.parent,
|
|
93
|
-
effectName,
|
|
94
|
-
});
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Effect creation in domain
|
|
99
|
-
const STORE_IN_DOMAIN_CREATION_METHODS = ["createEffect", "effect"];
|
|
100
|
-
if (
|
|
101
|
-
STORE_IN_DOMAIN_CREATION_METHODS.includes(node.callee?.property?.name)
|
|
102
|
-
) {
|
|
103
|
-
const resultSavedInVariable =
|
|
104
|
-
node.parent.type === "VariableDeclarator";
|
|
105
|
-
if (!resultSavedInVariable) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const effectName = node.parent.id.name;
|
|
110
|
-
if (namingOf.effect.isValid({ name: effectName })) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
reportEffectNameConventionViolation({
|
|
115
|
-
context,
|
|
116
|
-
node: node.parent,
|
|
117
|
-
effectName,
|
|
118
|
-
});
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
function reportEffectNameConventionViolation({ context, node, effectName }) {
|
|
127
|
-
context.report({
|
|
128
|
-
node,
|
|
129
|
-
messageId: "invalidName",
|
|
130
|
-
data: {
|
|
131
|
-
effectName,
|
|
132
|
-
},
|
|
133
|
-
suggest: [
|
|
134
|
-
{
|
|
135
|
-
messageId: "renameEffect",
|
|
136
|
-
data: { effectName },
|
|
137
|
-
fix(fixer) {
|
|
138
|
-
return fixer.insertTextAfterRange(node.id.range, "Fx");
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
});
|
|
143
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
const { ESLintUtils } = require("@typescript-eslint/utils");
|
|
2
|
-
|
|
3
|
-
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
4
|
-
const { nodeTypeIs } = require("../../utils/node-type-is");
|
|
5
|
-
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
6
|
-
const { namingOf } = require("../../utils/naming");
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
meta: {
|
|
10
|
-
type: "problem",
|
|
11
|
-
docs: {
|
|
12
|
-
description: "Enforce first capital letter for gate naming",
|
|
13
|
-
category: "Naming",
|
|
14
|
-
recommended: true,
|
|
15
|
-
url: createLinkToRule("enforce-gate-naming-convention"),
|
|
16
|
-
},
|
|
17
|
-
messages: {
|
|
18
|
-
invalidName:
|
|
19
|
-
'Gate "{{ gateName }}" should be named with first capital letter, rename it to "{{ correctedGateName }}"',
|
|
20
|
-
renameGate: 'Rename "{{ gateName }}" to "{{ correctedGateName }}"',
|
|
21
|
-
},
|
|
22
|
-
schema: [],
|
|
23
|
-
hasSuggestions: true,
|
|
24
|
-
},
|
|
25
|
-
create(context) {
|
|
26
|
-
let parserServices;
|
|
27
|
-
try {
|
|
28
|
-
parserServices = ESLintUtils.getParserServices(context);
|
|
29
|
-
} catch (err) {
|
|
30
|
-
// no types information
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// TypeScript-way
|
|
34
|
-
if (parserServices?.program) {
|
|
35
|
-
return {
|
|
36
|
-
VariableDeclarator(node) {
|
|
37
|
-
const isEffectorGate = nodeTypeIs.gate({
|
|
38
|
-
node,
|
|
39
|
-
context,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!isEffectorGate) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const gateName = node.id.name;
|
|
47
|
-
|
|
48
|
-
if (namingOf.gate.isInvalid({ name: gateName })) {
|
|
49
|
-
reportGateNameConventionViolation({ context, node, gateName });
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// JavaScript-way
|
|
56
|
-
const importedFromEffectorReact = new Map();
|
|
57
|
-
return {
|
|
58
|
-
ImportDeclaration(node) {
|
|
59
|
-
extractImportedFrom({
|
|
60
|
-
importMap: importedFromEffectorReact,
|
|
61
|
-
node,
|
|
62
|
-
packageName: "effector-react",
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
CallExpression(node) {
|
|
66
|
-
// Effect creation with method
|
|
67
|
-
const GATE_CREATION_METHODS = ["createGate"];
|
|
68
|
-
for (const method of GATE_CREATION_METHODS) {
|
|
69
|
-
const localMethod = importedFromEffectorReact.get(method);
|
|
70
|
-
if (!localMethod) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const isEffectorGateCreation = node.callee.name === localMethod;
|
|
75
|
-
if (!isEffectorGateCreation) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const resultSavedInVariable =
|
|
80
|
-
node.parent.type === "VariableDeclarator";
|
|
81
|
-
if (!resultSavedInVariable) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const gateName = node.parent.id.name;
|
|
86
|
-
if (namingOf.gate.isValid({ name: gateName })) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
reportGateNameConventionViolation({
|
|
91
|
-
context,
|
|
92
|
-
node: node.parent,
|
|
93
|
-
gateName,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
function reportGateNameConventionViolation({ context, node, gateName }) {
|
|
102
|
-
const [firstChar, ...restChars] = gateName.split("");
|
|
103
|
-
const correctedGateName = [firstChar.toUpperCase(), ...restChars].join("");
|
|
104
|
-
|
|
105
|
-
context.report({
|
|
106
|
-
node,
|
|
107
|
-
messageId: "invalidName",
|
|
108
|
-
data: {
|
|
109
|
-
gateName,
|
|
110
|
-
correctedGateName,
|
|
111
|
-
},
|
|
112
|
-
suggest: [
|
|
113
|
-
{
|
|
114
|
-
messageId: "renameGate",
|
|
115
|
-
data: { gateName, correctedGateName },
|
|
116
|
-
fix(fixer) {
|
|
117
|
-
return fixer.replaceTextRange(node.id.range, correctedGateName);
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
});
|
|
122
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
const { ESLintUtils } = require("@typescript-eslint/utils");
|
|
2
|
-
|
|
3
|
-
const { extractImportedFrom } = require("../../utils/extract-imported-from");
|
|
4
|
-
const { namingOf } = require("../../utils/naming");
|
|
5
|
-
const {
|
|
6
|
-
validateStoreNameConvention,
|
|
7
|
-
} = require("../../utils/validate-store-name-convention");
|
|
8
|
-
const {
|
|
9
|
-
getStoreNameConvention,
|
|
10
|
-
} = require("../../utils/get-store-name-convention");
|
|
11
|
-
const {
|
|
12
|
-
getCorrectedStoreName,
|
|
13
|
-
} = require("../../utils/get-corrected-store-name");
|
|
14
|
-
const { createLinkToRule } = require("../../utils/create-link-to-rule");
|
|
15
|
-
const { nodeTypeIs } = require("../../utils/node-type-is");
|
|
16
|
-
const { traverseParentByType } = require("../../utils/traverse-parent-by-type");
|
|
17
|
-
|
|
18
|
-
module.exports = {
|
|
19
|
-
meta: {
|
|
20
|
-
type: "problem",
|
|
21
|
-
docs: {
|
|
22
|
-
description:
|
|
23
|
-
"Enforce $ as a prefix or postfix for any store created by Effector methods",
|
|
24
|
-
category: "Naming",
|
|
25
|
-
recommended: true,
|
|
26
|
-
url: createLinkToRule("enforce-store-naming-convention"),
|
|
27
|
-
},
|
|
28
|
-
messages: {
|
|
29
|
-
invalidName:
|
|
30
|
-
'Store "{{ storeName }}" should be named with {{ storeNameConvention }}, rename it to "{{ correctedStoreName }}"',
|
|
31
|
-
renameStore: 'Rename "{{ storeName }}" to "{{ correctedStoreName }}"',
|
|
32
|
-
},
|
|
33
|
-
schema: [],
|
|
34
|
-
hasSuggestions: true,
|
|
35
|
-
},
|
|
36
|
-
create(context) {
|
|
37
|
-
let parserServices;
|
|
38
|
-
try {
|
|
39
|
-
parserServices = ESLintUtils.getParserServices(context);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
// no types information
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
validateStoreNameConvention(context);
|
|
45
|
-
|
|
46
|
-
// TypeScript-way
|
|
47
|
-
if (parserServices?.program) {
|
|
48
|
-
return {
|
|
49
|
-
VariableDeclarator(node) {
|
|
50
|
-
const isEffectorStore = nodeTypeIs.store({
|
|
51
|
-
node,
|
|
52
|
-
context,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
if (!isEffectorStore) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const storeName = node.id.name;
|
|
60
|
-
|
|
61
|
-
if (namingOf.store.isInvalid({ name: storeName, context })) {
|
|
62
|
-
reportStoreNameConventionViolation({
|
|
63
|
-
context,
|
|
64
|
-
node,
|
|
65
|
-
storeName,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// JavaScript-way
|
|
73
|
-
const importedFromEffector = new Map();
|
|
74
|
-
return {
|
|
75
|
-
ImportDeclaration(node) {
|
|
76
|
-
extractImportedFrom({
|
|
77
|
-
importMap: importedFromEffector,
|
|
78
|
-
node,
|
|
79
|
-
packageName: "effector",
|
|
80
|
-
});
|
|
81
|
-
},
|
|
82
|
-
CallExpression(node) {
|
|
83
|
-
// Store creation with method
|
|
84
|
-
const STORE_CREATION_METHODS = ["createStore", "restore", "combine"];
|
|
85
|
-
for (const method of STORE_CREATION_METHODS) {
|
|
86
|
-
const localMethod = importedFromEffector.get(method);
|
|
87
|
-
if (!localMethod) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const isEffectorStoreCreation = node.callee.name === localMethod;
|
|
92
|
-
if (!isEffectorStoreCreation) {
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const parentNode = traverseParentByType(node, "VariableDeclarator", {
|
|
97
|
-
stopOnTypes: ["Program", "BlockStatement", "Property"],
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const resultSavedInVariable =
|
|
101
|
-
parentNode?.type === "VariableDeclarator";
|
|
102
|
-
if (!resultSavedInVariable) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const storeName = parentNode.id.name;
|
|
107
|
-
if (namingOf.store.isValid({ name: storeName, context })) {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
reportStoreNameConventionViolation({
|
|
112
|
-
context,
|
|
113
|
-
node: parentNode,
|
|
114
|
-
storeName,
|
|
115
|
-
});
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Store creation with .map
|
|
120
|
-
if (node.callee?.property?.name === "map") {
|
|
121
|
-
const storeNameCreatedFromMap = node.callee?.object?.name;
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
namingOf.store.isInvalid({ name: storeNameCreatedFromMap, context })
|
|
125
|
-
) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const resultSavedInVariable =
|
|
130
|
-
node.parent.type === "VariableDeclarator";
|
|
131
|
-
|
|
132
|
-
if (!resultSavedInVariable) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const storeName = node.parent.id.name;
|
|
137
|
-
|
|
138
|
-
if (namingOf.store.isValid({ name: storeName, context })) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
reportStoreNameConventionViolation({
|
|
143
|
-
context,
|
|
144
|
-
node: node.parent,
|
|
145
|
-
storeName,
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Store creation in domain
|
|
151
|
-
const STORE_IN_DOMAIN_CREATION_METHODS = ["createStore", "store"];
|
|
152
|
-
if (
|
|
153
|
-
STORE_IN_DOMAIN_CREATION_METHODS.includes(node.callee?.property?.name)
|
|
154
|
-
) {
|
|
155
|
-
const parentNode = traverseParentByType(node, "VariableDeclarator", {
|
|
156
|
-
stopOnTypes: ["Program", "BlockStatement", "Property"],
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
const resultSavedInVariable =
|
|
160
|
-
parentNode?.type === "VariableDeclarator";
|
|
161
|
-
if (!resultSavedInVariable) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const storeName = parentNode.id.name;
|
|
166
|
-
|
|
167
|
-
if (namingOf.store.isValid({ name: storeName, context })) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
reportStoreNameConventionViolation({
|
|
172
|
-
context,
|
|
173
|
-
node: parentNode,
|
|
174
|
-
storeName,
|
|
175
|
-
});
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
};
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
function reportStoreNameConventionViolation({ context, node, storeName }) {
|
|
184
|
-
const storeNameConvention = getStoreNameConvention(context);
|
|
185
|
-
const correctedStoreName = getCorrectedStoreName(storeName, context);
|
|
186
|
-
|
|
187
|
-
context.report({
|
|
188
|
-
node,
|
|
189
|
-
messageId: "invalidName",
|
|
190
|
-
data: {
|
|
191
|
-
storeName,
|
|
192
|
-
correctedStoreName,
|
|
193
|
-
storeNameConvention,
|
|
194
|
-
},
|
|
195
|
-
suggest: [
|
|
196
|
-
{
|
|
197
|
-
messageId: "renameStore",
|
|
198
|
-
data: { storeName, correctedStoreName },
|
|
199
|
-
fix(fixer) {
|
|
200
|
-
return fixer.replaceTextRange(node.id.range, correctedStoreName);
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
});
|
|
205
|
-
}
|