eslint-plugin-mobx 0.0.13 → 0.1.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.
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "eslint";
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function create(context) {
4
+ var _a;
5
+ var sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
6
+ return {
7
+ "FunctionDeclaration,FunctionExpression,ArrowFunctionExpression,ClassDeclaration,ClassExpression": function (cmp) {
8
+ var _a, _b;
9
+ if (cmp.parent &&
10
+ cmp.parent.type === "CallExpression" &&
11
+ cmp.parent.callee.name === "observer") {
12
+ // observer(...)
13
+ return;
14
+ }
15
+ var forwardRef = cmp.parent &&
16
+ cmp.parent.type === "CallExpression" &&
17
+ cmp.parent.callee.name === "forwardRef"
18
+ ? cmp.parent
19
+ : undefined;
20
+ if (forwardRef &&
21
+ forwardRef.parent &&
22
+ forwardRef.parent.type === "CallExpression" &&
23
+ forwardRef.parent.callee.name === "observer") {
24
+ // forwardRef(observer(...))
25
+ return;
26
+ }
27
+ var cmpOrForwardRef = forwardRef || cmp;
28
+ var name = (_a = cmp.id) === null || _a === void 0 ? void 0 : _a.name;
29
+ // If anonymous try to infer name from variable declaration
30
+ if (!name && ((_b = cmpOrForwardRef.parent) === null || _b === void 0 ? void 0 : _b.type) === "VariableDeclarator") {
31
+ name = cmpOrForwardRef.parent.id.name;
32
+ }
33
+ if (cmp.type.startsWith("Class")) {
34
+ // Must extend Component or React.Component
35
+ var superClass = cmp.superClass;
36
+ if (!superClass) {
37
+ // not a component
38
+ return;
39
+ }
40
+ var superClassText = sourceCode.getText(superClass);
41
+ if (superClassText !== "Component" && superClassText !== "React.Component") {
42
+ // not a component
43
+ return;
44
+ }
45
+ }
46
+ else {
47
+ // Name must start with uppercase letter
48
+ if (!(name === null || name === void 0 ? void 0 : name.charAt(0).match(/^[A-Z]$/))) {
49
+ // not a component
50
+ return;
51
+ }
52
+ }
53
+ var fix = function (fixer) {
54
+ return [
55
+ fixer.insertTextBefore(sourceCode.getFirstToken(cmpOrForwardRef), (name && cmp.type.endsWith("Declaration") ? "const ".concat(name, " = ") : "") +
56
+ "observer("),
57
+ fixer.insertTextAfter(sourceCode.getLastToken(cmpOrForwardRef), ")")
58
+ ];
59
+ };
60
+ context.report({
61
+ node: cmp,
62
+ messageId: "missingObserver",
63
+ data: {
64
+ name: name || "<anonymous>"
65
+ },
66
+ fix: fix
67
+ });
68
+ }
69
+ };
70
+ }
71
+ var rule = {
72
+ meta: {
73
+ type: "problem",
74
+ fixable: "code",
75
+ docs: {
76
+ description: "prevents missing `observer` on react component",
77
+ recommended: true
78
+ },
79
+ messages: {
80
+ missingObserver: "Component `{{ name }}` is missing `observer`."
81
+ }
82
+ },
83
+ create: create
84
+ };
85
+ exports.default = rule;
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "eslint";
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function create(context) {
4
+ var _a;
5
+ var sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
6
+ return {
7
+ 'CallExpression[callee.name="observer"]': function (observer) {
8
+ var _a;
9
+ var cmp = observer.arguments[0];
10
+ if (!cmp)
11
+ return;
12
+ if ((_a = cmp === null || cmp === void 0 ? void 0 : cmp.id) === null || _a === void 0 ? void 0 : _a.name)
13
+ return;
14
+ var fix = function (fixer) {
15
+ var _a;
16
+ // Use name from variable for autofix
17
+ var name = ((_a = observer.parent) === null || _a === void 0 ? void 0 : _a.type) === "VariableDeclarator"
18
+ ? observer.parent.id.name
19
+ : undefined;
20
+ if (!name)
21
+ return;
22
+ if (cmp.type === "ArrowFunctionExpression") {
23
+ var arrowToken = sourceCode.getTokenBefore(cmp.body);
24
+ var fixes = [
25
+ fixer.replaceText(arrowToken, ""),
26
+ fixer.insertTextBefore(cmp, "function ".concat(name))
27
+ ];
28
+ if (cmp.body.type !== "BlockStatement") {
29
+ fixes.push(fixer.insertTextBefore(cmp.body, "{ return "), fixer.insertTextAfter(cmp.body, " }"));
30
+ }
31
+ return fixes;
32
+ }
33
+ if (cmp.type === "FunctionExpression") {
34
+ var functionToken = sourceCode.getFirstToken(cmp);
35
+ return fixer.replaceText(functionToken, "function ".concat(name));
36
+ }
37
+ if (cmp.type === "ClassExpression") {
38
+ var classToken = sourceCode.getFirstToken(cmp);
39
+ return fixer.replaceText(classToken, "class ".concat(name));
40
+ }
41
+ };
42
+ context.report({
43
+ node: cmp,
44
+ messageId: "observerComponentMustHaveName",
45
+ fix: fix
46
+ });
47
+ }
48
+ };
49
+ }
50
+ var rule = {
51
+ meta: {
52
+ type: "problem",
53
+ fixable: "code",
54
+ docs: {
55
+ description: "forbids anonymous functions or classes as `observer` components",
56
+ recommended: true
57
+ },
58
+ messages: {
59
+ observerComponentMustHaveName: "`observer` component must have a name."
60
+ }
61
+ },
62
+ create: create
63
+ };
64
+ exports.default = rule;
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
13
+ }
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ var utils_1 = require("./utils");
20
+ function create(context) {
21
+ return {
22
+ 'CallExpression[callee.name=/(makeObservable|makeAutoObservable)/]': function (makeObservable) {
23
+ var _a;
24
+ // Only interested about makeObservable(this, ...) inside constructor and not inside nested bindable function
25
+ var _b = __read(makeObservable.arguments, 1), firstArg = _b[0];
26
+ if (!firstArg)
27
+ return;
28
+ if (firstArg.type !== 'ThisExpression')
29
+ return;
30
+ // MethodDefinition[key.name="constructor"][kind="constructor"]
31
+ // FunctionExpression
32
+ // BlockStatement
33
+ // ExpressionStatement
34
+ // CallExpression[callee.name="makeObservable"]
35
+ var closestFunction = (0, utils_1.findAncestor)(makeObservable, function (node) { return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration'; });
36
+ if (((_a = closestFunction === null || closestFunction === void 0 ? void 0 : closestFunction.parent) === null || _a === void 0 ? void 0 : _a.kind) !== 'constructor')
37
+ return;
38
+ if (makeObservable.parent.parent.parent !== closestFunction) {
39
+ context.report({
40
+ node: makeObservable,
41
+ messageId: 'mustCallUnconditionally',
42
+ data: {
43
+ name: makeObservable.callee.name,
44
+ }
45
+ });
46
+ }
47
+ },
48
+ };
49
+ }
50
+ var rule = {
51
+ meta: {
52
+ type: 'problem',
53
+ docs: {
54
+ description: 'disallows calling `makeObservable(this)` conditionally inside constructors',
55
+ recommended: true,
56
+ },
57
+ messages: {
58
+ mustCallUnconditionally: '`{{ name }}` must be called unconditionally inside constructor.',
59
+ }
60
+ },
61
+ create: create,
62
+ };
63
+ exports.default = rule;
@@ -0,0 +1,3 @@
1
+ export declare function isMobxDecorator(decorator: any): boolean;
2
+ export declare function findAncestor(node: any, match: any): any;
3
+ export declare function assert(expr: any, error?: any): void;
package/dist/utils.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isMobxDecorator = isMobxDecorator;
4
+ exports.findAncestor = findAncestor;
5
+ exports.assert = assert;
6
+ var mobxDecorators = new Set(['observable', 'computed', 'action', 'flow', 'override']);
7
+ function isMobxDecorator(decorator) {
8
+ var _a, _b;
9
+ return mobxDecorators.has(decorator.expression.name) // @foo
10
+ || mobxDecorators.has((_a = decorator.expression.callee) === null || _a === void 0 ? void 0 : _a.name) // @foo()
11
+ || mobxDecorators.has((_b = decorator.expression.object) === null || _b === void 0 ? void 0 : _b.name); // @foo.bar
12
+ }
13
+ function findAncestor(node, match) {
14
+ var parent = node.parent;
15
+ if (!parent)
16
+ return;
17
+ if (match(parent))
18
+ return parent;
19
+ return findAncestor(parent, match);
20
+ }
21
+ function assert(expr, error) {
22
+ if (!expr) {
23
+ error !== null && error !== void 0 ? error : (error = 'Assertion failed');
24
+ error = error instanceof Error ? error : new Error(error);
25
+ throw error;
26
+ }
27
+ }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "eslint-plugin-mobx",
3
- "version": "0.0.13",
3
+ "version": "0.1.0",
4
4
  "description": "ESLint rules for MobX",
5
5
  "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "https://github.com/mobxjs/mobx.git",
@@ -25,20 +26,15 @@
25
26
  ],
26
27
  "homepage": "https://mobx.js.org/",
27
28
  "peerDependencies": {
28
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
29
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
30
+ },
31
+ "dependencies": {
32
+ "@types/eslint": "^9.6.1",
33
+ "@types/estree": "^1.0.0"
29
34
  },
30
35
  "devDependencies": {
31
- "@babel/core": "^7.16.0",
32
- "@babel/preset-env": "^7.16.4",
33
- "@rollup/plugin-babel": "^5.3.0",
34
- "@rollup/plugin-commonjs": "^21.0.1",
35
- "@rollup/plugin-node-resolve": "13.0.6",
36
- "@typescript-eslint/eslint-plugin": "^5.0.0",
37
- "@typescript-eslint/parser": "^5.0.0",
38
- "eslint": "^7.0.0",
39
36
  "eslint-7": "npm:eslint@^7.0.0",
40
- "eslint-9": "npm:eslint@^9.0.0",
41
- "rollup": "^2.60.2"
37
+ "eslint-9": "npm:eslint@^9.0.0"
42
38
  },
43
39
  "keywords": [
44
40
  "eslint",
@@ -50,7 +46,10 @@
50
46
  "test:7": "jest --config jest.config-eslint-7.js",
51
47
  "test:9": "jest --config jest.config-eslint-9.js",
52
48
  "test": "npm run test:7 && npm run test:9",
53
- "build": "yarn rollup --config",
54
- "prepublishOnly": "yarn build"
49
+ "test:types": "tsc --noEmit",
50
+ "test:check": "npm run test:types",
51
+ "prebuild": "rm -rf dist",
52
+ "build": "tsc",
53
+ "prepublishOnly": "npm run build"
55
54
  }
56
55
  }
