UNPKG

5.06 kBJavaScriptView Raw
1/**
2 * @fileoverview Enforce consistent usage of destructuring assignment of props, state, and context.
3 */
4
5'use strict';
6
7const Components = require('../util/Components');
8const docsUrl = require('../util/docsUrl');
9const isAssignmentLHS = require('../util/ast').isAssignmentLHS;
10
11const DEFAULT_OPTION = 'always';
12
13module.exports = {
14 meta: {
15 docs: {
16 description: 'Enforce consistent usage of destructuring assignment of props, state, and context',
17 category: 'Stylistic Issues',
18 recommended: false,
19 url: docsUrl('destructuring-assignment')
20 },
21 schema: [{
22 type: 'string',
23 enum: [
24 'always',
25 'never'
26 ]
27 }, {
28 type: 'object',
29 properties: {
30 ignoreClassFields: {
31 type: 'boolean'
32 }
33 },
34 additionalProperties: false
35 }]
36 },
37
38 create: Components.detect((context, components, utils) => {
39 const configuration = context.options[0] || DEFAULT_OPTION;
40 const ignoreClassFields = context.options[1] && context.options[1].ignoreClassFields === true || false;
41
42 /**
43 * @param {ASTNode} node We expect either an ArrowFunctionExpression,
44 * FunctionDeclaration, or FunctionExpression
45 */
46 function handleStatelessComponent(node) {
47 const destructuringProps = node.params && node.params[0] && node.params[0].type === 'ObjectPattern';
48 const destructuringContext = node.params && node.params[1] && node.params[1].type === 'ObjectPattern';
49
50 if (destructuringProps && components.get(node) && configuration === 'never') {
51 context.report({
52 node,
53 message: 'Must never use destructuring props assignment in SFC argument'
54 });
55 } else if (destructuringContext && components.get(node) && configuration === 'never') {
56 context.report({
57 node,
58 message: 'Must never use destructuring context assignment in SFC argument'
59 });
60 }
61 }
62
63 function handleSFCUsage(node) {
64 // props.aProp || context.aProp
65 const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentLHS(node);
66 if (isPropUsed && configuration === 'always') {
67 context.report({
68 node,
69 message: `Must use destructuring ${node.object.name} assignment`
70 });
71 }
72 }
73
74 function isInClassProperty(node) {
75 let curNode = node.parent;
76 while (curNode) {
77 if (curNode.type === 'ClassProperty') {
78 return true;
79 }
80 curNode = curNode.parent;
81 }
82 return false;
83 }
84
85 function handleClassUsage(node) {
86 // this.props.Aprop || this.context.aProp || this.state.aState
87 const isPropUsed = (
88 node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression'
89 && (node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state')
90 && !isAssignmentLHS(node)
91 );
92
93 if (
94 isPropUsed && configuration === 'always'
95 && !(ignoreClassFields && isInClassProperty(node))
96 ) {
97 context.report({
98 node,
99 message: `Must use destructuring ${node.object.property.name} assignment`
100 });
101 }
102 }
103
104 return {
105
106 FunctionDeclaration: handleStatelessComponent,
107
108 ArrowFunctionExpression: handleStatelessComponent,
109
110 FunctionExpression: handleStatelessComponent,
111
112 MemberExpression(node) {
113 const SFCComponent = components.get(context.getScope(node).block);
114 const classComponent = utils.getParentComponent(node);
115 if (SFCComponent) {
116 handleSFCUsage(node);
117 }
118 if (classComponent) {
119 handleClassUsage(node);
120 }
121 },
122
123 VariableDeclarator(node) {
124 const classComponent = utils.getParentComponent(node);
125 const SFCComponent = components.get(context.getScope(node).block);
126
127 const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
128 // let {foo} = props;
129 const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
130 // let {foo} = this.props;
131 const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (
132 node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state'
133 );
134
135 if (SFCComponent && destructuringSFC && configuration === 'never') {
136 context.report({
137 node,
138 message: `Must never use destructuring ${node.init.name} assignment`
139 });
140 }
141
142 if (
143 classComponent && destructuringClass && configuration === 'never'
144 && !(ignoreClassFields && node.parent.type === 'ClassProperty')
145 ) {
146 context.report({
147 node,
148 message: `Must never use destructuring ${node.init.property.name} assignment`
149 });
150 }
151 }
152 };
153 })
154};