eslint-plugin-mobx 0.0.0 → 0.0.5
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 +20 -0
- package/LICENSE +21 -0
- package/README.md +67 -33
- package/dist/index.js +505 -0
- package/package.json +50 -32
- package/src/exhaustive-make-observable.js +111 -0
- package/src/index.js +29 -0
- package/src/missing-make-observable.js +70 -0
- package/src/missing-observer.js +63 -0
- package/src/no-anonymous-observer.js +57 -0
- package/src/unconditional-make-observable.js +44 -0
- package/src/utils.js +29 -0
- package/.editorconfig +0 -15
- package/.eslintrc.js +0 -35
- package/husky.config.js +0 -5
- package/lint-staged.config.js +0 -4
- package/package-lock.json +0 -1293
- package/prettier.config.js +0 -7
- package/todo-rules.md +0 -115
package/package.json
CHANGED
|
@@ -1,34 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
"name": "eslint-plugin-mobx",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "ESLint rules for MobX",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/mobxjs/mobx.git",
|
|
9
|
+
"directory": "packages/eslint-plugin-mobx"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"funding": {
|
|
13
|
+
"type": "opencollective",
|
|
14
|
+
"url": "https://opencollective.com/mobx"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/mobxjs/mobx/issues"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src",
|
|
21
|
+
"dist",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"CHANGELOG.md",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"homepage": "https://mobx.js.org/",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^4.29.3",
|
|
32
|
+
"@typescript-eslint/parser": "^4.0.0",
|
|
33
|
+
"eslint": "^7.0.0",
|
|
34
|
+
"@babel/core": "^7.16.0",
|
|
35
|
+
"@babel/preset-env": "^7.16.4",
|
|
36
|
+
"rollup": "^2.60.2",
|
|
37
|
+
"@rollup/plugin-babel": "^5.3.0",
|
|
38
|
+
"@rollup/plugin-commonjs": "^21.0.1",
|
|
39
|
+
"@rollup/plugin-node-resolve": "13.0.6"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"eslint",
|
|
43
|
+
"eslint-plugin",
|
|
44
|
+
"eslintplugin",
|
|
45
|
+
"mobx"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "jest",
|
|
49
|
+
"build": "yarn rollup --config",
|
|
50
|
+
"prepublish": "yarn build"
|
|
51
|
+
}
|
|
34
52
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { findAncestor, isMobxDecorator } = require('./utils.js');
|
|
4
|
+
|
|
5
|
+
// TODO support this.foo = 5; in constructor
|
|
6
|
+
// TODO? report on field as well
|
|
7
|
+
function create(context) {
|
|
8
|
+
const sourceCode = context.getSourceCode();
|
|
9
|
+
|
|
10
|
+
function fieldToKey(field) {
|
|
11
|
+
// TODO cache on field?
|
|
12
|
+
const key = sourceCode.getText(field.key);
|
|
13
|
+
return field.computed ? `[${key}]` : key;
|
|
14
|
+
}
|
|
15
|
+
|
|
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
|
+
}
|
|
38
|
+
|
|
39
|
+
const annotationProps = secondArg?.properties || [];
|
|
40
|
+
const nonAnnotatedMembers = [];
|
|
41
|
+
let hasAnyDecorator = false;
|
|
42
|
+
|
|
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
|
+
/*
|
|
53
|
+
// With decorators, second arg must be null/undefined or not provided
|
|
54
|
+
if (hasAnyDecorator && secondArg && secondArg.name !== "undefined" && secondArg.value !== null) {
|
|
55
|
+
context.report({
|
|
56
|
+
node: makeObservable,
|
|
57
|
+
message: 'When using decorators, second arg must be `null`, `undefined` or not provided.',
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
// Without decorators, in constructor, second arg must be object literal
|
|
61
|
+
if (!hasAnyDecorator && firstArg.type === 'ThisExpression' && (!secondArg || secondArg.type !== 'ObjectExpression')) {
|
|
62
|
+
context.report({
|
|
63
|
+
node: makeObservable,
|
|
64
|
+
message: 'Second argument must be object in form of `{ key: annotation }`.',
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
*/
|
|
68
|
+
|
|
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(', ');
|
|
73
|
+
|
|
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
|
+
};
|
|
85
|
+
|
|
86
|
+
context.report({
|
|
87
|
+
node: makeObservable,
|
|
88
|
+
messageId: 'missingAnnotation',
|
|
89
|
+
data: { keyList },
|
|
90
|
+
fix,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
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.',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
create,
|
|
111
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const exhaustiveMakeObservable = require('./exhaustive-make-observable.js');
|
|
4
|
+
const unconditionalMakeObservable = require('./unconditional-make-observable.js');
|
|
5
|
+
const missingMakeObservable = require('./missing-make-observable.js');
|
|
6
|
+
const missingObserver = require('./missing-observer');
|
|
7
|
+
const noAnonymousObserver = require('./no-anonymous-observer.js');
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
configs: {
|
|
11
|
+
recommended: {
|
|
12
|
+
plugins: ['mobx'],
|
|
13
|
+
rules: {
|
|
14
|
+
'mobx/exhaustive-make-observable': 'warn',
|
|
15
|
+
'mobx/unconditional-make-observable': 'error',
|
|
16
|
+
'mobx/missing-make-observable': 'error',
|
|
17
|
+
'mobx/no-missing-observer': 'warn',
|
|
18
|
+
'mobx/no-anonymous-observer': 'warn',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
rules: {
|
|
23
|
+
'exhaustive-make-observable': exhaustiveMakeObservable,
|
|
24
|
+
'unconditional-make-observable': unconditionalMakeObservable,
|
|
25
|
+
'missing-make-observable': missingMakeObservable,
|
|
26
|
+
'missing-observer': missingObserver,
|
|
27
|
+
'no-anonymous-observer': noAnonymousObserver,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { findAncestor, isMobxDecorator } = require('./utils.js');
|
|
4
|
+
|
|
5
|
+
function create(context) {
|
|
6
|
+
const sourceCode = context.getSourceCode();
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
'Decorator': decorator => {
|
|
10
|
+
if (!isMobxDecorator(decorator)) return;
|
|
11
|
+
const clazz = findAncestor(decorator, node => node.type === 'ClassDeclaration' || node.type === 'ClassExpression');
|
|
12
|
+
if (!clazz) return;
|
|
13
|
+
// ClassDeclaration > ClassBody > []
|
|
14
|
+
const constructor = clazz.body.body.find(node => node.kind === 'constructor');
|
|
15
|
+
// MethodDefinition > FunctionExpression > BlockStatement > []
|
|
16
|
+
const isMakeObservable = node => node.expression?.callee?.name === 'makeObservable' && node.expression?.arguments[0]?.type === 'ThisExpression';
|
|
17
|
+
const makeObservable = constructor?.value.body?.body.find(isMakeObservable)?.expression;
|
|
18
|
+
|
|
19
|
+
if (makeObservable) {
|
|
20
|
+
// make sure second arg is nullish
|
|
21
|
+
const secondArg = makeObservable.arguments[1];
|
|
22
|
+
if (secondArg && secondArg.value !== null && secondArg.name !== 'undefined') {
|
|
23
|
+
context.report({
|
|
24
|
+
node: makeObservable,
|
|
25
|
+
messageId: 'secondArgMustBeNullish',
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
const fix = fixer => {
|
|
30
|
+
if (constructor?.value.type === 'TSEmptyBodyFunctionExpression') {
|
|
31
|
+
// constructor() - yes this a thing
|
|
32
|
+
const closingBracket = sourceCode.getLastToken(constructor.value);
|
|
33
|
+
return fixer.insertTextAfter(closingBracket, ' { makeObservable(this); }')
|
|
34
|
+
} else if (constructor) {
|
|
35
|
+
// constructor() {}
|
|
36
|
+
const closingBracket = sourceCode.getLastToken(constructor.value.body);
|
|
37
|
+
return fixer.insertTextBefore(closingBracket, ';makeObservable(this);')
|
|
38
|
+
} else {
|
|
39
|
+
// class C {}
|
|
40
|
+
const openingBracket = sourceCode.getFirstToken(clazz.body);
|
|
41
|
+
return fixer.insertTextAfter(openingBracket, '\nconstructor() { makeObservable(this); }')
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
context.report({
|
|
46
|
+
node: clazz,
|
|
47
|
+
messageId: 'missingMakeObservable',
|
|
48
|
+
fix,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
meta: {
|
|
57
|
+
type: 'problem',
|
|
58
|
+
fixable: 'code',
|
|
59
|
+
docs: {
|
|
60
|
+
description: 'prevents missing `makeObservable(this)` when using decorators',
|
|
61
|
+
recommended: true,
|
|
62
|
+
suggestion: false,
|
|
63
|
+
},
|
|
64
|
+
messages: {
|
|
65
|
+
missingMakeObservable: "Constructor is missing `makeObservable(this)`.",
|
|
66
|
+
secondArgMustBeNullish: "`makeObservable`'s second argument must be nullish or not provided when using decorators."
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
create,
|
|
70
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function create(context) {
|
|
4
|
+
const sourceCode = context.getSourceCode();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
'FunctionDeclaration,FunctionExpression,ArrowFunctionExpression,ClassDeclaration,ClassExpression': cmp => {
|
|
8
|
+
// Already has observer
|
|
9
|
+
if (cmp.parent && cmp.parent.type === 'CallExpression' && cmp.parent.callee.name === 'observer') return;
|
|
10
|
+
let name = cmp.id?.name;
|
|
11
|
+
// If anonymous try to infer name from variable declaration
|
|
12
|
+
if (!name && cmp.parent?.type === 'VariableDeclarator') {
|
|
13
|
+
name = cmp.parent.id.name;
|
|
14
|
+
}
|
|
15
|
+
if (cmp.type.startsWith('Class')) {
|
|
16
|
+
// Must extend Component or React.Component
|
|
17
|
+
const { superClass } = cmp;
|
|
18
|
+
if (!superClass) return;
|
|
19
|
+
const superClassText = sourceCode.getText(superClass);
|
|
20
|
+
if (superClassText !== 'Component' && superClassText !== 'React.Component') return;
|
|
21
|
+
} else {
|
|
22
|
+
// Name must start with uppercase letter
|
|
23
|
+
if (!name?.charAt(0).match(/^[A-Z]$/)) return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const fix = fixer => {
|
|
27
|
+
return [
|
|
28
|
+
fixer.insertTextBefore(
|
|
29
|
+
sourceCode.getFirstToken(cmp),
|
|
30
|
+
(name && cmp.type.endsWith('Declaration') ? `const ${name} = ` : '') + 'observer(',
|
|
31
|
+
),
|
|
32
|
+
fixer.insertTextAfter(
|
|
33
|
+
sourceCode.getLastToken(cmp),
|
|
34
|
+
')',
|
|
35
|
+
),
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
context.report({
|
|
39
|
+
node: cmp,
|
|
40
|
+
messageId: 'missingObserver',
|
|
41
|
+
data: {
|
|
42
|
+
name: name || '<anonymous>',
|
|
43
|
+
},
|
|
44
|
+
fix,
|
|
45
|
+
})
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
meta: {
|
|
52
|
+
type: 'problem',
|
|
53
|
+
fixable: 'code',
|
|
54
|
+
docs: {
|
|
55
|
+
description: 'prevents missing `observer` on react component',
|
|
56
|
+
recommended: true,
|
|
57
|
+
},
|
|
58
|
+
messages: {
|
|
59
|
+
missingObserver: "Component `{{ name }}` is missing `observer`.",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
create,
|
|
63
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function create(context) {
|
|
4
|
+
const sourceCode = context.getSourceCode();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
'CallExpression[callee.name="observer"]': observer => {
|
|
8
|
+
const cmp = observer.arguments[0];
|
|
9
|
+
if (!cmp) return;
|
|
10
|
+
if (cmp?.id?.name) return;
|
|
11
|
+
|
|
12
|
+
const fix = fixer => {
|
|
13
|
+
// Use name from variable for autofix
|
|
14
|
+
const name = observer.parent?.type === 'VariableDeclarator'
|
|
15
|
+
? observer.parent.id.name
|
|
16
|
+
: undefined;
|
|
17
|
+
|
|
18
|
+
if (!name) return;
|
|
19
|
+
if (cmp.type === 'ArrowFunctionExpression') {
|
|
20
|
+
const arrowToken = sourceCode.getTokenBefore(cmp.body);
|
|
21
|
+
return [
|
|
22
|
+
fixer.replaceText(arrowToken, ''),
|
|
23
|
+
fixer.insertTextBefore(cmp, `function ${name}`),
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
if (cmp.type === 'FunctionExpression') {
|
|
27
|
+
const functionToken = sourceCode.getFirstToken(cmp);
|
|
28
|
+
return fixer.replaceText(functionToken, `function ${name}`);
|
|
29
|
+
}
|
|
30
|
+
if (cmp.type === 'ClassExpression') {
|
|
31
|
+
const classToken = sourceCode.getFirstToken(cmp);
|
|
32
|
+
return fixer.replaceText(classToken, `class ${name}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
context.report({
|
|
36
|
+
node: cmp,
|
|
37
|
+
messageId: 'observerComponentMustHaveName',
|
|
38
|
+
fix,
|
|
39
|
+
})
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
meta: {
|
|
46
|
+
type: 'problem',
|
|
47
|
+
fixable: 'code',
|
|
48
|
+
docs: {
|
|
49
|
+
description: 'forbids anonymous functions or classes as `observer` components',
|
|
50
|
+
recommended: true,
|
|
51
|
+
},
|
|
52
|
+
messages: {
|
|
53
|
+
observerComponentMustHaveName: "`observer` component must have a name.",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
create,
|
|
57
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { findAncestor } = require('./utils.js');
|
|
4
|
+
|
|
5
|
+
function create(context) {
|
|
6
|
+
return {
|
|
7
|
+
'CallExpression[callee.name=/(makeObservable|makeAutoObservable)/]': makeObservable => {
|
|
8
|
+
// Only iterested about makeObservable(this, ...) inside constructor and not inside nested bindable function
|
|
9
|
+
const [firstArg] = makeObservable.arguments;
|
|
10
|
+
if (!firstArg) return;
|
|
11
|
+
if (firstArg.type !== 'ThisExpression') return;
|
|
12
|
+
// MethodDefinition[key.name="constructor"][kind="constructor"]
|
|
13
|
+
// FunctionExpression
|
|
14
|
+
// BlockStatement
|
|
15
|
+
// ExpressionStatement
|
|
16
|
+
// CallExpression[callee.name="makeObservable"]
|
|
17
|
+
const closestFunction = findAncestor(makeObservable, node => node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration');
|
|
18
|
+
if (closestFunction?.parent?.kind !== 'constructor') return;
|
|
19
|
+
if (makeObservable.parent.parent.parent !== closestFunction) {
|
|
20
|
+
context.report({
|
|
21
|
+
node: makeObservable,
|
|
22
|
+
messageId: 'mustCallUnconditionally',
|
|
23
|
+
data: {
|
|
24
|
+
name: makeObservable.callee.name,
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
meta: {
|
|
34
|
+
type: 'problem',
|
|
35
|
+
docs: {
|
|
36
|
+
description: 'disallows calling `makeObservable(this)` conditionally inside constructors',
|
|
37
|
+
recommended: true,
|
|
38
|
+
},
|
|
39
|
+
messages: {
|
|
40
|
+
mustCallUnconditionally: '`{{ name }}` must be called unconditionally inside constructor.',
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
create,
|
|
44
|
+
}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const mobxDecorators = new Set(['observable', 'computed', 'action', 'flow', 'override']);
|
|
4
|
+
|
|
5
|
+
function isMobxDecorator(decorator) {
|
|
6
|
+
return mobxDecorators.has(decorator.expression.name) // @foo
|
|
7
|
+
|| mobxDecorators.has(decorator.expression.callee?.name) // @foo()
|
|
8
|
+
|| mobxDecorators.has(decorator.expression.object?.name) // @foo.bar
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function findAncestor(node, match) {
|
|
12
|
+
const { parent } = node;
|
|
13
|
+
if (!parent) return;
|
|
14
|
+
if (match(parent)) return parent;
|
|
15
|
+
return findAncestor(parent, match);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assert(expr, error) {
|
|
19
|
+
if (!expr) {
|
|
20
|
+
error ??= 'Assertion failed';
|
|
21
|
+
error = error instanceof Error ? error : new Error(error)
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
findAncestor,
|
|
28
|
+
isMobxDecorator,
|
|
29
|
+
}
|
package/.editorconfig
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
root = true
|
|
2
|
-
|
|
3
|
-
[*]
|
|
4
|
-
indent_style = space
|
|
5
|
-
indent_size = 2
|
|
6
|
-
charset = utf-8
|
|
7
|
-
trim_trailing_whitespace = true
|
|
8
|
-
insert_final_newline = true
|
|
9
|
-
max_line_length = 80
|
|
10
|
-
|
|
11
|
-
[*.md]
|
|
12
|
-
trim_trailing_whitespace = false
|
|
13
|
-
|
|
14
|
-
[{*.json,*.yml}]
|
|
15
|
-
indent_size = 2
|
package/.eslintrc.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
parserOptions: {
|
|
4
|
-
ecmaVersion: '2020',
|
|
5
|
-
sourceType: 'module',
|
|
6
|
-
},
|
|
7
|
-
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
|
|
8
|
-
env: {
|
|
9
|
-
es6: true,
|
|
10
|
-
node: true,
|
|
11
|
-
},
|
|
12
|
-
// 0 - off, 1 - warn, 2 - error
|
|
13
|
-
rules: {
|
|
14
|
-
'prefer-template': 1,
|
|
15
|
-
'no-param-reassign': 2,
|
|
16
|
-
'no-new-wrappers': 2,
|
|
17
|
-
'no-shadow': [1, { hoist: 'functions' }],
|
|
18
|
-
'eol-last': [1, 'always'],
|
|
19
|
-
'lines-between-class-members': [
|
|
20
|
-
2,
|
|
21
|
-
'always',
|
|
22
|
-
{
|
|
23
|
-
exceptAfterSingleLine: true,
|
|
24
|
-
},
|
|
25
|
-
],
|
|
26
|
-
'no-lonely-if': 2,
|
|
27
|
-
'no-console': [
|
|
28
|
-
1,
|
|
29
|
-
{
|
|
30
|
-
allow: ['error'],
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
'object-shorthand': 2,
|
|
34
|
-
},
|
|
35
|
-
}
|
package/husky.config.js
DELETED
package/lint-staged.config.js
DELETED