811e7ea10c5f21b3b8e366d6a65c8393fa3d1911
[platform/framework/web/crosswalk-tizen.git] /
1 /**
2  * @fileoverview Prevent missing displayName in a React component definition
3  * @author Yannick Croissant
4  */
5 'use strict';
6
7 var componentUtil = require('../util/component');
8 var ComponentList = componentUtil.List;
9
10 // ------------------------------------------------------------------------------
11 // Rule Definition
12 // ------------------------------------------------------------------------------
13
14 module.exports = function(context) {
15
16   var componentList = new ComponentList();
17
18   var MISSING_MESSAGE = 'Component definition is missing display name';
19   var MISSING_MESSAGE_NAMED_COMP = '{{component}} component definition is missing display name';
20
21   /**
22    * Checks if the component must be validated
23    * @param {Object} component The component to process
24    * @returns {Boolean} True if the component must be validated, false if not.
25    */
26   function mustBeValidated(component) {
27     return (
28       component &&
29       component.isReactComponent &&
30       !component.hasDisplayName
31     );
32   }
33
34   /**
35    * Checks if we are declaring a display name
36    * @param {ASTNode} node The AST node being checked.
37    * @returns {Boolean} True if we are declaring a display name, false if not.
38    */
39   function isDisplayNameDeclaration(node) {
40
41     // Special case for class properties
42     // (babel-eslint does not expose property name so we have to rely on tokens)
43     if (node.type === 'ClassProperty') {
44       var tokens = context.getFirstTokens(node, 2);
45       if (tokens[0].value === 'displayName' || tokens[1].value === 'displayName') {
46         return true;
47       }
48       return false;
49     }
50
51     return Boolean(
52       node &&
53       node.name === 'displayName'
54     );
55   }
56
57   /**
58    * Mark a prop type as declared
59    * @param {ASTNode} node The AST node being checked.
60    */
61   function markDisplayNameAsDeclared(node) {
62     componentList.set(context, node, {
63       hasDisplayName: true
64     });
65   }
66
67   /**
68    * Reports missing display name for a given component
69    * @param {Object} component The component to process
70    */
71   function reportMissingDisplayName(component) {
72     context.report(
73       component.node,
74       component.name === componentUtil.DEFAULT_COMPONENT_NAME ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP, {
75         component: component.name
76       }
77     );
78   }
79
80   // --------------------------------------------------------------------------
81   // Public
82   // --------------------------------------------------------------------------
83
84   return {
85
86     ClassProperty: function(node) {
87       if (!isDisplayNameDeclaration(node)) {
88         return;
89       }
90
91       markDisplayNameAsDeclared(node);
92     },
93
94     MemberExpression: function(node) {
95       if (!isDisplayNameDeclaration(node.property)) {
96         return;
97       }
98       var component = componentList.getByName(node.object.name);
99       if (!component) {
100         return;
101       }
102       markDisplayNameAsDeclared(component.node);
103     },
104
105     MethodDefinition: function(node) {
106       if (!isDisplayNameDeclaration(node.key)) {
107         return;
108       }
109       markDisplayNameAsDeclared(node);
110     },
111
112     ObjectExpression: function(node) {
113       // Search for the displayName declaration
114       node.properties.forEach(function(property) {
115         if (!isDisplayNameDeclaration(property.key)) {
116           return;
117         }
118         markDisplayNameAsDeclared(node);
119       });
120     },
121
122     'Program:exit': function() {
123       var list = componentList.getList();
124       // Report missing display name for all classes
125       for (var component in list) {
126         if (!list.hasOwnProperty(component) || !mustBeValidated(list[component])) {
127           continue;
128         }
129         reportMissingDisplayName(list[component]);
130       }
131     },
132
133     ReturnStatement: function(node) {
134       if (!componentUtil.isReactComponent(context, node)) {
135         return;
136       }
137       componentList.set(context, node, {
138         isReactComponent: true
139       });
140     }
141   };
142
143 };