react-scripts
Version:
Configuration and scripts for Create React App.
121 lines (109 loc) • 3.51 kB
JavaScript
/**
* @fileoverview Enforce ES5 or ES6 class for returning value in render function.
* @author Mark Orel
*/
;
var Components = require('../util/Components');
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = Components.detect(function(context, components, utils) {
/**
* Mark a return statement as present
* @param {ASTNode} node The AST node being checked.
*/
function markReturnStatementPresent(node) {
components.set(node, {
hasReturnStatement: true
});
}
/**
* Get properties for a given AST node
* @param {ASTNode} node The AST node being checked.
* @returns {Array} Properties array.
*/
function getComponentProperties(node) {
switch (node.type) {
case 'ClassDeclaration':
return node.body.body;
case 'ObjectExpression':
return node.properties;
default:
return [];
}
}
/**
* Get properties name
* @param {Object} node - Property.
* @returns {String} Property name.
*/
function getPropertyName(node) {
// Special case for class properties
// (babel-eslint does not expose property name so we have to rely on tokens)
if (node.type === 'ClassProperty') {
var tokens = context.getFirstTokens(node, 2);
return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
} else if (['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
return node.key.name;
}
return '';
}
/**
* Check if a given AST node has a render method
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if there is a render method, false if not
*/
function hasRenderMethod(node) {
var properties = getComponentProperties(node);
for (var i = 0, j = properties.length; i < j; i++) {
if (getPropertyName(properties[i]) !== 'render') {
continue;
}
return /FunctionExpression$/.test(properties[i].value.type);
}
return false;
}
return {
ReturnStatement: function(node) {
var ancestors = context.getAncestors(node).reverse();
var depth = 0;
for (var i = 0, j = ancestors.length; i < j; i++) {
if (/Function(Expression|Declaration)$/.test(ancestors[i].type)) {
depth++;
}
if (
!/(MethodDefinition|(Class)?Property)$/.test(ancestors[i].type) ||
getPropertyName(ancestors[i]) !== 'render' ||
depth > 1
) {
continue;
}
markReturnStatementPresent(node);
}
},
ArrowFunctionExpression: function(node) {
if (node.expression === false || getPropertyName(node.parent) !== 'render') {
return;
}
markReturnStatementPresent(node);
},
'Program:exit': function() {
var list = components.list();
for (var component in list) {
if (
!list.hasOwnProperty(component) ||
!hasRenderMethod(list[component].node) ||
list[component].hasReturnStatement ||
(!utils.isES5Component(list[component].node) && !utils.isES6Component(list[component].node))
) {
continue;
}
context.report({
node: list[component].node,
message: 'Your render method should have return statement'
});
}
}
};
});
module.exports.schema = [{}];