Skip to content

Commit 18d722d

Browse files
ssutarpzuraq
authored andcommitted
[FEAT] Add validation checks detecting action infinite loop (#94)
1 parent d4a84e7 commit 18d722d

File tree

5 files changed

+135
-4
lines changed

5 files changed

+135
-4
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const Foo = EmberObject.extend({
2+
actions: {
3+
bar() {
4+
this._super(...arguments);
5+
this.get("bar")();
6+
}
7+
}
8+
});
9+
10+
const Foo = EmberObject.extend({
11+
actions: {
12+
biz() {
13+
this._super(...arguments);
14+
get(this, "biz")();
15+
}
16+
}
17+
});
18+
19+
const Foo = EmberObject.extend({
20+
actions: {
21+
baz() {
22+
this._super(...arguments);
23+
tryInvoke(this, "baz");
24+
}
25+
}
26+
});
27+
28+
const Foo = EmberObject.extend({
29+
actions: {
30+
sendBaz() {
31+
this._super(...arguments);
32+
this.send("sendBaz");
33+
}
34+
}
35+
});
36+
37+
const Foo = EmberObject.extend({
38+
actions: {
39+
thisBaz() {
40+
this._super(...arguments);
41+
this.thisBaz();
42+
}
43+
}
44+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"decorators": true
3+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const Foo = EmberObject.extend({
2+
actions: {
3+
bar() {
4+
this._super(...arguments);
5+
this.get("bar")();
6+
}
7+
}
8+
});
9+
10+
const Foo = EmberObject.extend({
11+
actions: {
12+
biz() {
13+
this._super(...arguments);
14+
get(this, "biz")();
15+
}
16+
}
17+
});
18+
19+
const Foo = EmberObject.extend({
20+
actions: {
21+
baz() {
22+
this._super(...arguments);
23+
tryInvoke(this, "baz");
24+
}
25+
}
26+
});
27+
28+
const Foo = EmberObject.extend({
29+
actions: {
30+
sendBaz() {
31+
this._super(...arguments);
32+
this.send("sendBaz");
33+
}
34+
}
35+
});
36+
37+
const Foo = EmberObject.extend({
38+
actions: {
39+
thisBaz() {
40+
this._super(...arguments);
41+
this.thisBaz();
42+
}
43+
}
44+
});

transforms/helpers/parse-helper.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,6 @@ function parseEmberObjectCallExpression(eoCallExpression) {
244244
* @param {Object} options
245245
*/
246246
function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
247-
logger.info(`[${filePath}]: BEGIN`);
248-
249247
const runtimeConfigPath = options["runtime-config-path"];
250248

251249
if (runtimeConfigPath) {
@@ -255,7 +253,7 @@ function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
255253
);
256254
if (!options.runtimeData) {
257255
logger.warn(
258-
`${filePath} SKIPPED Could not find runtime data NO_RUNTIME_DATA`
256+
`[${filePath}]: SKIPPED Could not find runtime data NO_RUNTIME_DATA`
259257
);
260258
return;
261259
}
@@ -291,7 +289,7 @@ function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
291289
options.runtimeData
292290
);
293291

294-
const errors = hasValidProps(eoProps, getOptions(options));
292+
const errors = hasValidProps(j, eoProps, getOptions(options));
295293
if (errors.length) {
296294
logger.warn(
297295
`[${filePath}]: FAILURE \nValidation errors: \n\t${errors.join("\n\t")}`

transforms/helpers/validation-helper.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ function isFileOfType(file, type) {
4949
* @returns {Boolean}
5050
*/
5151
function hasValidProps(
52+
j,
5253
{ instanceProps = [] } = {},
5354
{ decorators = false, classFields = true } = {}
5455
) {
@@ -72,6 +73,7 @@ function hasValidProps(
7273

7374
if (instanceProp.isAction) {
7475
errors = errors.concat(getLifecycleHookErrors(instanceProp));
76+
errors = errors.concat(getInfiniteLoopErrors(j, instanceProp));
7577
}
7678

7779
if (
@@ -139,4 +141,44 @@ function isExtendsMixin(j, eoCallExpression) {
139141
return j(eoCallExpression).get("arguments").value.length > 1;
140142
}
141143

144+
/**
145+
* Validation against pattern mentioned https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2
146+
*
147+
* @param {Object} j - jscodeshift lib reference
148+
* @param {EOProp} actionsProp
149+
*/
150+
function getInfiniteLoopErrors(j, actionsProp) {
151+
const actionProps = get(actionsProp, "value.properties");
152+
return actionProps.reduce((errors, actionProp) => {
153+
const actionName = getPropName(actionProp);
154+
if (actionName) {
155+
const functExpr = j(actionProp.value);
156+
157+
// Occurences of this.actionName()
158+
const actionCalls = functExpr.find(j.CallExpression, {
159+
callee: {
160+
type: "MemberExpression",
161+
object: {
162+
type: "ThisExpression"
163+
},
164+
property: {
165+
type: "Identifier",
166+
name: actionName
167+
}
168+
}
169+
});
170+
171+
// Occurences of this.get('actionName')() or get(this, 'actionName')()
172+
const actionLiterals = functExpr.find(j.Literal, { value: actionName });
173+
174+
if (actionLiterals.length || actionCalls.length) {
175+
errors.push(
176+
`[${actionName}]: Transform not supported - calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details`
177+
);
178+
}
179+
}
180+
return errors;
181+
}, []);
182+
}
183+
142184
module.exports = { isFileOfType, hasValidProps, isExtendsMixin, isTestFile };

0 commit comments

Comments
 (0)