1 | /**
|
2 | * disallow the deprecated directive replace property
|
3 | *
|
4 | * This rule disallows the replace attribute in a directive definition object.
|
5 | * The replace property of a directive definition object is deprecated since angular 1.3 ([latest angular docs](https://docs.angularjs.org/api/ng/service/$compile).
|
6 | *
|
7 | * The option `ignoreReplaceFalse` let you ignore directive definitions with replace set to false.
|
8 | *
|
9 | * @version 0.15.0
|
10 | * @category deprecatedAngularFeature
|
11 | * @sinceAngularVersion 1.x
|
12 | */
|
13 | ;
|
14 |
|
15 | var angularRule = require('./utils/angular-rule');
|
16 |
|
17 | module.exports = {
|
18 | meta: {
|
19 | schema: [{
|
20 | type: 'object',
|
21 | properties: {
|
22 | ignoreReplaceFalse: {
|
23 | type: 'boolean'
|
24 | }
|
25 | }
|
26 | }]
|
27 | },
|
28 | create: angularRule(function(context) {
|
29 | var options = context.options[0] || {};
|
30 | var ignoreReplaceFalse = !!options.ignoreReplaceFalse;
|
31 |
|
32 | var potentialReplaceNodes = {};
|
33 |
|
34 | function addPotentialReplaceNode(variableName, node) {
|
35 | var nodeList = potentialReplaceNodes[variableName] || [];
|
36 |
|
37 | nodeList.push({
|
38 | name: variableName,
|
39 | node: node,
|
40 | block: context.getScope().block.body
|
41 | });
|
42 |
|
43 | potentialReplaceNodes[variableName] = nodeList;
|
44 | }
|
45 |
|
46 | return {
|
47 | 'angular?directive': function(callExpressionNode, fnNode) {
|
48 | if (!fnNode || !fnNode.body) {
|
49 | return;
|
50 | }
|
51 | fnNode.body.body.forEach(function(statement) {
|
52 | if (statement.type === 'ReturnStatement') {
|
53 | // get potential replace node by argument name of empty string for object expressions
|
54 | var potentialNodes = potentialReplaceNodes[statement.argument.name || ''];
|
55 | if (!potentialNodes) {
|
56 | return;
|
57 | }
|
58 | potentialNodes.forEach(function(report) {
|
59 | // only reports nodes that belong to the same expression
|
60 | if (report.block === statement.parent) {
|
61 | context.report(report.node, 'Directive definition property replace is deprecated.');
|
62 | }
|
63 | });
|
64 | }
|
65 | });
|
66 | },
|
67 | AssignmentExpression: function(node) {
|
68 | // Only check for literal member property assignments.
|
69 | if (node.left.type !== 'MemberExpression') {
|
70 | return;
|
71 | }
|
72 | // Only check setting properties named 'replace'.
|
73 | if (node.left.property.name !== 'replace') {
|
74 | return;
|
75 | }
|
76 | if (ignoreReplaceFalse && node.right.value === false) {
|
77 | return;
|
78 | }
|
79 | addPotentialReplaceNode(node.left.object.name, node);
|
80 | },
|
81 | Property: function(node) {
|
82 | // This only checks for objects which have defined a literal restrict property.
|
83 | if (node.key.name !== 'replace') {
|
84 | return;
|
85 | }
|
86 | if (ignoreReplaceFalse === true && node.value.value === false) {
|
87 | return;
|
88 | }
|
89 |
|
90 | // assumption: Property always belongs to a ObjectExpression
|
91 | var objectExpressionParent = node.parent.parent;
|
92 |
|
93 | // add to potential replace nodes if the object is defined in a variable
|
94 | if (objectExpressionParent.type === 'VariableDeclarator') {
|
95 | addPotentialReplaceNode(objectExpressionParent.id.name, node);
|
96 | }
|
97 |
|
98 | // report directly if object is part of a return statement and inside a directive body
|
99 | if (objectExpressionParent.type === 'ReturnStatement') {
|
100 | addPotentialReplaceNode('', node);
|
101 | }
|
102 | }
|
103 | };
|
104 | })
|
105 | };
|