Skip to content

Commit 74f1130

Browse files
committed
Group component related functions
1 parent 3b5cf97 commit 74f1130

File tree

4 files changed

+302
-306
lines changed

4 files changed

+302
-306
lines changed

lib/rules/display-name.js

Lines changed: 29 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -4,97 +4,20 @@
44
*/
55
'use strict';
66

7+
var componentUtil = require('../util/component');
8+
var ComponentList = componentUtil.List;
9+
710
// ------------------------------------------------------------------------------
811
// Rule Definition
912
// ------------------------------------------------------------------------------
1013

1114
module.exports = function(context) {
1215

13-
var components = {};
16+
var componentList = new ComponentList();
1417

1518
var MISSING_MESSAGE = 'Component definition is missing display name';
1619
var MISSING_MESSAGE_NAMED_COMP = '{{component}} component definition is missing display name';
1720

18-
var defaultClassName = 'eslintReactComponent';
19-
20-
/**
21-
* Get the component id from an ASTNode
22-
* @param {ASTNode} node The AST node being checked.
23-
* @returns {String} The component id.
24-
*/
25-
function getComponentId(node) {
26-
if (
27-
node.type === 'MemberExpression' &&
28-
node.property && node.property.name === 'displayName' &&
29-
node.object && components[node.object.name]
30-
) {
31-
return node.object.name;
32-
}
33-
34-
var scope = context.getScope();
35-
while (scope && scope.type !== 'class') {
36-
scope = scope.upper;
37-
}
38-
39-
if (scope) {
40-
return scope.block.id.name;
41-
}
42-
43-
return defaultClassName;
44-
}
45-
46-
/**
47-
* Get the component from an ASTNode
48-
* @param {ASTNode} node The AST node being checked.
49-
* @returns {Object} The component object.
50-
*/
51-
function getComponent(node) {
52-
var id = getComponentId(node);
53-
if (!components[id]) {
54-
components[id] = {
55-
name: id,
56-
node: node,
57-
hasDisplayName: false
58-
};
59-
}
60-
return components[id];
61-
}
62-
63-
/**
64-
* Detect if we are in a React component by checking the render method
65-
* @param {ASTNode} node The AST node being checked.
66-
*/
67-
function detectReactComponent(node) {
68-
var scope = context.getScope();
69-
if (
70-
(node.argument.type === 'Literal' && (node.argument.value !== null && node.argument.value !== false)) &&
71-
(node.argument.type !== 'JSXElement') &&
72-
(scope.block.parent.key.name === 'render')
73-
) {
74-
return;
75-
}
76-
var component = getComponent(node);
77-
component.isComponentDefinition = true;
78-
}
79-
80-
/**
81-
* Checks if we are inside a component definition
82-
* @param {ASTNode} node The AST node being checked.
83-
* @returns {Boolean} True if we are inside a component definition, false if not.
84-
*/
85-
function isComponentDefinition(node) {
86-
var isES5Component = Boolean(
87-
node.parent &&
88-
node.parent.callee &&
89-
node.parent.callee.object &&
90-
node.parent.callee.property &&
91-
node.parent.callee.object.name === 'React' &&
92-
node.parent.callee.property.name === 'createClass'
93-
);
94-
var isES6Component = getComponent(node).isComponentDefinition;
95-
return isES5Component || isES6Component;
96-
}
97-
9821
/**
9922
* Checks if we are declaring a display name
10023
* @param {ASTNode} node The AST node being checked.
@@ -112,22 +35,23 @@ module.exports = function(context) {
11235
* @param {ASTNode} node The AST node being checked.
11336
*/
11437
function markDisplayNameAsDeclared(node) {
115-
var component = getComponent(node);
116-
component.hasDisplayName = true;
38+
componentList.set(context, node, {
39+
hasDisplayName: true
40+
});
11741
}
11842

11943
/**
12044
* Reports missing display name for a given component
121-
* @param {String} id The id of the component to process
45+
* @param {Object} component The component to process
12246
*/
123-
function reportMissingDisplayName(id) {
124-
if (!components[id] || components[id].hasDisplayName === true) {
47+
function reportMissingDisplayName(component) {
48+
if (!component || component.hasDisplayName === true) {
12549
return;
12650
}
12751
context.report(
128-
components[id].node,
129-
id === defaultClassName ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP, {
130-
component: id
52+
component.node,
53+
component.name === componentUtil.DEFAULT_COMPONENT_NAME ? MISSING_MESSAGE : MISSING_MESSAGE_NAMED_COMP, {
54+
component: component.name
13155
}
13256
);
13357
}
@@ -142,14 +66,14 @@ module.exports = function(context) {
14266
if (!isDisplayNameDeclaration(node.property)) {
14367
return;
14468
}
145-
markDisplayNameAsDeclared(node);
146-
},
147-
148-
ObjectExpression: function(node) {
149-
if (!isComponentDefinition(node)) {
69+
var component = componentList.getByName(node.object.name);
70+
if (!component) {
15071
return;
15172
}
73+
markDisplayNameAsDeclared(component.node);
74+
},
15275

76+
ObjectExpression: function(node) {
15377
// Search for the displayName declaration
15478
node.properties.forEach(function(property) {
15579
if (!isDisplayNameDeclaration(property.key)) {
@@ -159,29 +83,23 @@ module.exports = function(context) {
15983
});
16084
},
16185

162-
'ObjectExpression:exit': function(node) {
163-
if (!isComponentDefinition(node)) {
164-
return;
165-
}
166-
167-
// Report missing display name for all ES5 classes
168-
reportMissingDisplayName(defaultClassName);
169-
170-
// Reset the ES5 default object
171-
components[defaultClassName] = null;
172-
},
173-
17486
'Program:exit': function() {
175-
// Report missing display name for all ES6 classes
176-
for (var component in components) {
177-
if (!components.hasOwnProperty(component)) {
87+
var list = componentList.getList();
88+
// Report missing display name for all classes
89+
for (var component in list) {
90+
if (!list.hasOwnProperty(component)) {
17891
continue;
17992
}
180-
reportMissingDisplayName(component);
93+
reportMissingDisplayName(list[component]);
18194
}
18295
},
18396

184-
ReturnStatement: detectReactComponent
97+
ReturnStatement: function(node) {
98+
if (!componentUtil.isReactComponent(context, node)) {
99+
return;
100+
}
101+
componentList.set(context, node);
102+
}
185103
};
186104

187105
};

lib/rules/no-multi-comp.js

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,86 +4,45 @@
44
*/
55
'use strict';
66

7+
var componentUtil = require('../util/component');
8+
var ComponentList = componentUtil.List;
9+
710
// ------------------------------------------------------------------------------
811
// Rule Definition
912
// ------------------------------------------------------------------------------
1013

1114
module.exports = function(context) {
1215

13-
var components = [];
16+
var componentList = new ComponentList();
1417

1518
var MULTI_COMP_MESSAGE = 'Declare only one React component per file';
1619

17-
/**
18-
* Checks if we are inside a component definition
19-
* @param {ASTNode} node The AST node being checked.
20-
* @returns {Boolean} True if we are inside a component definition, false if not.
21-
*/
22-
function isComponentDefinition(node) {
23-
return Boolean(
24-
node.parent &&
25-
node.parent.callee &&
26-
node.parent.callee.object &&
27-
node.parent.callee.property &&
28-
node.parent.callee.object.name === 'React' &&
29-
node.parent.callee.property.name === 'createClass'
30-
);
31-
}
32-
33-
/**
34-
* Get the component from an ASTNode
35-
* @param {ASTNode} node The AST node being checked.
36-
* @returns {Object} The component object.
37-
*/
38-
function getComponent() {
39-
var scope = context.getScope();
40-
while (scope && scope.type !== 'class') {
41-
if (isComponentDefinition(scope.block.parent.parent)) {
42-
return scope.block.parent.parent.parent.callee;
43-
}
44-
scope = scope.upper;
45-
}
46-
47-
return scope.block;
48-
}
49-
50-
/**
51-
* Detect if we are in a React component by checking the render method
52-
* @param {ASTNode} node The AST node being checked.
53-
*/
54-
function detectReactComponent(node) {
55-
var scope = context.getScope();
56-
if (
57-
(node.argument.type === 'Literal' && (node.argument.value !== null && node.argument.value !== false)) &&
58-
(node.argument.type !== 'JSXElement') &&
59-
(scope.block.parent.key.name === 'render')
60-
) {
61-
return;
62-
}
63-
components.push(getComponent(node));
64-
}
65-
66-
/**
67-
* Reports missing display name for a given component
68-
* @param {String} id The id of the component to process
69-
*/
70-
function reportMultiComponent() {
71-
if (components.length <= 1) {
72-
return;
73-
}
74-
75-
for (var i = 1, j = components.length; i < j; i++) {
76-
context.report(components[i], MULTI_COMP_MESSAGE);
77-
}
78-
}
79-
8020
// --------------------------------------------------------------------------
8121
// Public
8222
// --------------------------------------------------------------------------
8323

8424
return {
85-
'Program:exit': reportMultiComponent,
25+
'Program:exit': function() {
26+
if (componentList.count() <= 1) {
27+
return;
28+
}
29+
30+
var list = componentList.getList();
31+
var i = 0;
8632

87-
ReturnStatement: detectReactComponent
33+
for (var component in list) {
34+
if (!list.hasOwnProperty(component) || ++i === 1) {
35+
continue;
36+
}
37+
context.report(list[component].node, MULTI_COMP_MESSAGE);
38+
}
39+
},
40+
41+
ReturnStatement: function(node) {
42+
if (!componentUtil.isReactComponent(context, node)) {
43+
return;
44+
}
45+
componentList.set(context, node);
46+
}
8847
};
8948
};

0 commit comments

Comments
 (0)