eslint-plugin-mobx 0.0.8 → 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 CHANGED
@@ -1,5 +1,19 @@
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
+
11
+ ## 0.0.9
12
+
13
+ ### Patch Changes
14
+
15
+ - [`e63c2df0`](https://github.com/mobxjs/mobx/commit/e63c2df0ef166868675bce21892cd686a46db953) [#3443](https://github.com/mobxjs/mobx/pull/3443) Thanks [@urugator](https://github.com/urugator)! - changeset for PR #3423: deprecate no-anonymous-observer
16
+
3
17
  ## 0.0.8
4
18
 
5
19
  ### 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
 
@@ -82,9 +94,26 @@ It's a bit opinionated and can lead to a lot of false positives depending on you
82
94
  ]
83
95
  ```
84
96
 
85
- ### mobx/no-anonymous-observer
97
+ ### mobx/no-anonymous-observer (deprecated)
98
+
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
+
101
+ ```
102
+ {
103
+ "rules": {
104
+ "react/display-name": "warn"
105
+ },
106
+ "settings": {
107
+ "componentWrapperFunctions": [
108
+ "observer"
109
+ ]
110
+ }
111
+ }
112
+ ```
113
+
114
+ ---
86
115
 
87
116
  Forbids anonymous functions or classes as `observer` components.
88
117
  Improves debugging experience and [avoids problem with inability to customize `displayName`](https://github.com/mobxjs/mobx/issues/2721).
89
- Plays nice with `eslint-plugin-react-hooks` and `mobx/missing-observer` as both of these don't not recognize anonymous function as component.
118
+ Plays nice with `eslint-plugin-react-hooks` and `mobx/missing-observer` as both of these don't recognize anonymous function as component.
90
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
- isMobxDecorator$1 = utils.isMobxDecorator; // TODO support this.foo = 5; in constructor
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
- firstArg = _makeObservable$argum[0],
125
- secondArg = _makeObservable$argum[1];
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 === 'FunctionExpression' || node.type === 'FunctionDeclaration';
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) !== 'constructor') return;
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 === 'ObjectExpression') {
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; //if (member.type !== 'MethodDefinition' && member.type !== 'ClassProperty') 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
- context.report({
166
- node: makeObservable,
167
- message: 'When using decorators, second arg must be `null`, `undefined` or not provided.',
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
- context.report({
173
- node: makeObservable,
174
- message: 'Second argument must be object in form of `{ key: annotation }`.',
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, ": true");
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 !== 'ObjectExpression') {
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: 'missingAnnotation',
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: 'suggestion',
218
- fixable: 'code',
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: 'enforce all fields being listen in `makeObservable`',
203
+ description: "enforce all fields being listen in `makeObservable`",
221
204
  recommended: true,
222
205
  suggestion: false
223
206
  },
224
207
  messages: {
225
- 'missingAnnotation': 'Missing annotation for {{ keyList }}. To exclude a field, use `false` as annotation.'
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
- firstArg = _makeObservable$argum[0];
241
-
221
+ firstArg = _makeObservable$argum[0];
242
222
  if (!firstArg) return;
243
- if (firstArg.type !== 'ThisExpression') return; // MethodDefinition[key.name="constructor"][kind="constructor"]
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
- isMobxDecorator = utils.isMobxDecorator;
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; // ClassDeclaration > ClassBody > []
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
- }); // MethodDefinition > FunctionExpression > BlockStatement > []
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; // If anonymous try to infer name from variable declaration
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",
@@ -496,8 +449,7 @@ var src = {
496
449
  "mobx/exhaustive-make-observable": "warn",
497
450
  "mobx/unconditional-make-observable": "error",
498
451
  "mobx/missing-make-observable": "error",
499
- "mobx/missing-observer": "warn",
500
- "mobx/no-anonymous-observer": "warn"
452
+ "mobx/missing-observer": "warn"
501
453
  }
502
454
  }
503
455
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-mobx",
3
- "version": "0.0.8",
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": "^4.29.3",
32
- "@typescript-eslint/parser": "^4.0.0",
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
- "prepublish": "yarn build"
50
+ "prepublishOnly": "yarn build"
51
51
  }
52
52
  }
@@ -1,55 +1,62 @@
1
- 'use strict';
1
+ "use strict"
2
2
 
3
- const { findAncestor, isMobxDecorator } = require('./utils.js');
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
- const sourceCode = context.getSourceCode();
8
+ const sourceCode = context.getSourceCode()
9
+ const autofixAnnotation = context.options[0]?.autofixAnnotation ?? true
9
10
 
10
- function fieldToKey(field) {
11
- // TODO cache on field?
12
- const key = sourceCode.getText(field.key);
13
- return field.computed ? `[${key}]` : key;
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
- return {
17
- 'CallExpression[callee.name="makeObservable"]': makeObservable => {
18
- // Only interested about makeObservable(this, ...) in constructor or makeObservable({}, ...)
19
- // ClassDeclaration
20
- // ClassBody
21
- // MethodDefinition[kind="constructor"]
22
- // FunctionExpression
23
- // BlockStatement
24
- // ExpressionStatement
25
- // CallExpression[callee.name="makeObservable"]
26
- const [firstArg, secondArg] = makeObservable.arguments;
27
- if (!firstArg) return;
28
- let members;
29
- if (firstArg.type === 'ThisExpression') {
30
- const closestFunction = findAncestor(makeObservable, node => node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration')
31
- if (closestFunction?.parent?.kind !== 'constructor') return;
32
- members = closestFunction.parent.parent.parent.body.body;
33
- } else if (firstArg.type === 'ObjectExpression') {
34
- members = firstArg.properties;
35
- } else {
36
- return;
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
- const annotationProps = secondArg?.properties || [];
40
- const nonAnnotatedMembers = [];
41
- let hasAnyDecorator = false;
44
+ const annotationProps = secondArg?.properties || []
45
+ const nonAnnotatedMembers = []
46
+ let hasAnyDecorator = false
42
47
 
43
- members.forEach(member => {
44
- if (member.static) return;
45
- if (member.kind === "constructor") return;
46
- //if (member.type !== 'MethodDefinition' && member.type !== 'ClassProperty') return;
47
- hasAnyDecorator = hasAnyDecorator || member.decorators?.some(isMobxDecorator) || false;
48
- if (!annotationProps.some(prop => fieldToKey(prop) === fieldToKey(member))) { // TODO optimize?
49
- nonAnnotatedMembers.push(member);
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
- if (!hasAnyDecorator && nonAnnotatedMembers.length) {
70
- // Set avoids reporting twice for setter+getter pair or actual duplicates
71
- const keys = [...new Set(nonAnnotatedMembers.map(fieldToKey))];
72
- const keyList = keys.map(key => `\`${key}\``).join(', ');
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
- const fix = fixer => {
75
- const annotationList = keys.map(key => `${key}: true`).join(', ') + ',';
76
- if (!secondArg) {
77
- return fixer.insertTextAfter(firstArg, `, { ${annotationList} }`);
78
- } else if (secondArg.type !== 'ObjectExpression') {
79
- return fixer.replaceText(secondArg, `{ ${annotationList} }`);
80
- } else {
81
- const openingBracket = sourceCode.getFirstToken(secondArg)
82
- return fixer.insertTextAfter(openingBracket, ` ${annotationList} `);
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
- context.report({
87
- node: makeObservable,
88
- messageId: 'missingAnnotation',
89
- data: { keyList },
90
- fix,
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
- meta: {
99
- type: 'suggestion',
100
- fixable: 'code',
101
- docs: {
102
- description: 'enforce all fields being listen in `makeObservable`',
103
- recommended: true,
104
- suggestion: false,
105
- },
106
- messages: {
107
- 'missingAnnotation': 'Missing annotation for {{ keyList }}. To exclude a field, use `false` as annotation.',
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
- create,
111
- };
130
+ create
131
+ }
package/src/index.js CHANGED
@@ -14,8 +14,7 @@ module.exports = {
14
14
  "mobx/exhaustive-make-observable": "warn",
15
15
  "mobx/unconditional-make-observable": "error",
16
16
  "mobx/missing-make-observable": "error",
17
- "mobx/missing-observer": "warn",
18
- "mobx/no-anonymous-observer": "warn"
17
+ "mobx/missing-observer": "warn"
19
18
  }
20
19
  }
21
20
  },