UNPKG

3.22 kBJavaScriptView Raw
1const {docsUrl} = require('../utilities');
2
3module.exports = {
4 meta: {
5 docs: {
6 description:
7 'Prefer class properties to assignment of literals in constructors.',
8 category: 'Stylistic Issues',
9 recommended: false,
10 uri: docsUrl('prefer-class-properties'),
11 },
12 schema: [
13 {
14 enum: ['always', 'never'],
15 },
16 ],
17 },
18
19 create(context) {
20 const applyAlways = (context.options[0] || 'always') === 'always';
21
22 function isSimpleLiteralProperty(prop) {
23 return !prop.computed && isSimpleLiteral(prop.value);
24 }
25
26 function isSimpleLiteral(node) {
27 return (
28 node.type === 'Literal' ||
29 (node.type === 'MemberExpression' && isSimpleLiteral(node.object)) ||
30 (node.type === 'CallExpression' && isSimpleLiteral(node.callee)) ||
31 (node.type === 'ArrayExpression' &&
32 node.elements.every(isSimpleLiteral)) ||
33 (node.type === 'ObjectExpression' &&
34 node.properties.every(isSimpleLiteralProperty))
35 );
36 }
37
38 function isStaticMemberExpression(node) {
39 while (node && node.type === 'MemberExpression') {
40 if (node.computed && node.property.type !== 'Literal') {
41 return false;
42 }
43 // eslint-disable-next-line no-param-reassign
44 node = node.object;
45 }
46
47 return true;
48 }
49
50 function checkConstructorThisAssignment(node) {
51 if (isSimpleLiteral(node.right) && isStaticMemberExpression(node.left)) {
52 context.report({
53 node,
54 message: 'Unexpected assignment of literal instance member.',
55 });
56 }
57 }
58
59 function getTopLevelThisAssignmentExpressions(functionNode) {
60 return functionNode.body.body
61 .filter((bodyNode) => {
62 return (
63 bodyNode.type === 'ExpressionStatement' &&
64 bodyNode.expression.type === 'AssignmentExpression' &&
65 bodyNode.expression.left.type === 'MemberExpression' &&
66 bodyNode.expression.left.object.type === 'ThisExpression'
67 );
68 })
69 .map((bodyNode) => {
70 return bodyNode.expression;
71 });
72 }
73
74 function getConstructor(classNode) {
75 return classNode.body.body.find((propertyNode) => {
76 return (
77 propertyNode.type === 'MethodDefinition' &&
78 propertyNode.key.name === 'constructor'
79 );
80 });
81 }
82
83 function getClassInstanceProperties(classNode) {
84 return classNode.body.body.filter((propertyNode) => {
85 return propertyNode.type === 'ClassProperty' && !propertyNode.static;
86 });
87 }
88
89 function checkClassDeclaration(node) {
90 if (applyAlways) {
91 const constructor = getConstructor(node);
92 if (!constructor) {
93 return;
94 }
95
96 getTopLevelThisAssignmentExpressions(constructor.value).forEach(
97 checkConstructorThisAssignment,
98 );
99 } else {
100 getClassInstanceProperties(node).forEach((propertyNode) => {
101 context.report({
102 node: propertyNode,
103 message: 'Unexpected class property.',
104 });
105 });
106 }
107 }
108
109 return {
110 ClassDeclaration: checkClassDeclaration,
111 };
112 },
113};