@@ -1,11 +1,11 @@
1
- "use strict"
1
+ import type { Rule } from "eslint"
2
2
 
3
- const { findAncestor, isMobxDecorator } = require("./utils.js")
3
+ import { findAncestor, isMobxDecorator } from "./utils"
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.sourceCode ?? context.getSourceCode()
9
9
  const autofixAnnotation = context.options[0]?.autofixAnnotation ?? true
10
10
 
11
11
  function fieldToKey(field) {
@@ -42,7 +42,7 @@ function create(context) {
42
42
  }
43
43
 
44
44
  const annotationProps = secondArg?.properties || []
45
- const nonAnnotatedMembers = []
45
+ const nonAnnotatedMembers: any[] = []
46
46
  let hasAnyDecorator = false
47
47
 
48
48
  members.forEach(member => {
@@ -102,7 +102,7 @@ function create(context) {
102
102
  }
103
103
  }
104
104
 
105
- module.exports = {
105
+ const rule: Rule.RuleModule = {
106
106
  meta: {
107
107
  type: "suggestion",
108
108
  fixable: "code",
@@ -119,8 +119,7 @@ module.exports = {
119
119
  ],
120
120
  docs: {
121
121
  description: "enforce all fields being listen in `makeObservable`",
122
- recommended: true,
123
- suggestion: false
122
+ recommended: true
124
123
  },
125
124
  messages: {
126
125
  missingAnnotation:
@@ -129,3 +128,5 @@ module.exports = {
129
128
  },
130
129
  create
131
130
  }
131
+
132
+ export default rule
@@ -1,17 +1,17 @@
1
- "use strict"
1
+ import fs from "fs"
2
+ import path from "path"
2
3
 
3
- const fs = require("fs")
4
- const path = require("path")
4
+ import type { ESLint, Linter } from "eslint"
5
5
 
6
- const exhaustiveMakeObservable = require("./exhaustive-make-observable.js")
7
- const unconditionalMakeObservable = require("./unconditional-make-observable.js")
8
- const missingMakeObservable = require("./missing-make-observable.js")
9
- const missingObserver = require("./missing-observer")
10
- const noAnonymousObserver = require("./no-anonymous-observer.js")
6
+ import exhaustiveMakeObservable from "./exhaustive-make-observable"
7
+ import unconditionalMakeObservable from "./unconditional-make-observable"
8
+ import missingMakeObservable from "./missing-make-observable"
9
+ import missingObserver from "./missing-observer"
10
+ import noAnonymousObserver from "./no-anonymous-observer"
11
11
 
12
12
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"))
13
13
 
14
- const pluginMobx = {
14
+ const pluginMobx: ESLint.Plugin = {
15
15
  meta: {
16
16
  name: pkg.name,
17
17
  version: pkg.version
@@ -25,14 +25,17 @@ const pluginMobx = {
25
25
  }
26
26
  }
27
27
 
28
- const recommendedRules = {
28
+ const recommendedRules: Linter.RulesRecord = {
29
29
  "mobx/exhaustive-make-observable": "warn",
30
30
  "mobx/unconditional-make-observable": "error",
31
31
  "mobx/missing-make-observable": "error",
32
32
  "mobx/missing-observer": "warn"
33
33
  }
34
34
 
35
- module.exports = {
35
+ const plugin: typeof pluginMobx & {
36
+ configs: { recommended: Linter.LegacyConfig }
37
+ flatConfigs: { recommended: Linter.Config }
38
+ } = {
36
39
  ...pluginMobx,
37
40
  configs: {
38
41
  recommended: {
@@ -48,3 +51,5 @@ module.exports = {
48
51
  }
49
52
  }
50
53
  }
54
+
55
+ export = plugin
@@ -1,9 +1,9 @@
1
- 'use strict';
1
+ import type { Rule } from 'eslint';
2
2
 
3
- const { findAncestor, isMobxDecorator } = require('./utils.js');
3
+ import { findAncestor, isMobxDecorator } from './utils';
4
4
 
5
5
  function create(context) {
6
- const sourceCode = context.getSourceCode();
6
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
7
7
 
8
8
  return {
9
9
  'Decorator': decorator => {
@@ -53,14 +53,13 @@ function create(context) {
53
53
  };
54
54
  }
55
55
 
56
- module.exports = {
56
+ const rule: Rule.RuleModule = {
57
57
  meta: {
58
58
  type: 'problem',
59
59
  fixable: 'code',
60
60
  docs: {
61
61
  description: 'prevents missing `makeObservable(this)` when using decorators',
62
62
  recommended: true,
63
- suggestion: false,
64
63
  },
65
64
  messages: {
66
65
  missingMakeObservable: "Constructor is missing `makeObservable(this)`.",
@@ -68,4 +67,6 @@ module.exports = {
68
67
  },
69
68
  },
70
69
  create,
71
- };
70
+ };
71
+
72
+ export default rule;
@@ -1,7 +1,7 @@
1
- "use strict"
1
+ import type { Rule } from "eslint"
2
2
 
3
3
  function create(context) {
4
- const sourceCode = context.getSourceCode()
4
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
5
5
 
6
6
  return {
7
7
  "FunctionDeclaration,FunctionExpression,ArrowFunctionExpression,ClassDeclaration,ClassExpression":
@@ -78,7 +78,7 @@ function create(context) {
78
78
  }
79
79
  }
80
80
 
81
- module.exports = {
81
+ const rule: Rule.RuleModule = {
82
82
  meta: {
83
83
  type: "problem",
84
84
  fixable: "code",
@@ -92,3 +92,5 @@ module.exports = {
92
92
  },
93
93
  create
94
94
  }
95
+
96
+ export default rule
@@ -1,7 +1,7 @@
1
- "use strict"
1
+ import type { Rule } from "eslint"
2
2
 
3
3
  function create(context) {
4
- const sourceCode = context.getSourceCode()
4
+ const sourceCode = context.sourceCode ?? context.getSourceCode()
5
5
 
6
6
  return {
7
7
  'CallExpression[callee.name="observer"]': observer => {
@@ -49,7 +49,7 @@ function create(context) {
49
49
  }
50
50
  }
51
51
 
52
- module.exports = {
52
+ const rule: Rule.RuleModule = {
53
53
  meta: {
54
54
  type: "problem",
55
55
  fixable: "code",
@@ -63,3 +63,5 @@ module.exports = {
63
63
  },
64
64
  create
65
65
  }
66
+
67
+ export default rule
@@ -1,11 +1,11 @@
1
- 'use strict';
1
+ import type { Rule } from 'eslint';
2
2
 
3
- const { findAncestor } = require('./utils.js');
3
+ import { findAncestor } from './utils';
4
4
 
5
5
  function create(context) {
6
6
  return {
7
7
  'CallExpression[callee.name=/(makeObservable|makeAutoObservable)/]': makeObservable => {
8
- // Only iterested about makeObservable(this, ...) inside constructor and not inside nested bindable function
8
+ // Only interested about makeObservable(this, ...) inside constructor and not inside nested bindable function
9
9
  const [firstArg] = makeObservable.arguments;
10
10
  if (!firstArg) return;
11
11
  if (firstArg.type !== 'ThisExpression') return;
@@ -29,7 +29,7 @@ function create(context) {
29
29
  };
30
30
  }
31
31
 
32
- module.exports = {
32
+ const rule: Rule.RuleModule = {
33
33
  meta: {
34
34
  type: 'problem',
35
35
  docs: {
@@ -41,4 +41,6 @@ module.exports = {
41
41
  }
42
42
  },
43
43
  create,
44
- }
44
+ };
45
+
46
+ export default rule;
@@ -1,29 +1,22 @@
1
- 'use strict';
2
-
3
1
  const mobxDecorators = new Set(['observable', 'computed', 'action', 'flow', 'override']);
4
2
 
5
- function isMobxDecorator(decorator) {
3
+ export function isMobxDecorator(decorator) {
6
4
  return mobxDecorators.has(decorator.expression.name) // @foo
7
5
  || mobxDecorators.has(decorator.expression.callee?.name) // @foo()
8
6
  || mobxDecorators.has(decorator.expression.object?.name) // @foo.bar
9
7
  }
10
8
 
11
- function findAncestor(node, match) {
9
+ export function findAncestor(node, match) {
12
10
  const { parent } = node;
13
11
  if (!parent) return;
14
12
  if (match(parent)) return parent;
15
13
  return findAncestor(parent, match);
16
14
  }
17
15
 
18
- function assert(expr, error) {
16
+ export function assert(expr, error?) {
19
17
  if (!expr) {
20
18
  error ??= 'Assertion failed';
21
19
  error = error instanceof Error ? error : new Error(error)
22
20
  throw error;
23
21
  }
24
22
  }
25
-
26
- module.exports = {
27
- findAncestor,
28
- isMobxDecorator,
29
- }