eslint-plugin-mobx 0.0.9 → 0.0.10
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 +8 -0
- package/README.md +17 -3
- package/dist/index.js +78 -125
- package/package.json +4 -4
- package/src/exhaustive-make-observable.js +100 -80
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# eslint-plugin-mobx
|
|
2
2
|
|
|
3
|
+
## 0.0.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`44a5fe07`](https://github.com/mobxjs/mobx/commit/44a5fe07fb95c2ba24d8df19f18b57ee92abb1a9) [#3881](https://github.com/mobxjs/mobx/pull/3881) Thanks [@kade-robertson](https://github.com/kade-robertson)! - Adds an option for the `mobx/exhaustive-make-observable` eslint rule to configure whether fields are annotated with `true` or `false` with the autofixer.
|
|
8
|
+
|
|
9
|
+
This option defaults to `true` if not present or an invalid value is received to maintain existing behavior.
|
|
10
|
+
|
|
3
11
|
## 0.0.9
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -35,10 +35,22 @@ module.exports = {
|
|
|
35
35
|
### mobx/exhaustive-make-observable
|
|
36
36
|
|
|
37
37
|
Makes sure that `makeObservable` annotates all fields defined on class or object literal.<br>
|
|
38
|
-
**Autofix** adds `field: true` for each missing field.<br>
|
|
39
38
|
To exclude a field, annotate it using `field: false`.<br>
|
|
40
39
|
Does not support fields introduced by constructor (`this.foo = 5`).<br>
|
|
41
|
-
Does not warn about annotated non-existing fields (there is a runtime check, but the autofix removing the field could be handy...)
|
|
40
|
+
Does not warn about annotated non-existing fields (there is a runtime check, but the autofix removing the field could be handy...).<br>
|
|
41
|
+
**Autofix** adds `field: true` for each missing field by default. You can change this behaviour by specifying options in your eslint config:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"rules": {
|
|
46
|
+
"mobx/exhaustive-make-observable": ["error", { "autofixAnnotation": false }]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This is a boolean value that controls if the field is annotated with `true` or `false`.
|
|
52
|
+
If you are migrating an existing project using `makeObservable` and do not want this rule to override
|
|
53
|
+
your current usage (even if it may be wrong), you should run the autofix with the annotation set to `false` to maintain existing behaviour: `eslint --no-eslintrc --fix --rule='mobx/exhaustive-make-observable: [2, { "autofixAnnotation": false }]' .`
|
|
42
54
|
|
|
43
55
|
### mobx/missing-make-observable
|
|
44
56
|
|
|
@@ -85,6 +97,7 @@ It's a bit opinionated and can lead to a lot of false positives depending on you
|
|
|
85
97
|
### mobx/no-anonymous-observer (deprecated)
|
|
86
98
|
|
|
87
99
|
_Deprecated in favor of [react/display-name](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/display-name.md) + [componentWrapperFunctions](https://github.com/jsx-eslint/eslint-plugin-react). Example of **.eslintrc**:_
|
|
100
|
+
|
|
88
101
|
```
|
|
89
102
|
{
|
|
90
103
|
"rules": {
|
|
@@ -97,9 +110,10 @@ _Deprecated in favor of [react/display-name](https://github.com/jsx-eslint/eslin
|
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
```
|
|
113
|
+
|
|
100
114
|
---
|
|
101
115
|
|
|
102
116
|
Forbids anonymous functions or classes as `observer` components.
|
|
103
117
|
Improves debugging experience and [avoids problem with inability to customize `displayName`](https://github.com/mobxjs/mobx/issues/2721).
|
|
104
|
-
Plays nice with `eslint-plugin-react-hooks` and `mobx/missing-observer` as both of these don't
|
|
118
|
+
Plays nice with `eslint-plugin-react-hooks` and `mobx/missing-observer` as both of these don't recognize anonymous function as component.
|
|
105
119
|
**Autofix** infers the name from variable if possible.
|
package/dist/index.js
CHANGED
|
@@ -1,55 +1,47 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
function _iterableToArrayLimit(arr, i) {
|
|
4
|
+
var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"];
|
|
5
|
+
if (null != _i) {
|
|
6
|
+
var _s,
|
|
7
|
+
_e,
|
|
8
|
+
_x,
|
|
9
|
+
_r,
|
|
10
|
+
_arr = [],
|
|
11
|
+
_n = !0,
|
|
12
|
+
_d = !1;
|
|
13
|
+
try {
|
|
14
|
+
if (_x = (_i = _i.call(arr)).next, 0 === i) {
|
|
15
|
+
if (Object(_i) !== _i) return;
|
|
16
|
+
_n = !1;
|
|
17
|
+
} else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
_d = !0, _e = err;
|
|
20
|
+
} finally {
|
|
21
|
+
try {
|
|
22
|
+
if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return;
|
|
23
|
+
} finally {
|
|
24
|
+
if (_d) throw _e;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return _arr;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
3
30
|
function _slicedToArray(arr, i) {
|
|
4
31
|
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
|
|
5
32
|
}
|
|
6
|
-
|
|
7
33
|
function _toConsumableArray(arr) {
|
|
8
34
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
9
35
|
}
|
|
10
|
-
|
|
11
36
|
function _arrayWithoutHoles(arr) {
|
|
12
37
|
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
13
38
|
}
|
|
14
|
-
|
|
15
39
|
function _arrayWithHoles(arr) {
|
|
16
40
|
if (Array.isArray(arr)) return arr;
|
|
17
41
|
}
|
|
18
|
-
|
|
19
42
|
function _iterableToArray(iter) {
|
|
20
43
|
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
21
44
|
}
|
|
22
|
-
|
|
23
|
-
function _iterableToArrayLimit(arr, i) {
|
|
24
|
-
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
25
|
-
|
|
26
|
-
if (_i == null) return;
|
|
27
|
-
var _arr = [];
|
|
28
|
-
var _n = true;
|
|
29
|
-
var _d = false;
|
|
30
|
-
|
|
31
|
-
var _s, _e;
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
|
|
35
|
-
_arr.push(_s.value);
|
|
36
|
-
|
|
37
|
-
if (i && _arr.length === i) break;
|
|
38
|
-
}
|
|
39
|
-
} catch (err) {
|
|
40
|
-
_d = true;
|
|
41
|
-
_e = err;
|
|
42
|
-
} finally {
|
|
43
|
-
try {
|
|
44
|
-
if (!_n && _i["return"] != null) _i["return"]();
|
|
45
|
-
} finally {
|
|
46
|
-
if (_d) throw _e;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return _arr;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
45
|
function _unsupportedIterableToArray(o, minLen) {
|
|
54
46
|
if (!o) return;
|
|
55
47
|
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
@@ -58,28 +50,21 @@ function _unsupportedIterableToArray(o, minLen) {
|
|
|
58
50
|
if (n === "Map" || n === "Set") return Array.from(o);
|
|
59
51
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
60
52
|
}
|
|
61
|
-
|
|
62
53
|
function _arrayLikeToArray(arr, len) {
|
|
63
54
|
if (len == null || len > arr.length) len = arr.length;
|
|
64
|
-
|
|
65
55
|
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
66
|
-
|
|
67
56
|
return arr2;
|
|
68
57
|
}
|
|
69
|
-
|
|
70
58
|
function _nonIterableSpread() {
|
|
71
59
|
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
72
60
|
}
|
|
73
|
-
|
|
74
61
|
function _nonIterableRest() {
|
|
75
62
|
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
76
63
|
}
|
|
77
64
|
|
|
78
65
|
var mobxDecorators = new Set(['observable', 'computed', 'action', 'flow', 'override']);
|
|
79
|
-
|
|
80
66
|
function isMobxDecorator$2(decorator) {
|
|
81
67
|
var _decorator$expression, _decorator$expression2;
|
|
82
|
-
|
|
83
68
|
return mobxDecorators.has(decorator.expression.name) // @foo
|
|
84
69
|
|| mobxDecorators.has((_decorator$expression = decorator.expression.callee) === null || _decorator$expression === void 0 ? void 0 : _decorator$expression.name) // @foo()
|
|
85
70
|
|| mobxDecorators.has((_decorator$expression2 = decorator.expression.object) === null || _decorator$expression2 === void 0 ? void 0 : _decorator$expression2.name); // @foo.bar
|
|
@@ -91,67 +76,61 @@ function findAncestor$3(node, match) {
|
|
|
91
76
|
if (match(parent)) return parent;
|
|
92
77
|
return findAncestor$3(parent, match);
|
|
93
78
|
}
|
|
94
|
-
|
|
95
79
|
var utils = {
|
|
96
80
|
findAncestor: findAncestor$3,
|
|
97
81
|
isMobxDecorator: isMobxDecorator$2
|
|
98
82
|
};
|
|
99
83
|
|
|
100
84
|
var findAncestor$2 = utils.findAncestor,
|
|
101
|
-
|
|
102
|
-
// TODO? report on field as well
|
|
85
|
+
isMobxDecorator$1 = utils.isMobxDecorator;
|
|
103
86
|
|
|
87
|
+
// TODO support this.foo = 5; in constructor
|
|
88
|
+
// TODO? report on field as well
|
|
104
89
|
function create$4(context) {
|
|
90
|
+
var _context$options$0$au, _context$options$;
|
|
105
91
|
var sourceCode = context.getSourceCode();
|
|
106
|
-
|
|
92
|
+
var autofixAnnotation = (_context$options$0$au = (_context$options$ = context.options[0]) === null || _context$options$ === void 0 ? void 0 : _context$options$.autofixAnnotation) !== null && _context$options$0$au !== void 0 ? _context$options$0$au : true;
|
|
107
93
|
function fieldToKey(field) {
|
|
108
94
|
// TODO cache on field?
|
|
109
95
|
var key = sourceCode.getText(field.key);
|
|
110
96
|
return field.computed ? "[".concat(key, "]") : key;
|
|
111
97
|
}
|
|
112
|
-
|
|
113
98
|
return {
|
|
114
99
|
'CallExpression[callee.name="makeObservable"]': function CallExpressionCalleeNameMakeObservable(makeObservable) {
|
|
115
100
|
// Only interested about makeObservable(this, ...) in constructor or makeObservable({}, ...)
|
|
116
|
-
// ClassDeclaration
|
|
101
|
+
// ClassDeclaration
|
|
117
102
|
// ClassBody
|
|
118
103
|
// MethodDefinition[kind="constructor"]
|
|
119
104
|
// FunctionExpression
|
|
120
105
|
// BlockStatement
|
|
121
106
|
// ExpressionStatement
|
|
122
|
-
// CallExpression[callee.name="makeObservable"]
|
|
107
|
+
// CallExpression[callee.name="makeObservable"]
|
|
123
108
|
var _makeObservable$argum = _slicedToArray(makeObservable.arguments, 2),
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
109
|
+
firstArg = _makeObservable$argum[0],
|
|
110
|
+
secondArg = _makeObservable$argum[1];
|
|
127
111
|
if (!firstArg) return;
|
|
128
112
|
var members;
|
|
129
|
-
|
|
130
|
-
if (firstArg.type === 'ThisExpression') {
|
|
113
|
+
if (firstArg.type === "ThisExpression") {
|
|
131
114
|
var _closestFunction$pare;
|
|
132
|
-
|
|
133
115
|
var closestFunction = findAncestor$2(makeObservable, function (node) {
|
|
134
|
-
return node.type ===
|
|
116
|
+
return node.type === "FunctionExpression" || node.type === "FunctionDeclaration";
|
|
135
117
|
});
|
|
136
|
-
if ((closestFunction === null || closestFunction === void 0 ? void 0 : (_closestFunction$pare = closestFunction.parent) === null || _closestFunction$pare === void 0 ? void 0 : _closestFunction$pare.kind) !==
|
|
118
|
+
if ((closestFunction === null || closestFunction === void 0 ? void 0 : (_closestFunction$pare = closestFunction.parent) === null || _closestFunction$pare === void 0 ? void 0 : _closestFunction$pare.kind) !== "constructor") return;
|
|
137
119
|
members = closestFunction.parent.parent.parent.body.body;
|
|
138
|
-
} else if (firstArg.type ===
|
|
120
|
+
} else if (firstArg.type === "ObjectExpression") {
|
|
139
121
|
members = firstArg.properties;
|
|
140
122
|
} else {
|
|
141
123
|
return;
|
|
142
124
|
}
|
|
143
|
-
|
|
144
125
|
var annotationProps = (secondArg === null || secondArg === void 0 ? void 0 : secondArg.properties) || [];
|
|
145
126
|
var nonAnnotatedMembers = [];
|
|
146
127
|
var hasAnyDecorator = false;
|
|
147
128
|
members.forEach(function (member) {
|
|
148
129
|
var _member$decorators;
|
|
149
|
-
|
|
150
130
|
if (member["static"]) return;
|
|
151
|
-
if (member.kind === "constructor") return;
|
|
152
|
-
|
|
131
|
+
if (member.kind === "constructor") return;
|
|
132
|
+
//if (member.type !== 'MethodDefinition' && member.type !== 'ClassProperty') return;
|
|
153
133
|
hasAnyDecorator = hasAnyDecorator || ((_member$decorators = member.decorators) === null || _member$decorators === void 0 ? void 0 : _member$decorators.some(isMobxDecorator$1)) || false;
|
|
154
|
-
|
|
155
134
|
if (!annotationProps.some(function (prop) {
|
|
156
135
|
return fieldToKey(prop) === fieldToKey(member);
|
|
157
136
|
})) {
|
|
@@ -162,46 +141,42 @@ function create$4(context) {
|
|
|
162
141
|
/*
|
|
163
142
|
// With decorators, second arg must be null/undefined or not provided
|
|
164
143
|
if (hasAnyDecorator && secondArg && secondArg.name !== "undefined" && secondArg.value !== null) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
144
|
+
context.report({
|
|
145
|
+
node: makeObservable,
|
|
146
|
+
message: 'When using decorators, second arg must be `null`, `undefined` or not provided.',
|
|
147
|
+
})
|
|
169
148
|
}
|
|
170
149
|
// Without decorators, in constructor, second arg must be object literal
|
|
171
150
|
if (!hasAnyDecorator && firstArg.type === 'ThisExpression' && (!secondArg || secondArg.type !== 'ObjectExpression')) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
151
|
+
context.report({
|
|
152
|
+
node: makeObservable,
|
|
153
|
+
message: 'Second argument must be object in form of `{ key: annotation }`.',
|
|
154
|
+
})
|
|
176
155
|
}
|
|
177
156
|
*/
|
|
178
157
|
|
|
179
158
|
if (!hasAnyDecorator && nonAnnotatedMembers.length) {
|
|
180
159
|
// Set avoids reporting twice for setter+getter pair or actual duplicates
|
|
181
160
|
var keys = _toConsumableArray(new Set(nonAnnotatedMembers.map(fieldToKey)));
|
|
182
|
-
|
|
183
161
|
var keyList = keys.map(function (key) {
|
|
184
162
|
return "`".concat(key, "`");
|
|
185
|
-
}).join(
|
|
186
|
-
|
|
163
|
+
}).join(", ");
|
|
187
164
|
var fix = function fix(fixer) {
|
|
188
165
|
var annotationList = keys.map(function (key) {
|
|
189
|
-
return "".concat(key, ":
|
|
190
|
-
}).join(
|
|
191
|
-
|
|
166
|
+
return "".concat(key, ": ").concat(autofixAnnotation);
|
|
167
|
+
}).join(", ") + ",";
|
|
192
168
|
if (!secondArg) {
|
|
193
169
|
return fixer.insertTextAfter(firstArg, ", { ".concat(annotationList, " }"));
|
|
194
|
-
} else if (secondArg.type !==
|
|
170
|
+
} else if (secondArg.type !== "ObjectExpression") {
|
|
195
171
|
return fixer.replaceText(secondArg, "{ ".concat(annotationList, " }"));
|
|
196
172
|
} else {
|
|
197
173
|
var openingBracket = sourceCode.getFirstToken(secondArg);
|
|
198
174
|
return fixer.insertTextAfter(openingBracket, " ".concat(annotationList, " "));
|
|
199
175
|
}
|
|
200
176
|
};
|
|
201
|
-
|
|
202
177
|
context.report({
|
|
203
178
|
node: makeObservable,
|
|
204
|
-
messageId:
|
|
179
|
+
messageId: "missingAnnotation",
|
|
205
180
|
data: {
|
|
206
181
|
keyList: keyList
|
|
207
182
|
},
|
|
@@ -211,46 +186,50 @@ function create$4(context) {
|
|
|
211
186
|
}
|
|
212
187
|
};
|
|
213
188
|
}
|
|
214
|
-
|
|
215
189
|
var exhaustiveMakeObservable$1 = {
|
|
216
190
|
meta: {
|
|
217
|
-
type:
|
|
218
|
-
fixable:
|
|
191
|
+
type: "suggestion",
|
|
192
|
+
fixable: "code",
|
|
193
|
+
schema: [{
|
|
194
|
+
type: "object",
|
|
195
|
+
properties: {
|
|
196
|
+
autofixAnnotation: {
|
|
197
|
+
type: "boolean"
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
additionalProperties: false
|
|
201
|
+
}],
|
|
219
202
|
docs: {
|
|
220
|
-
description:
|
|
203
|
+
description: "enforce all fields being listen in `makeObservable`",
|
|
221
204
|
recommended: true,
|
|
222
205
|
suggestion: false
|
|
223
206
|
},
|
|
224
207
|
messages: {
|
|
225
|
-
|
|
208
|
+
missingAnnotation: "Missing annotation for {{ keyList }}. To exclude a field, use `false` as annotation."
|
|
226
209
|
}
|
|
227
210
|
},
|
|
228
211
|
create: create$4
|
|
229
212
|
};
|
|
230
213
|
|
|
231
214
|
var findAncestor$1 = utils.findAncestor;
|
|
232
|
-
|
|
233
215
|
function create$3(context) {
|
|
234
216
|
return {
|
|
235
217
|
'CallExpression[callee.name=/(makeObservable|makeAutoObservable)/]': function CallExpressionCalleeNameMakeObservableMakeAutoObservable(makeObservable) {
|
|
236
218
|
var _closestFunction$pare;
|
|
237
|
-
|
|
238
219
|
// Only iterested about makeObservable(this, ...) inside constructor and not inside nested bindable function
|
|
239
220
|
var _makeObservable$argum = _slicedToArray(makeObservable.arguments, 1),
|
|
240
|
-
|
|
241
|
-
|
|
221
|
+
firstArg = _makeObservable$argum[0];
|
|
242
222
|
if (!firstArg) return;
|
|
243
|
-
if (firstArg.type !== 'ThisExpression') return;
|
|
223
|
+
if (firstArg.type !== 'ThisExpression') return;
|
|
224
|
+
// MethodDefinition[key.name="constructor"][kind="constructor"]
|
|
244
225
|
// FunctionExpression
|
|
245
226
|
// BlockStatement
|
|
246
227
|
// ExpressionStatement
|
|
247
228
|
// CallExpression[callee.name="makeObservable"]
|
|
248
|
-
|
|
249
229
|
var closestFunction = findAncestor$1(makeObservable, function (node) {
|
|
250
230
|
return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
|
|
251
231
|
});
|
|
252
232
|
if ((closestFunction === null || closestFunction === void 0 ? void 0 : (_closestFunction$pare = closestFunction.parent) === null || _closestFunction$pare === void 0 ? void 0 : _closestFunction$pare.kind) !== 'constructor') return;
|
|
253
|
-
|
|
254
233
|
if (makeObservable.parent.parent.parent !== closestFunction) {
|
|
255
234
|
context.report({
|
|
256
235
|
node: makeObservable,
|
|
@@ -263,7 +242,6 @@ function create$3(context) {
|
|
|
263
242
|
}
|
|
264
243
|
};
|
|
265
244
|
}
|
|
266
|
-
|
|
267
245
|
var unconditionalMakeObservable$1 = {
|
|
268
246
|
meta: {
|
|
269
247
|
type: 'problem',
|
|
@@ -279,38 +257,32 @@ var unconditionalMakeObservable$1 = {
|
|
|
279
257
|
};
|
|
280
258
|
|
|
281
259
|
var findAncestor = utils.findAncestor,
|
|
282
|
-
|
|
283
|
-
|
|
260
|
+
isMobxDecorator = utils.isMobxDecorator;
|
|
284
261
|
function create$2(context) {
|
|
285
262
|
var sourceCode = context.getSourceCode();
|
|
286
263
|
return {
|
|
287
264
|
'Decorator': function Decorator(decorator) {
|
|
288
265
|
var _clazz$body$body$find, _constructor$value$bo, _constructor$value$bo2;
|
|
289
|
-
|
|
290
266
|
if (!isMobxDecorator(decorator)) return;
|
|
291
267
|
var clazz = findAncestor(decorator, function (node) {
|
|
292
268
|
return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
|
|
293
269
|
});
|
|
294
|
-
if (!clazz) return;
|
|
295
|
-
|
|
270
|
+
if (!clazz) return;
|
|
271
|
+
// ClassDeclaration > ClassBody > []
|
|
296
272
|
var constructor = (_clazz$body$body$find = clazz.body.body.find(function (node) {
|
|
297
273
|
return node.kind === 'constructor' && node.value.type === 'FunctionExpression';
|
|
298
274
|
})) !== null && _clazz$body$body$find !== void 0 ? _clazz$body$body$find : clazz.body.body.find(function (node) {
|
|
299
275
|
return node.kind === 'constructor';
|
|
300
|
-
});
|
|
301
|
-
|
|
276
|
+
});
|
|
277
|
+
// MethodDefinition > FunctionExpression > BlockStatement > []
|
|
302
278
|
var isMakeObservable = function isMakeObservable(node) {
|
|
303
279
|
var _node$expression, _node$expression$call, _node$expression2, _node$expression2$arg;
|
|
304
|
-
|
|
305
280
|
return ((_node$expression = node.expression) === null || _node$expression === void 0 ? void 0 : (_node$expression$call = _node$expression.callee) === null || _node$expression$call === void 0 ? void 0 : _node$expression$call.name) === 'makeObservable' && ((_node$expression2 = node.expression) === null || _node$expression2 === void 0 ? void 0 : (_node$expression2$arg = _node$expression2.arguments[0]) === null || _node$expression2$arg === void 0 ? void 0 : _node$expression2$arg.type) === 'ThisExpression';
|
|
306
281
|
};
|
|
307
|
-
|
|
308
282
|
var makeObservable = constructor === null || constructor === void 0 ? void 0 : (_constructor$value$bo = constructor.value.body) === null || _constructor$value$bo === void 0 ? void 0 : (_constructor$value$bo2 = _constructor$value$bo.body.find(isMakeObservable)) === null || _constructor$value$bo2 === void 0 ? void 0 : _constructor$value$bo2.expression;
|
|
309
|
-
|
|
310
283
|
if (makeObservable) {
|
|
311
284
|
// make sure second arg is nullish
|
|
312
285
|
var secondArg = makeObservable.arguments[1];
|
|
313
|
-
|
|
314
286
|
if (secondArg && secondArg.value !== null && secondArg.name !== 'undefined') {
|
|
315
287
|
context.report({
|
|
316
288
|
node: makeObservable,
|
|
@@ -326,7 +298,6 @@ function create$2(context) {
|
|
|
326
298
|
} else if (constructor) {
|
|
327
299
|
// constructor() {}
|
|
328
300
|
var _closingBracket = sourceCode.getLastToken(constructor.value.body);
|
|
329
|
-
|
|
330
301
|
return fixer.insertTextBefore(_closingBracket, ';makeObservable(this);');
|
|
331
302
|
} else {
|
|
332
303
|
// class C {}
|
|
@@ -334,7 +305,6 @@ function create$2(context) {
|
|
|
334
305
|
return fixer.insertTextAfter(openingBracket, '\nconstructor() { makeObservable(this); }');
|
|
335
306
|
}
|
|
336
307
|
};
|
|
337
|
-
|
|
338
308
|
context.report({
|
|
339
309
|
node: clazz,
|
|
340
310
|
messageId: 'missingMakeObservable',
|
|
@@ -344,7 +314,6 @@ function create$2(context) {
|
|
|
344
314
|
}
|
|
345
315
|
};
|
|
346
316
|
}
|
|
347
|
-
|
|
348
317
|
var missingMakeObservable$1 = {
|
|
349
318
|
meta: {
|
|
350
319
|
type: 'problem',
|
|
@@ -367,15 +336,13 @@ function create$1(context) {
|
|
|
367
336
|
return {
|
|
368
337
|
'FunctionDeclaration,FunctionExpression,ArrowFunctionExpression,ClassDeclaration,ClassExpression': function FunctionDeclarationFunctionExpressionArrowFunctionExpressionClassDeclarationClassExpression(cmp) {
|
|
369
338
|
var _cmp$id, _cmp$parent;
|
|
370
|
-
|
|
371
339
|
// Already has observer
|
|
372
340
|
if (cmp.parent && cmp.parent.type === 'CallExpression' && cmp.parent.callee.name === 'observer') return;
|
|
373
|
-
var name = (_cmp$id = cmp.id) === null || _cmp$id === void 0 ? void 0 : _cmp$id.name;
|
|
374
|
-
|
|
341
|
+
var name = (_cmp$id = cmp.id) === null || _cmp$id === void 0 ? void 0 : _cmp$id.name;
|
|
342
|
+
// If anonymous try to infer name from variable declaration
|
|
375
343
|
if (!name && ((_cmp$parent = cmp.parent) === null || _cmp$parent === void 0 ? void 0 : _cmp$parent.type) === 'VariableDeclarator') {
|
|
376
344
|
name = cmp.parent.id.name;
|
|
377
345
|
}
|
|
378
|
-
|
|
379
346
|
if (cmp.type.startsWith('Class')) {
|
|
380
347
|
// Must extend Component or React.Component
|
|
381
348
|
var superClass = cmp.superClass;
|
|
@@ -384,15 +351,12 @@ function create$1(context) {
|
|
|
384
351
|
if (superClassText !== 'Component' && superClassText !== 'React.Component') return;
|
|
385
352
|
} else {
|
|
386
353
|
var _name;
|
|
387
|
-
|
|
388
354
|
// Name must start with uppercase letter
|
|
389
355
|
if (!((_name = name) !== null && _name !== void 0 && _name.charAt(0).match(/^[A-Z]$/))) return;
|
|
390
356
|
}
|
|
391
|
-
|
|
392
357
|
var fix = function fix(fixer) {
|
|
393
358
|
return [fixer.insertTextBefore(sourceCode.getFirstToken(cmp), (name && cmp.type.endsWith('Declaration') ? "const ".concat(name, " = ") : '') + 'observer('), fixer.insertTextAfter(sourceCode.getLastToken(cmp), ')')];
|
|
394
359
|
};
|
|
395
|
-
|
|
396
360
|
context.report({
|
|
397
361
|
node: cmp,
|
|
398
362
|
messageId: 'missingObserver',
|
|
@@ -404,7 +368,6 @@ function create$1(context) {
|
|
|
404
368
|
}
|
|
405
369
|
};
|
|
406
370
|
}
|
|
407
|
-
|
|
408
371
|
var missingObserver$1 = {
|
|
409
372
|
meta: {
|
|
410
373
|
type: 'problem',
|
|
@@ -425,40 +388,31 @@ function create(context) {
|
|
|
425
388
|
return {
|
|
426
389
|
'CallExpression[callee.name="observer"]': function CallExpressionCalleeNameObserver(observer) {
|
|
427
390
|
var _cmp$id;
|
|
428
|
-
|
|
429
391
|
var cmp = observer.arguments[0];
|
|
430
392
|
if (!cmp) return;
|
|
431
393
|
if (cmp !== null && cmp !== void 0 && (_cmp$id = cmp.id) !== null && _cmp$id !== void 0 && _cmp$id.name) return;
|
|
432
|
-
|
|
433
394
|
var fix = function fix(fixer) {
|
|
434
395
|
var _observer$parent;
|
|
435
|
-
|
|
436
396
|
// Use name from variable for autofix
|
|
437
397
|
var name = ((_observer$parent = observer.parent) === null || _observer$parent === void 0 ? void 0 : _observer$parent.type) === "VariableDeclarator" ? observer.parent.id.name : undefined;
|
|
438
398
|
if (!name) return;
|
|
439
|
-
|
|
440
399
|
if (cmp.type === "ArrowFunctionExpression") {
|
|
441
400
|
var arrowToken = sourceCode.getTokenBefore(cmp.body);
|
|
442
401
|
var fixes = [fixer.replaceText(arrowToken, ""), fixer.insertTextBefore(cmp, "function ".concat(name))];
|
|
443
|
-
|
|
444
402
|
if (cmp.body.type !== "BlockStatement") {
|
|
445
403
|
fixes.push(fixer.insertTextBefore(cmp.body, "{ return "), fixer.insertTextAfter(cmp.body, " }"));
|
|
446
404
|
}
|
|
447
|
-
|
|
448
405
|
return fixes;
|
|
449
406
|
}
|
|
450
|
-
|
|
451
407
|
if (cmp.type === "FunctionExpression") {
|
|
452
408
|
var functionToken = sourceCode.getFirstToken(cmp);
|
|
453
409
|
return fixer.replaceText(functionToken, "function ".concat(name));
|
|
454
410
|
}
|
|
455
|
-
|
|
456
411
|
if (cmp.type === "ClassExpression") {
|
|
457
412
|
var classToken = sourceCode.getFirstToken(cmp);
|
|
458
413
|
return fixer.replaceText(classToken, "class ".concat(name));
|
|
459
414
|
}
|
|
460
415
|
};
|
|
461
|
-
|
|
462
416
|
context.report({
|
|
463
417
|
node: cmp,
|
|
464
418
|
messageId: "observerComponentMustHaveName",
|
|
@@ -467,7 +421,6 @@ function create(context) {
|
|
|
467
421
|
}
|
|
468
422
|
};
|
|
469
423
|
}
|
|
470
|
-
|
|
471
424
|
var noAnonymousObserver$1 = {
|
|
472
425
|
meta: {
|
|
473
426
|
type: "problem",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-mobx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "ESLint rules for MobX",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
32
|
-
"@typescript-eslint/parser": "^
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
|
32
|
+
"@typescript-eslint/parser": "^5.0.0",
|
|
33
33
|
"eslint": "^7.0.0",
|
|
34
34
|
"@babel/core": "^7.16.0",
|
|
35
35
|
"@babel/preset-env": "^7.16.4",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"test": "jest",
|
|
49
49
|
"build": "yarn rollup --config",
|
|
50
|
-
"
|
|
50
|
+
"prepublishOnly": "yarn build"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -1,55 +1,62 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict"
|
|
2
2
|
|
|
3
|
-
const { findAncestor, isMobxDecorator } = require(
|
|
3
|
+
const { findAncestor, isMobxDecorator } = require("./utils.js")
|
|
4
4
|
|
|
5
5
|
// TODO support this.foo = 5; in constructor
|
|
6
6
|
// TODO? report on field as well
|
|
7
7
|
function create(context) {
|
|
8
|
-
|
|
8
|
+
const sourceCode = context.getSourceCode()
|
|
9
|
+
const autofixAnnotation = context.options[0]?.autofixAnnotation ?? true
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
function fieldToKey(field) {
|
|
12
|
+
// TODO cache on field?
|
|
13
|
+
const key = sourceCode.getText(field.key)
|
|
14
|
+
return field.computed ? `[${key}]` : key
|
|
15
|
+
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
17
|
+
return {
|
|
18
|
+
'CallExpression[callee.name="makeObservable"]': makeObservable => {
|
|
19
|
+
// Only interested about makeObservable(this, ...) in constructor or makeObservable({}, ...)
|
|
20
|
+
// ClassDeclaration
|
|
21
|
+
// ClassBody
|
|
22
|
+
// MethodDefinition[kind="constructor"]
|
|
23
|
+
// FunctionExpression
|
|
24
|
+
// BlockStatement
|
|
25
|
+
// ExpressionStatement
|
|
26
|
+
// CallExpression[callee.name="makeObservable"]
|
|
27
|
+
const [firstArg, secondArg] = makeObservable.arguments
|
|
28
|
+
if (!firstArg) return
|
|
29
|
+
let members
|
|
30
|
+
if (firstArg.type === "ThisExpression") {
|
|
31
|
+
const closestFunction = findAncestor(
|
|
32
|
+
makeObservable,
|
|
33
|
+
node =>
|
|
34
|
+
node.type === "FunctionExpression" || node.type === "FunctionDeclaration"
|
|
35
|
+
)
|
|
36
|
+
if (closestFunction?.parent?.kind !== "constructor") return
|
|
37
|
+
members = closestFunction.parent.parent.parent.body.body
|
|
38
|
+
} else if (firstArg.type === "ObjectExpression") {
|
|
39
|
+
members = firstArg.properties
|
|
40
|
+
} else {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
const annotationProps = secondArg?.properties || []
|
|
45
|
+
const nonAnnotatedMembers = []
|
|
46
|
+
let hasAnyDecorator = false
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
members.forEach(member => {
|
|
49
|
+
if (member.static) return
|
|
50
|
+
if (member.kind === "constructor") return
|
|
51
|
+
//if (member.type !== 'MethodDefinition' && member.type !== 'ClassProperty') return;
|
|
52
|
+
hasAnyDecorator =
|
|
53
|
+
hasAnyDecorator || member.decorators?.some(isMobxDecorator) || false
|
|
54
|
+
if (!annotationProps.some(prop => fieldToKey(prop) === fieldToKey(member))) {
|
|
55
|
+
// TODO optimize?
|
|
56
|
+
nonAnnotatedMembers.push(member)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
/*
|
|
53
60
|
// With decorators, second arg must be null/undefined or not provided
|
|
54
61
|
if (hasAnyDecorator && secondArg && secondArg.name !== "undefined" && secondArg.value !== null) {
|
|
55
62
|
context.report({
|
|
@@ -66,46 +73,59 @@ function create(context) {
|
|
|
66
73
|
}
|
|
67
74
|
*/
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
if (!hasAnyDecorator && nonAnnotatedMembers.length) {
|
|
77
|
+
// Set avoids reporting twice for setter+getter pair or actual duplicates
|
|
78
|
+
const keys = [...new Set(nonAnnotatedMembers.map(fieldToKey))]
|
|
79
|
+
const keyList = keys.map(key => `\`${key}\``).join(", ")
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
const fix = fixer => {
|
|
82
|
+
const annotationList =
|
|
83
|
+
keys.map(key => `${key}: ${autofixAnnotation}`).join(", ") + ","
|
|
84
|
+
if (!secondArg) {
|
|
85
|
+
return fixer.insertTextAfter(firstArg, `, { ${annotationList} }`)
|
|
86
|
+
} else if (secondArg.type !== "ObjectExpression") {
|
|
87
|
+
return fixer.replaceText(secondArg, `{ ${annotationList} }`)
|
|
88
|
+
} else {
|
|
89
|
+
const openingBracket = sourceCode.getFirstToken(secondArg)
|
|
90
|
+
return fixer.insertTextAfter(openingBracket, ` ${annotationList} `)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
context.report({
|
|
95
|
+
node: makeObservable,
|
|
96
|
+
messageId: "missingAnnotation",
|
|
97
|
+
data: { keyList },
|
|
98
|
+
fix
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
module.exports = {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
meta: {
|
|
107
|
+
type: "suggestion",
|
|
108
|
+
fixable: "code",
|
|
109
|
+
schema: [
|
|
110
|
+
{
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
autofixAnnotation: {
|
|
114
|
+
type: "boolean"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
additionalProperties: false
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
docs: {
|
|
121
|
+
description: "enforce all fields being listen in `makeObservable`",
|
|
122
|
+
recommended: true,
|
|
123
|
+
suggestion: false
|
|
124
|
+
},
|
|
125
|
+
messages: {
|
|
126
|
+
missingAnnotation:
|
|
127
|
+
"Missing annotation for {{ keyList }}. To exclude a field, use `false` as annotation."
|
|
128
|
+
}
|
|
108
129
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
};
|
|
130
|
+
create
|
|
131
|
+
}
|