eslint-plugin-react
Advanced tools
Comparing version 4.3.0 to 5.0.0
@@ -6,2 +6,33 @@ # Change Log | ||
## [5.0.0] - 2016-04-17 | ||
### Added | ||
* Add `jsx-first-prop-new-line` rule ([#410][] @jseminck) | ||
### Breaking | ||
* Update rules for React 15: | ||
* Add warnings for `LinkedStateMixin`, `ReactPerf.printDOM` and `ReactPerf.getMeasurementsSummaryMap` in `no-deprecated` | ||
* Allow stateless components to return `null` in `prefer-stateless-function` | ||
* Remove SVG attributes warnings ([#490][]) | ||
If you're still not using React 15 you can keep the old behavior by setting the React version to `0.14` in the [shared settings](README.md#configuration). | ||
### Fixed | ||
* Rewrite `require-render-return` rule ([#542][], [#543][]) | ||
* Fix `prefer-stateless-function` crash ([#544][]) | ||
* Fix external propTypes handling ([#545][]) | ||
* Do not mark inline functions in JSX as components ([#546][]) | ||
### Changed | ||
* Update dependencies | ||
* Documentation improvements | ||
[5.0.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v4.3.0...v5.0.0 | ||
[#410]: https://github.com/yannickcr/eslint-plugin-react/issues/410 | ||
[#490]: https://github.com/yannickcr/eslint-plugin-react/issues/490 | ||
[#542]: https://github.com/yannickcr/eslint-plugin-react/issues/542 | ||
[#543]: https://github.com/yannickcr/eslint-plugin-react/issues/543 | ||
[#544]: https://github.com/yannickcr/eslint-plugin-react/issues/544 | ||
[#545]: https://github.com/yannickcr/eslint-plugin-react/issues/545 | ||
[#546]: https://github.com/yannickcr/eslint-plugin-react/issues/546 | ||
## [4.3.0] - 2016-04-07 | ||
@@ -8,0 +39,0 @@ ### Added |
@@ -45,3 +45,4 @@ 'use strict'; | ||
'prefer-stateless-function': require('./lib/rules/prefer-stateless-function'), | ||
'require-render-return': require('./lib/rules/require-render-return') | ||
'require-render-return': require('./lib/rules/require-render-return'), | ||
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line') | ||
}, | ||
@@ -48,0 +49,0 @@ configs: { |
@@ -51,2 +51,8 @@ /** | ||
deprecated.MemberExpression[pragma + '.renderToStaticMarkup'] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup']; | ||
// 15.0.0 | ||
deprecated.MemberExpression[pragma + '.addons.LinkedStateMixin'] = ['15.0.0']; | ||
deprecated.MemberExpression['ReactPerf.printDOM'] = ['15.0.0', 'ReactPerf.printOperations']; | ||
deprecated.MemberExpression['Perf.printDOM'] = ['15.0.0', 'Perf.printOperations']; | ||
deprecated.MemberExpression['ReactPerf.getMeasurementsSummaryMap'] = ['15.0.0', 'ReactPerf.getWasted']; | ||
deprecated.MemberExpression['Perf.getMeasurementsSummaryMap'] = ['15.0.0', 'Perf.getWasted']; | ||
@@ -53,0 +59,0 @@ return deprecated; |
@@ -7,2 +7,4 @@ /** | ||
var versionUtil = require('../util/version'); | ||
// ------------------------------------------------------------------------------ | ||
@@ -15,8 +17,9 @@ // Constants | ||
var DOM_ATTRIBUTE_NAMES = { | ||
// Standard | ||
'accept-charset': 'acceptCharset', | ||
class: 'className', | ||
for: 'htmlFor', | ||
'http-equiv': 'httpEquiv', | ||
// SVG | ||
'http-equiv': 'httpEquiv' | ||
}; | ||
var SVGDOM_ATTRIBUTE_NAMES = { | ||
'clip-path': 'clipPath', | ||
@@ -92,9 +95,14 @@ 'fill-opacity': 'fillOpacity', | ||
* Get the standard name of the attribute. | ||
* @param {Object} context The current rule context. | ||
* @param {String} name - Name of the attribute. | ||
* @returns {String} The standard name of the attribute. | ||
*/ | ||
function getStandardName(name) { | ||
function getStandardName(context, name) { | ||
if (DOM_ATTRIBUTE_NAMES[name]) { | ||
return DOM_ATTRIBUTE_NAMES[name]; | ||
} | ||
// Also check for SVG attribute for React <15 | ||
if (!versionUtil.test(context, '15.0.0') && SVGDOM_ATTRIBUTE_NAMES[name]) { | ||
return SVGDOM_ATTRIBUTE_NAMES[name]; | ||
} | ||
var i; | ||
@@ -120,3 +128,3 @@ var found = DOM_PROPERTY_NAMES.some(function(element, index) { | ||
var name = sourceCode.getText(node.name); | ||
var standardName = getStandardName(name); | ||
var standardName = getStandardName(context, name); | ||
if (!isTagName(node) || !standardName) { | ||
@@ -123,0 +131,0 @@ return; |
@@ -10,2 +10,3 @@ /** | ||
var Components = require('../util/Components'); | ||
var versionUtil = require('../util/version'); | ||
@@ -58,53 +59,127 @@ // ------------------------------------------------------------------------------ | ||
/** | ||
* Checks whether the constructor body is a redundant super call. | ||
* Checks whether a given array of statements is a single call of `super`. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {Array} body - constructor body content. | ||
* @param {Array} ctorParams - The params to check against super call. | ||
* @returns {boolean} true if the construtor body is redundant | ||
* @param {ASTNode[]} body - An array of statements to check. | ||
* @returns {boolean} `true` if the body is a single call of `super`. | ||
*/ | ||
function isRedundantSuperCall(body, ctorParams) { | ||
if ( | ||
body.length !== 1 || | ||
body[0].type !== 'ExpressionStatement' || | ||
body[0].expression.callee.type !== 'Super' | ||
) { | ||
function isSingleSuperCall(body) { | ||
return ( | ||
body.length === 1 && | ||
body[0].type === 'ExpressionStatement' && | ||
body[0].expression.type === 'CallExpression' && | ||
body[0].expression.callee.type === 'Super' | ||
); | ||
} | ||
/** | ||
* Checks whether a given node is a pattern which doesn't have any side effects. | ||
* Default parameters and Destructuring parameters can have side effects. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {ASTNode} node - A pattern node. | ||
* @returns {boolean} `true` if the node doesn't have any side effects. | ||
*/ | ||
function isSimple(node) { | ||
return node.type === 'Identifier' || node.type === 'RestElement'; | ||
} | ||
/** | ||
* Checks whether a given array of expressions is `...arguments` or not. | ||
* `super(...arguments)` passes all arguments through. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {ASTNode[]} superArgs - An array of expressions to check. | ||
* @returns {boolean} `true` if the superArgs is `...arguments`. | ||
*/ | ||
function isSpreadArguments(superArgs) { | ||
return ( | ||
superArgs.length === 1 && | ||
superArgs[0].type === 'SpreadElement' && | ||
superArgs[0].argument.type === 'Identifier' && | ||
superArgs[0].argument.name === 'arguments' | ||
); | ||
} | ||
/** | ||
* Checks whether given 2 nodes are identifiers which have the same name or not. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {ASTNode} ctorParam - A node to check. | ||
* @param {ASTNode} superArg - A node to check. | ||
* @returns {boolean} `true` if the nodes are identifiers which have the same | ||
* name. | ||
*/ | ||
function isValidIdentifierPair(ctorParam, superArg) { | ||
return ( | ||
ctorParam.type === 'Identifier' && | ||
superArg.type === 'Identifier' && | ||
ctorParam.name === superArg.name | ||
); | ||
} | ||
/** | ||
* Checks whether given 2 nodes are a rest/spread pair which has the same values. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {ASTNode} ctorParam - A node to check. | ||
* @param {ASTNode} superArg - A node to check. | ||
* @returns {boolean} `true` if the nodes are a rest/spread pair which has the | ||
* same values. | ||
*/ | ||
function isValidRestSpreadPair(ctorParam, superArg) { | ||
return ( | ||
ctorParam.type === 'RestElement' && | ||
superArg.type === 'SpreadElement' && | ||
isValidIdentifierPair(ctorParam.argument, superArg.argument) | ||
); | ||
} | ||
/** | ||
* Checks whether given 2 nodes have the same value or not. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {ASTNode} ctorParam - A node to check. | ||
* @param {ASTNode} superArg - A node to check. | ||
* @returns {boolean} `true` if the nodes have the same value or not. | ||
*/ | ||
function isValidPair(ctorParam, superArg) { | ||
return ( | ||
isValidIdentifierPair(ctorParam, superArg) || | ||
isValidRestSpreadPair(ctorParam, superArg) | ||
); | ||
} | ||
/** | ||
* Checks whether the parameters of a constructor and the arguments of `super()` | ||
* have the same values or not. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {ASTNode} ctorParams - The parameters of a constructor to check. | ||
* @param {ASTNode} superArgs - The arguments of `super()` to check. | ||
* @returns {boolean} `true` if those have the same values. | ||
*/ | ||
function isPassingThrough(ctorParams, superArgs) { | ||
if (ctorParams.length !== superArgs.length) { | ||
return false; | ||
} | ||
var superArgs = body[0].expression.arguments; | ||
var firstSuperArg = superArgs[0]; | ||
var lastSuperArgIndex = superArgs.length - 1; | ||
var lastSuperArg = superArgs[lastSuperArgIndex]; | ||
var isSimpleParameterList = ctorParams.every(function(param) { | ||
return param.type === 'Identifier' || param.type === 'RestElement'; | ||
}); | ||
/** | ||
* Checks if a super argument is the same with constructor argument | ||
* @param {ASTNode} arg argument node | ||
* @param {number} index argument index | ||
* @returns {boolean} true if the arguments are same, false otherwise | ||
*/ | ||
function isSameIdentifier(arg, index) { | ||
return ( | ||
arg.type === 'Identifier' && | ||
arg.name === ctorParams[index].name | ||
); | ||
for (var i = 0; i < ctorParams.length; ++i) { | ||
if (!isValidPair(ctorParams[i], superArgs[i])) { | ||
return false; | ||
} | ||
} | ||
var spreadsArguments = | ||
superArgs.length === 1 && | ||
firstSuperArg.type === 'SpreadElement' && | ||
firstSuperArg.argument.name === 'arguments'; | ||
return true; | ||
} | ||
var passesParamsAsArgs = | ||
superArgs.length === ctorParams.length && | ||
superArgs.every(isSameIdentifier) || | ||
superArgs.length <= ctorParams.length && | ||
superArgs.slice(0, -1).every(isSameIdentifier) && | ||
lastSuperArg.type === 'SpreadElement' && | ||
ctorParams[lastSuperArgIndex].type === 'RestElement' && | ||
lastSuperArg.argument.name === ctorParams[lastSuperArgIndex].argument.name; | ||
return isSimpleParameterList && (spreadsArguments || passesParamsAsArgs); | ||
/** | ||
* Checks whether the constructor body is a redundant super call. | ||
* @see ESLint no-useless-constructor rule | ||
* @param {Array} body - constructor body content. | ||
* @param {Array} ctorParams - The params to check against super call. | ||
* @returns {boolean} true if the construtor body is redundant | ||
*/ | ||
function isRedundantSuperCall(body, ctorParams) { | ||
return ( | ||
isSingleSuperCall(body) && | ||
ctorParams.every(isSimple) && | ||
( | ||
isSpreadArguments(body[0].expression.arguments) || | ||
isPassingThrough(ctorParams, body[0].expression.arguments) | ||
) | ||
); | ||
} | ||
@@ -214,3 +289,11 @@ | ||
} | ||
if (!blockNode || !blockNode.key || blockNode.key.name !== 'render' || utils.isReturningJSX(node, true)) { | ||
var isRender = blockNode && blockNode.key && blockNode.key.name === 'render'; | ||
var allowNull = versionUtil.test(context, '15.0.0'); // Stateless components can return null since React 15 | ||
var isReturningJSX = utils.isReturningJSX(node, !allowNull); | ||
var isReturningNull = node.argument && (node.argument.value === null || node.argument.value === false); | ||
if ( | ||
!isRender || | ||
(allowNull && (isReturningJSX || isReturningNull)) || | ||
(!allowNull && isReturningJSX) | ||
) { | ||
return; | ||
@@ -217,0 +300,0 @@ } |
@@ -664,2 +664,4 @@ /** | ||
buildReactDeclarationTypes(propTypes.parent.right); | ||
} else { | ||
ignorePropsValidation = true; | ||
} | ||
@@ -666,0 +668,0 @@ break; |
@@ -13,37 +13,41 @@ /** | ||
module.exports = Components.detect(function(context) { | ||
/** | ||
* Helper for checking if parent node is render method. | ||
* @param {ASTNode} node to check | ||
* @returns {boolean} If previous node is method and name is render returns true, otherwise false; | ||
*/ | ||
function checkParent(node) { | ||
return (node.parent.type === 'Property' || node.parent.type === 'MethodDefinition') | ||
&& node.parent.key | ||
&& node.parent.key.name === 'render'; | ||
} | ||
module.exports = Components.detect(function(context, components) { | ||
/** | ||
* Helper for checking if child node exists and if it's ReturnStatement | ||
* @param {ASTNode} node to check | ||
* @returns {boolean} True if ReturnStatement exists, otherwise false | ||
* Mark a return statement as present | ||
* @param {ASTNode} node The AST node being checked. | ||
*/ | ||
function checkReturnStatementExistence(node) { | ||
if (!node.body && !node.body.body && !node.body.body.length) { | ||
return false; | ||
} | ||
var hasReturnStatement = node.body.body.some(function(elem) { | ||
return elem.type === 'ReturnStatement'; | ||
function markReturnStatementPresent(node) { | ||
components.set(node, { | ||
hasReturnStatement: true | ||
}); | ||
return hasReturnStatement; | ||
} | ||
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 ( | ||
(ancestors[i].type !== 'Property' && ancestors[i].type !== 'MethodDefinition') || | ||
ancestors[i].key.name !== 'render' || | ||
depth > 1 | ||
) { | ||
continue; | ||
} | ||
markReturnStatementPresent(node); | ||
} | ||
}, | ||
return { | ||
FunctionExpression: function(node) { | ||
if (checkParent(node) && !checkReturnStatementExistence(node)) { | ||
'Program:exit': function() { | ||
var list = components.list(); | ||
for (var component in list) { | ||
if (!list.hasOwnProperty(component) || list[component].hasReturnStatement) { | ||
continue; | ||
} | ||
context.report({ | ||
node: node, | ||
node: list[component].node, | ||
message: 'Your render method should have return statement' | ||
@@ -50,0 +54,0 @@ }); |
@@ -394,3 +394,6 @@ /** | ||
node = utils.getParentComponent(); | ||
if (!node) { | ||
if ( | ||
!node || | ||
(node.parent && node.parent.type === 'JSXExpressionContainer') | ||
) { | ||
return; | ||
@@ -411,3 +414,6 @@ } | ||
node = utils.getParentComponent(); | ||
if (!node) { | ||
if ( | ||
!node || | ||
(node.parent && node.parent.type === 'JSXExpressionContainer') | ||
) { | ||
return; | ||
@@ -414,0 +420,0 @@ } |
{ | ||
"name": "eslint-plugin-react", | ||
"version": "4.3.0", | ||
"version": "5.0.0", | ||
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>", | ||
@@ -28,3 +28,3 @@ "description": "React specific linting rules for ESLint", | ||
"coveralls": "2.11.9", | ||
"eslint": "2.7.0", | ||
"eslint": "2.8.0", | ||
"istanbul": "0.4.3", | ||
@@ -31,0 +31,0 @@ "mocha": "2.4.5" |
@@ -41,3 +41,3 @@ ESLint-plugin-React | ||
"pragma": "React", // Pragma to use, default to "React" | ||
"version": "0.14.0" // React version, default to the latest React stable release | ||
"version": "15.0" // React version, default to the latest React stable release | ||
} | ||
@@ -95,2 +95,3 @@ } | ||
* [require-extension](docs/rules/require-extension.md): Restrict file extensions that may be required | ||
* [require-render-return](docs/rules/require-render-return.md): Enforce ES5 or ES6 class for returning value in render function | ||
* [self-closing-comp](docs/rules/self-closing-comp.md): Prevent extra closing tags for components without children | ||
@@ -107,2 +108,3 @@ * [sort-comp](docs/rules/sort-comp.md): Enforce component methods order | ||
* [jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Enforce or disallow spaces around equal signs in JSX attributes (fixable) | ||
* [jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md): Enforce position of the first prop in JSX | ||
* [jsx-handler-names](docs/rules/jsx-handler-names.md): Enforce event handler naming conventions in JSX | ||
@@ -109,0 +111,0 @@ * [jsx-indent-props](docs/rules/jsx-indent-props.md): Validate props indentation in JSX (fixable) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
227189
51
5124
187