UNPKG

2.85 kBJavaScriptView Raw
1/**
2 * require `$on` and `$watch` deregistration callbacks to be saved in a variable
3 *
4 * Watch and On methods on the scope object should be assigned to a variable, in order to be deleted in a $destroy event handler
5 * @version 0.1.0
6 * @category bestPractice
7 * @sinceAngularVersion 1.x
8 */
9'use strict';
10
11module.exports = {
12 meta: {
13 schema: []
14 },
15 create: function(context) {
16 function report(node, method) {
17 context.report(node, 'The "{{method}}" call should be assigned to a variable, in order to be destroyed during the $destroy event', {
18 method: method
19 });
20 }
21
22 /**
23 * Return true if the given node is a call expression calling a function
24 * named '$on' or '$watch' on an object named '$scope', '$rootScope' or
25 * 'scope'.
26 */
27 function isScopeOnOrWatch(node, scopes) {
28 if (node.type !== 'CallExpression') {
29 return false;
30 }
31
32 var calledFunction = node.callee;
33 if (calledFunction.type !== 'MemberExpression') {
34 return false;
35 }
36
37 // can only easily tell what name was used if a simple
38 // identifiers were used to access it.
39 var parentObject = calledFunction.object;
40 var accessedFunction = calledFunction.property;
41
42 // cannot check name of the parent object if it is returned from a
43 // complex expression.
44 if (parentObject.type !== 'Identifier' ||
45 accessedFunction.type !== 'Identifier') {
46 return false;
47 }
48
49 var objectName = parentObject.name;
50 var functionName = accessedFunction.name;
51
52 return scopes.indexOf(objectName) >= 0 && (functionName === '$on' ||
53 functionName === '$watch');
54 }
55
56 /**
57 * Return true if the given node is a call expression that has a first
58 * argument of the string '$destroy'.
59 */
60 function isFirstArgDestroy(node) {
61 var args = node.arguments;
62
63 return (args.length >= 1 &&
64 args[0].type === 'Literal' &&
65 args[0].value === '$destroy');
66 }
67
68 return {
69
70 CallExpression: function(node) {
71 if (isScopeOnOrWatch(node, ['$rootScope']) && !isFirstArgDestroy(node)) {
72 if (node.parent.type !== 'VariableDeclarator' &&
73 node.parent.type !== 'AssignmentExpression' &&
74 !(isScopeOnOrWatch(node.parent, ['$rootScope', '$scope', 'scope']) &&
75 isFirstArgDestroy(node.parent))) {
76 report(node, node.callee.property.name);
77 }
78 }
79 }
80 };
81 }
82};