@dartess/eslint-plugin 0.8.1 → 0.10.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 +11 -0
- package/README.md +7 -1
- package/dist/configs/mobx.js +1 -0
- package/dist/configs/recommended.js +10 -0
- package/dist/configs/vendor-rules/best-practices.d.ts +0 -5
- package/dist/configs/vendor-rules/best-practices.js +0 -9
- package/dist/configs/vendor-rules/errors.d.ts +0 -1
- package/dist/configs/vendor-rules/errors.js +0 -3
- package/dist/configs/vendor-rules/es6.d.ts +0 -4
- package/dist/configs/vendor-rules/es6.js +0 -8
- package/dist/configs/vendor-rules/style.d.ts +0 -1
- package/dist/configs/vendor-rules/style.js +0 -3
- package/dist/index.js +2 -0
- package/dist/rules/mobx-no-action-bound.d.ts +3 -0
- package/dist/rules/mobx-no-action-bound.js +25 -0
- package/dist/rules/mobx-require-observer.js +2 -2
- package/docs/rules/mobx-no-action-bound.md +40 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
[//]: # (https://keepachangelog.com/en/1.1.0/)
|
|
4
4
|
|
|
5
|
+
## [0.10.0] - 2026-02-23
|
|
6
|
+
|
|
7
|
+
- add rule `@dartess/mobx-no-action-bound`
|
|
8
|
+
- add `@dartess/mobx-no-action-bound` to `mobx` config
|
|
9
|
+
- change `no-sequences` option `allowInParentheses` from `true` to `false`
|
|
10
|
+
- allow `mobx-require-observer` without settings because `mobx` can be used without hooks
|
|
11
|
+
|
|
12
|
+
## [0.9.0] - 2026-02-03
|
|
13
|
+
|
|
14
|
+
- add `eslint-plugin-complete` to `recommended` config
|
|
15
|
+
|
|
5
16
|
## [0.8.1] - 2026-01-24
|
|
6
17
|
|
|
7
18
|
- fix `mobx-require-observer`: now it doesn't lose generics
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Also extends
|
|
|
8
8
|
* `eslint-plugin-import-x` — `recommended` & `typescript`
|
|
9
9
|
* `@eslint-community/eslint-plugin-eslint-comments` — `recommended`
|
|
10
10
|
* `eslint-plugin-de-morgan` — `recommended`
|
|
11
|
+
* `eslint-plugin-complete` — `recommended`
|
|
11
12
|
|
|
12
13
|
Also can extends (if it is applicable)
|
|
13
14
|
* `@eslint-react/eslint-plugin` — `strict-type-checked`
|
|
@@ -38,6 +39,7 @@ npm i -D eslint \
|
|
|
38
39
|
eslint-plugin-import-x \
|
|
39
40
|
eslint-import-resolver-typescript \
|
|
40
41
|
eslint-plugin-unicorn \
|
|
42
|
+
eslint-plugin-complete \
|
|
41
43
|
eslint-plugin-decorator-position \
|
|
42
44
|
eslint-plugin-de-morgan \
|
|
43
45
|
typescript-eslint \
|
|
@@ -68,6 +70,7 @@ npm i -D eslint-plugin-storybook
|
|
|
68
70
|
Edit or create `eslint.config.ts` (or `eslint.config.mts`). You probably have to install `jiti` for it.
|
|
69
71
|
|
|
70
72
|
```ts
|
|
73
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
71
74
|
import dartessEslintPluginRecommended from '@dartess/eslint-plugin/recommended';
|
|
72
75
|
import dartessEslintPluginRecommendedPostFormat from '@dartess/eslint-plugin/recommended-post-format';
|
|
73
76
|
|
|
@@ -108,7 +111,7 @@ export default [
|
|
|
108
111
|
// @see `Fine Tuning` -> `Formatters` section below
|
|
109
112
|
|
|
110
113
|
...dartessEslintPluginRecommendedPostFormat,
|
|
111
|
-
]
|
|
114
|
+
] satisfies TSESLint.FlatConfig.ConfigArray;
|
|
112
115
|
|
|
113
116
|
```
|
|
114
117
|
|
|
@@ -152,6 +155,8 @@ This plugin requires manual setup for you build tools.
|
|
|
152
155
|
|
|
153
156
|
If you're using Mobx with legacy decorators, you have to enable rule `mobx/missing-make-observable` manually.
|
|
154
157
|
|
|
158
|
+
if you're using Mobx stores with your custom hooks, set `settings.mobx.storeHooks` [by docs](docs/rules/mobx-require-observer.md).
|
|
159
|
+
|
|
155
160
|
## Supported Rules
|
|
156
161
|
|
|
157
162
|
Each rule has emojis denoting:
|
|
@@ -177,6 +182,7 @@ Each rule has emojis denoting:
|
|
|
177
182
|
| [mobx-require-observer](docs/rules/mobx-require-observer.md) | Components using the stores must be wrapped in an `observer` | ✅ | 🔧 | |
|
|
178
183
|
| [mobx-sync-autorun](docs/rules/mobx-sync-autorun.md) | Enforce synchronous autorun callback | ✅ | | |
|
|
179
184
|
| [mobx-sync-action](docs/rules/mobx-sync-action.md) | Enforce synchronous actions | ✅ | | |
|
|
185
|
+
| [mobx-no-action-bound](docs/rules/mobx-no-action-bound.md) | Enforce using arrow functions for binging `this` to actions | ✅ | | |
|
|
180
186
|
|
|
181
187
|
## Code Reuse Policy
|
|
182
188
|
|
package/dist/configs/mobx.js
CHANGED
|
@@ -9,6 +9,7 @@ const config = [
|
|
|
9
9
|
'mobx/missing-make-observable': 'off', // useless with modern decorators syntax. TODO check original plugin?
|
|
10
10
|
'@dartess/mobx-strict-observable-components-declaration': 'error',
|
|
11
11
|
'@dartess/mobx-require-observer': 'error',
|
|
12
|
+
'@dartess/mobx-no-action-bound': 'error',
|
|
12
13
|
'@dartess/mobx-sync-autorun': 'error', // TODO implement it by types?
|
|
13
14
|
'@dartess/mobx-sync-action': 'error', // TODO implement it by types?
|
|
14
15
|
},
|
|
@@ -10,6 +10,7 @@ import eslintCommentsPlugin from '@eslint-community/eslint-plugin-eslint-comment
|
|
|
10
10
|
// @ts-ignore: https://github.com/NullVoxPopuli/eslint-plugin-decorator-position/issues/778
|
|
11
11
|
import eslintPluginDecoratorPosition from 'eslint-plugin-decorator-position';
|
|
12
12
|
import eslintPluginDeMorgan from 'eslint-plugin-de-morgan';
|
|
13
|
+
import esLintPluginComplete from 'eslint-plugin-complete';
|
|
13
14
|
import dartessPlugin from "../index.js";
|
|
14
15
|
import vendorRulesBestPractices from "./vendor-rules/best-practices.js";
|
|
15
16
|
import vendorRulesErrors from "./vendor-rules/errors.js";
|
|
@@ -47,6 +48,7 @@ const config = [
|
|
|
47
48
|
convertWarnsToErrorsIfNeeded(eslintPluginImportX.flatConfigs.typescript),
|
|
48
49
|
convertWarnsToErrorsIfNeeded(eslintCommentsPlugin.recommended),
|
|
49
50
|
convertWarnsToErrorsIfNeeded(eslintPluginDeMorgan.configs.recommended),
|
|
51
|
+
...convertWarnsToErrorsIfNeeded(esLintPluginComplete.configs.recommended),
|
|
50
52
|
{
|
|
51
53
|
name: '@dartess/recommended',
|
|
52
54
|
plugins: {
|
|
@@ -108,6 +110,8 @@ const config = [
|
|
|
108
110
|
methods: 'above',
|
|
109
111
|
},
|
|
110
112
|
],
|
|
113
|
+
// totally disable comma operator
|
|
114
|
+
'no-sequences': ['error', { allowInParentheses: false }],
|
|
111
115
|
},
|
|
112
116
|
},
|
|
113
117
|
{
|
|
@@ -224,6 +228,12 @@ const config = [
|
|
|
224
228
|
],
|
|
225
229
|
// require names for tuple elements
|
|
226
230
|
'@dartess/ts-named-tuple-elements': 'error',
|
|
231
|
+
// disable some recommended rules
|
|
232
|
+
'complete/prefer-readonly-parameter-types': 'off', // can be flase-positive for Built-in methods
|
|
233
|
+
'complete/no-mutable-return': 'off', // can be hamful
|
|
234
|
+
'complete/strict-undefined-functions': 'off', // prefer unicorn/no-useless-undefined
|
|
235
|
+
'complete/require-break': 'off', // can be false-positive with TS7027
|
|
236
|
+
'complete/no-void-return-type': 'off', // conflict with @typescript-eslint/explicit-module-boundary-types
|
|
227
237
|
},
|
|
228
238
|
},
|
|
229
239
|
{
|
|
@@ -8,9 +8,6 @@ declare const rules: {
|
|
|
8
8
|
}];
|
|
9
9
|
'default-case-last': "error";
|
|
10
10
|
'default-param-last': "error";
|
|
11
|
-
eqeqeq: ["error", string, {
|
|
12
|
-
null: string;
|
|
13
|
-
}];
|
|
14
11
|
'grouped-accessor-pairs': "error";
|
|
15
12
|
'guard-for-in': "error";
|
|
16
13
|
'max-classes-per-file': ["error", number];
|
|
@@ -86,9 +83,7 @@ declare const rules: {
|
|
|
86
83
|
'no-return-assign': ["error", string];
|
|
87
84
|
'no-script-url': "error";
|
|
88
85
|
'no-self-compare': "error";
|
|
89
|
-
'no-sequences': "error";
|
|
90
86
|
'no-useless-concat': "error";
|
|
91
|
-
'no-useless-return': "error";
|
|
92
87
|
'no-void': "error";
|
|
93
88
|
'prefer-promise-reject-errors': ["error", {
|
|
94
89
|
allowEmptyReject: boolean;
|
|
@@ -17,9 +17,6 @@ const rules = {
|
|
|
17
17
|
'default-case-last': 'error',
|
|
18
18
|
// https://eslint.org/docs/rules/default-param-last
|
|
19
19
|
'default-param-last': 'error',
|
|
20
|
-
// require the use of === and !==
|
|
21
|
-
// https://eslint.org/docs/rules/eqeqeq
|
|
22
|
-
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
|
23
20
|
// Require grouped accessor pairs in object literals and classes
|
|
24
21
|
// https://eslint.org/docs/rules/grouped-accessor-pairs
|
|
25
22
|
'grouped-accessor-pairs': 'error',
|
|
@@ -168,15 +165,9 @@ const rules = {
|
|
|
168
165
|
// disallow comparisons where both sides are exactly the same
|
|
169
166
|
// https://eslint.org/docs/rules/no-self-compare
|
|
170
167
|
'no-self-compare': 'error',
|
|
171
|
-
// disallow use of comma operator
|
|
172
|
-
// https://eslint.org/docs/rules/no-sequences
|
|
173
|
-
'no-sequences': 'error',
|
|
174
168
|
// disallow useless string concatenation
|
|
175
169
|
// https://eslint.org/docs/rules/no-useless-concat
|
|
176
170
|
'no-useless-concat': 'error',
|
|
177
|
-
// disallow redundant return; keywords
|
|
178
|
-
// https://eslint.org/docs/rules/no-useless-return
|
|
179
|
-
'no-useless-return': 'error',
|
|
180
171
|
// disallow use of void operator
|
|
181
172
|
// https://eslint.org/docs/rules/no-void
|
|
182
173
|
'no-void': 'error',
|
|
@@ -13,9 +13,6 @@ const rules = {
|
|
|
13
13
|
// Disallow returning values from Promise executor functions
|
|
14
14
|
// https://eslint.org/docs/rules/no-promise-executor-return
|
|
15
15
|
'no-promise-executor-return': 'error',
|
|
16
|
-
// Disallow template literal placeholder syntax in regular strings
|
|
17
|
-
// https://eslint.org/docs/rules/no-template-curly-in-string
|
|
18
|
-
'no-template-curly-in-string': 'error',
|
|
19
16
|
// Disallow loops with a body that allows only one iteration
|
|
20
17
|
// https://eslint.org/docs/rules/no-unreachable-loop
|
|
21
18
|
'no-unreachable-loop': [
|
|
@@ -12,10 +12,6 @@ declare const rules: {
|
|
|
12
12
|
ignoreConstructors: boolean;
|
|
13
13
|
avoidQuotes: boolean;
|
|
14
14
|
}];
|
|
15
|
-
'prefer-const': ["error", {
|
|
16
|
-
destructuring: string;
|
|
17
|
-
ignoreReadBeforeAssign: boolean;
|
|
18
|
-
}];
|
|
19
15
|
'prefer-destructuring': ["error", {
|
|
20
16
|
VariableDeclarator: {
|
|
21
17
|
array: boolean;
|
|
@@ -37,14 +37,6 @@ const rules = {
|
|
|
37
37
|
avoidQuotes: true,
|
|
38
38
|
},
|
|
39
39
|
],
|
|
40
|
-
// suggest using of const declaration for variables that are never modified after declared
|
|
41
|
-
'prefer-const': [
|
|
42
|
-
'error',
|
|
43
|
-
{
|
|
44
|
-
destructuring: 'any',
|
|
45
|
-
ignoreReadBeforeAssign: true,
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
40
|
// Prefer destructuring from arrays and objects
|
|
49
41
|
// https://eslint.org/docs/rules/prefer-destructuring
|
|
50
42
|
'prefer-destructuring': [
|
|
@@ -39,9 +39,6 @@ const rules = {
|
|
|
39
39
|
'no-multi-assign': ['error'],
|
|
40
40
|
// disallow nested ternary expressions
|
|
41
41
|
'no-nested-ternary': 'error',
|
|
42
|
-
// disallow use of unary operators, ++ and --
|
|
43
|
-
// https://eslint.org/docs/rules/no-plusplus
|
|
44
|
-
'no-plusplus': 'error',
|
|
45
42
|
// disallow dangling underscores in identifiers
|
|
46
43
|
// https://eslint.org/docs/rules/no-underscore-dangle
|
|
47
44
|
'no-underscore-dangle': [
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import ruleStoriesExportMeta from "./rules/stories-export-meta.js";
|
|
|
5
5
|
import ruleStoriesExportTyped from "./rules/stories-export-typed.js";
|
|
6
6
|
import ruleMobxStrictObservableComponentsDeclaration from "./rules/mobx-strict-observable-components-declaration.js";
|
|
7
7
|
import ruleMobxRequireObserver from "./rules/mobx-require-observer.js";
|
|
8
|
+
import ruleMobxNoActionBound from "./rules/mobx-no-action-bound.js";
|
|
8
9
|
import ruleImportsMaxParentDepth from "./rules/imports-max-parent-depth.js";
|
|
9
10
|
import ruleTsNamedTupleElements from "./rules/ts-named-tuple-elements.js";
|
|
10
11
|
import ruleMobxSyncAutorun from "./rules/mobx-sync-autorun.js";
|
|
@@ -23,6 +24,7 @@ const plugin = {
|
|
|
23
24
|
'mobx-sync-action': ruleMobxSyncAction,
|
|
24
25
|
'mobx-sync-autorun': ruleMobxSyncAutorun,
|
|
25
26
|
'mobx-require-observer': ruleMobxRequireObserver,
|
|
27
|
+
'mobx-no-action-bound': ruleMobxNoActionBound,
|
|
26
28
|
'stories-export-typed': ruleStoriesExportTyped,
|
|
27
29
|
'stories-export-meta': ruleStoriesExportMeta,
|
|
28
30
|
'ts-named-tuple-elements': ruleTsNamedTupleElements,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
export default ESLintUtils.RuleCreator(() => '')({
|
|
3
|
+
name: 'mobx-no-action-bound',
|
|
4
|
+
defaultOptions: [],
|
|
5
|
+
meta: {
|
|
6
|
+
type: 'problem',
|
|
7
|
+
docs: {
|
|
8
|
+
description: 'Enforce using arrow functions for binging `this` to actions',
|
|
9
|
+
},
|
|
10
|
+
messages: {
|
|
11
|
+
noActionBound: 'rewrite action with arrow function for binding `this`',
|
|
12
|
+
},
|
|
13
|
+
schema: [],
|
|
14
|
+
},
|
|
15
|
+
create(context) {
|
|
16
|
+
return {
|
|
17
|
+
"Decorator[expression.object.name='action'][expression.property.name='bound']": function (node) {
|
|
18
|
+
context.report({
|
|
19
|
+
node,
|
|
20
|
+
messageId: 'noActionBound',
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -54,7 +54,7 @@ export default ESLintUtils.RuleCreator(() => '')({
|
|
|
54
54
|
const mobxSettings = context.settings.mobx;
|
|
55
55
|
const hooks = Array.isArray(mobxSettings?.storeHooks) ? mobxSettings.storeHooks : [];
|
|
56
56
|
if (hooks.length === 0) {
|
|
57
|
-
|
|
57
|
+
return {};
|
|
58
58
|
}
|
|
59
59
|
const { sourceCode } = context;
|
|
60
60
|
const program = sourceCode.ast;
|
|
@@ -138,7 +138,7 @@ export default ESLintUtils.RuleCreator(() => '')({
|
|
|
138
138
|
const replacement = `const ${name} = observer(${original});`;
|
|
139
139
|
fixes.push(fixer.replaceText(fn, replacement));
|
|
140
140
|
}
|
|
141
|
-
else if (varDecl
|
|
141
|
+
else if (varDecl?.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
142
142
|
// const Name = () => {} or function expression
|
|
143
143
|
const id = fn.parent.id;
|
|
144
144
|
const { name } = id;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Enforce using arrow functions for binging `this` to actions (mobx-no-action-bound)
|
|
2
|
+
|
|
3
|
+
Decorator `@action.bound` automatically bind methods to class instances to avoid context loss (`this`).
|
|
4
|
+
However, this can also be achieved by declaring methods as an properies with arrow functions.
|
|
5
|
+
|
|
6
|
+
This is the first problem with bound methods—their functionality is duplicated by native abilities.
|
|
7
|
+
|
|
8
|
+
In general, for optimization purposes, it's not recommended to bind _everything_; by default,
|
|
9
|
+
class methods should be on the prototype.
|
|
10
|
+
|
|
11
|
+
To resolve the dilemma of "how to avoid binding unnecessary things and avoid losing context"
|
|
12
|
+
the [`@typescript-eslint/unbound-method`](https://typescript-eslint.io/rules/unbound-method/) rule exists.
|
|
13
|
+
|
|
14
|
+
However, it can't detect whether a method is bound via an `@action.bound`. Therefore, it's best to avoid
|
|
15
|
+
using `@action.bound` at all and leave only one binding method—arrow functions with `@typescript-eslint/unbound-method`.
|
|
16
|
+
In this case you don't need think about it.
|
|
17
|
+
|
|
18
|
+
## Rule Details
|
|
19
|
+
|
|
20
|
+
Examples of **incorrect** code for this rule:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
class Store {
|
|
24
|
+
@action.bound
|
|
25
|
+
reset() {
|
|
26
|
+
this.value = null;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Examples of **correct** code for this rule:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
class Store {
|
|
35
|
+
@action
|
|
36
|
+
reset = () => {
|
|
37
|
+
this.value = null;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dartess/eslint-plugin",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"description": "A set of rules and configs for various TypeScript projects",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"eslint",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@stylistic/eslint-plugin": "^5.7.0",
|
|
54
54
|
"eslint": "^9.0.0",
|
|
55
55
|
"eslint-import-resolver-typescript": "^4.0.0",
|
|
56
|
+
"eslint-plugin-complete": "^1.2.0",
|
|
56
57
|
"eslint-plugin-de-morgan": "^2.0.0",
|
|
57
58
|
"eslint-plugin-decorator-position": "^6.0.0",
|
|
58
59
|
"eslint-plugin-import-x": "^4.1.0",
|