From 01af82d16e2db2b5530bb9a297186e6b9b1a066f Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 3 May 2016 21:05:44 +0000 Subject: [PATCH] Add class properties support to require-render-return (fixes #564) --- lib/rules/require-render-return.js | 31 +++++++++++++++++--- tests/lib/rules/require-render-return.js | 36 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index 28082846df..a94ac44f0a 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -38,6 +38,22 @@ module.exports = Components.detect(function(context, components, utils) { } } + /** + * 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; + } + + return node.key.name; + } + /** * Check if a given AST node has a render method * @param {ASTNode} node The AST node being checked. @@ -46,10 +62,10 @@ module.exports = Components.detect(function(context, components, utils) { function hasRenderMethod(node) { var properties = getComponentProperties(node); for (var i = 0, j = properties.length; i < j; i++) { - if (properties[i].key.name !== 'render') { + if (getPropertyName(properties[i]) !== 'render') { continue; } - return properties[i].value.type === 'FunctionExpression'; + return /FunctionExpression$/.test(properties[i].value.type); } return false; } @@ -63,8 +79,8 @@ module.exports = Components.detect(function(context, components, utils) { depth++; } if ( - (ancestors[i].type !== 'Property' && ancestors[i].type !== 'MethodDefinition') || - ancestors[i].key.name !== 'render' || + !/(MethodDefinition|(Class)?Property)$/.test(ancestors[i].type) || + getPropertyName(ancestors[i]) !== 'render' || depth > 1 ) { continue; @@ -73,6 +89,13 @@ module.exports = Components.detect(function(context, components, utils) { } }, + 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) { diff --git a/tests/lib/rules/require-render-return.js b/tests/lib/rules/require-render-return.js index f6e0665037..8da8fbffd4 100644 --- a/tests/lib/rules/require-render-return.js +++ b/tests/lib/rules/require-render-return.js @@ -37,6 +37,28 @@ ruleTester.run('require-render-return', rule, { '}' ].join('\n'), parserOptions: parserOptions + }, { + // ES6 class with render property + code: [ + 'class Hello extends React.Component {', + ' render = () => {', + ' return
Hello {this.props.name}
;', + ' }', + '}' + ].join('\n'), + parser: 'babel-eslint', + parserOptions: parserOptions + }, { + // ES6 class with render property (implicit return) + code: [ + 'class Hello extends React.Component {', + ' render = () => (', + '
Hello {this.props.name}
', + ' )', + '}' + ].join('\n'), + parser: 'babel-eslint', + parserOptions: parserOptions }, { // ES5 class code: [ @@ -150,5 +172,19 @@ ruleTester.run('require-render-return', rule, { errors: [{ message: 'Your render method should have return statement' }] + }, { + // Missing return ES6 class render property + code: [ + 'class Hello extends React.Component {', + ' render = () => {', + '
Hello {this.props.name}
', + ' }', + '}' + ].join('\n'), + parser: 'babel-eslint', + parserOptions: parserOptions, + errors: [{ + message: 'Your render method should have return statement' + }] } ]});