@@ -21,7 +21,7 @@ function isEmberSetThis(node, importedEmberName) {
21
21
types . isIdentifier ( node . callee . property ) &&
22
22
[ 'set' , 'setProperties' ] . includes ( node . callee . property . name ) &&
23
23
node . arguments . length > 0 &&
24
- memberExpressionBeginWithThis ( node . arguments [ 0 ] )
24
+ memberExpressionBeginsWithThis ( node . arguments [ 0 ] )
25
25
) ;
26
26
}
27
27
@@ -32,7 +32,7 @@ function isImportedSetThis(node, importedSetName, importedSetPropertiesName) {
32
32
types . isIdentifier ( node . callee ) &&
33
33
[ importedSetName , importedSetPropertiesName ] . includes ( node . callee . name ) &&
34
34
node . arguments . length > 0 &&
35
- memberExpressionBeginWithThis ( node . arguments [ 0 ] )
35
+ memberExpressionBeginsWithThis ( node . arguments [ 0 ] )
36
36
) ;
37
37
}
38
38
@@ -44,15 +44,50 @@ function isThisSet(node) {
44
44
types . isMemberExpression ( node . callee ) &&
45
45
types . isIdentifier ( node . callee . property ) &&
46
46
[ 'set' , 'setProperties' ] . includes ( node . callee . property . name ) &&
47
- memberExpressionBeginWithThis ( node . callee . object )
47
+ memberExpressionBeginsWithThis ( node . callee . object )
48
48
) ;
49
49
}
50
50
51
- function memberExpressionBeginWithThis ( node ) {
51
+ // import { sendEvent } from "@ember/object/events"
52
+ // Ember.sendEvent
53
+
54
+ // Looks for variations like:
55
+ // - this.send(...)
56
+ // - Ember.send(...)
57
+ const DISALLOWED_FUNCTION_CALLS = new Set ( [ 'send' , 'sendAction' , 'sendEvent' , 'trigger' ] ) ;
58
+ function isDisallowedFunctionCall ( node , importedEmberName ) {
59
+ return (
60
+ types . isCallExpression ( node ) &&
61
+ types . isMemberExpression ( node . callee ) &&
62
+ ( types . isThisExpression ( node . callee . object ) ||
63
+ ( types . isIdentifier ( node . callee . object ) && node . callee . object . name === importedEmberName ) ) &&
64
+ types . isIdentifier ( node . callee . property ) &&
65
+ DISALLOWED_FUNCTION_CALLS . has ( node . callee . property . name )
66
+ ) ;
67
+ }
68
+
69
+ // sendEvent(...)
70
+ function isImportedSendEventCall ( node , importedSendEventName ) {
71
+ return (
72
+ types . isCallExpression ( node ) &&
73
+ types . isIdentifier ( node . callee ) &&
74
+ node . callee . name === importedSendEventName
75
+ ) ;
76
+ }
77
+
78
+ /**
79
+ * Finds:
80
+ * - this
81
+ * - this.foo
82
+ * - this.foo.bar
83
+ * - this?.foo?.bar
84
+ * @param {node } node
85
+ */
86
+ function memberExpressionBeginsWithThis ( node ) {
52
87
if ( types . isThisExpression ( node ) ) {
53
88
return true ;
54
- } else if ( types . isMemberExpression ( node ) ) {
55
- return memberExpressionBeginWithThis ( node . object ) ;
89
+ } else if ( types . isMemberExpression ( node ) || types . isOptionalMemberExpression ( node ) ) {
90
+ return memberExpressionBeginsWithThis ( node . object ) ;
56
91
}
57
92
return false ;
58
93
}
@@ -61,16 +96,20 @@ function memberExpressionBeginWithThis(node) {
61
96
* Recursively finds calls that could be side effects in a computed property function body.
62
97
*
63
98
* @param {ASTNode } computedPropertyBody body of computed property to search
99
+ * @param {boolean } catchEvents
64
100
* @param {string } importedEmberName
65
101
* @param {string } importedSetName
66
102
* @param {string } importedSetPropertiesName
103
+ * @param {string } importedSendEventName
67
104
* @returns {Array<ASTNode> }
68
105
*/
69
106
function findSideEffects (
70
107
computedPropertyBody ,
108
+ catchEvents ,
71
109
importedEmberName ,
72
110
importedSetName ,
73
- importedSetPropertiesName
111
+ importedSetPropertiesName ,
112
+ importedSendEventName
74
113
) {
75
114
const results = [ ] ;
76
115
@@ -80,7 +119,9 @@ function findSideEffects(
80
119
isEmberSetThis ( child , importedEmberName ) || // Ember.set(this, 'foo', 123)
81
120
isImportedSetThis ( child , importedSetName , importedSetPropertiesName ) || // set(this, 'foo', 123)
82
121
isThisSet ( child ) || // this.set('foo', 123)
83
- propertySetterUtils . isThisSet ( child ) // this.foo = 123;
122
+ propertySetterUtils . isThisSet ( child ) || // this.foo = 123;
123
+ ( catchEvents && isDisallowedFunctionCall ( child , importedEmberName ) ) || // this.send('done')
124
+ ( catchEvents && isImportedSendEventCall ( child , importedSendEventName ) ) // sendEvent(...)
84
125
) {
85
126
results . push ( child ) ;
86
127
}
@@ -103,16 +144,30 @@ module.exports = {
103
144
'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-side-effects.md' ,
104
145
} ,
105
146
fixable : null ,
106
- schema : [ ] ,
147
+ schema : [
148
+ {
149
+ type : 'object' ,
150
+ properties : {
151
+ catchEvents : {
152
+ type : 'boolean' ,
153
+ default : false ,
154
+ } ,
155
+ } ,
156
+ } ,
157
+ ] ,
107
158
} ,
108
159
109
160
ERROR_MESSAGE ,
110
161
111
162
create ( context ) {
163
+ // Options:
164
+ const catchEvents = context . options [ 0 ] && context . options [ 0 ] . catchEvents ;
165
+
112
166
let importedEmberName ;
113
167
let importedComputedName ;
114
168
let importedSetName ;
115
169
let importedSetPropertiesName ;
170
+ let importedSendEventName ;
116
171
117
172
const report = function ( node ) {
118
173
context . report ( node , ERROR_MESSAGE ) ;
@@ -131,6 +186,10 @@ module.exports = {
131
186
importedSetPropertiesName ||
132
187
getImportIdentifier ( node , '@ember/object' , 'setProperties' ) ;
133
188
}
189
+ if ( node . source . value === '@ember/object/events' ) {
190
+ importedSendEventName =
191
+ importedSendEventName || getImportIdentifier ( node , '@ember/object/events' , 'sendEvent' ) ;
192
+ }
134
193
} ,
135
194
136
195
CallExpression ( node ) {
@@ -142,9 +201,11 @@ module.exports = {
142
201
143
202
findSideEffects (
144
203
computedPropertyBody ,
204
+ catchEvents ,
145
205
importedEmberName ,
146
206
importedSetName ,
147
- importedSetPropertiesName
207
+ importedSetPropertiesName ,
208
+ importedSendEventName
148
209
) . forEach ( report ) ;
149
210
} ,
150
211
@@ -157,9 +218,11 @@ module.exports = {
157
218
158
219
findSideEffects (
159
220
computedPropertyBody ,
221
+ catchEvents ,
160
222
importedEmberName ,
161
223
importedSetName ,
162
- importedSetPropertiesName
224
+ importedSetPropertiesName ,
225
+ importedSendEventName
163
226
) . forEach ( report ) ;
164
227
} ,
165
228
} ;
0 commit comments