eslint-plugin-react
Advanced tools
Comparing version 3.8.0 to 3.9.0
@@ -6,2 +6,26 @@ # Change Log | ||
## [3.9.0] - 2015-11-17 | ||
### Added | ||
* Add `jsx-key` rule ([#293][] @benmosher) | ||
* Add `allow-in-func` option to `no-did-update-set-state` ([#300][]) | ||
* Add option to only enforce `jsx-closing-bracket-location` rule to one type of tag (nonEmpty or selfClosing) ([#307][]) | ||
### Fixed | ||
* Fix crash when destructuring with only the rest spread ([#269][]) | ||
* Fix variables detection when searching for related components ([#303][]) | ||
* Fix `no-unknown-property` to not check custom elements ([#308][] @zertosh) | ||
### Changed | ||
* Improve `jsx-closing-bracket-location` error message ([#301][] @alopatin) | ||
* Update dependencies | ||
[3.9.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.8.0...v3.9.0 | ||
[#293]: https://github.com/yannickcr/eslint-plugin-react/pull/293 | ||
[#300]: https://github.com/yannickcr/eslint-plugin-react/issues/300 | ||
[#307]: https://github.com/yannickcr/eslint-plugin-react/issues/307 | ||
[#269]: https://github.com/yannickcr/eslint-plugin-react/issues/269 | ||
[#303]: https://github.com/yannickcr/eslint-plugin-react/issues/303 | ||
[#308]: https://github.com/yannickcr/eslint-plugin-react/pull/308 | ||
[#301]: https://github.com/yannickcr/eslint-plugin-react/pull/301 | ||
## [3.8.0] - 2015-11-07 | ||
@@ -8,0 +32,0 @@ ### Added |
@@ -34,3 +34,4 @@ 'use strict'; | ||
'forbid-prop-types': require('./lib/rules/forbid-prop-types'), | ||
'prefer-es6-class': require('./lib/rules/prefer-es6-class') | ||
'prefer-es6-class': require('./lib/rules/prefer-es6-class'), | ||
'jsx-key': require('./lib/rules/jsx-key') | ||
}, | ||
@@ -67,4 +68,5 @@ rulesConfig: { | ||
'forbid-prop-types': 0, | ||
'prefer-es6-class': 0 | ||
'prefer-es6-class': 0, | ||
'jsx-key': 0 | ||
} | ||
}; |
@@ -12,3 +12,3 @@ /** | ||
var MESSAGE = 'The closing bracket must be {{location}}'; | ||
var MESSAGE = 'The closing bracket must be {{location}}{{details}}'; | ||
var MESSAGE_LOCATION = { | ||
@@ -35,3 +35,3 @@ 'after-props': 'placed after the last prop', | ||
// [1, {location: 'something'}] (back-compat) | ||
if (config.hasOwnProperty('location') && typeof config.location === 'string') { | ||
if (config.hasOwnProperty('location')) { | ||
options.nonEmpty = config.location; | ||
@@ -41,7 +41,7 @@ options.selfClosing = config.location; | ||
// [1, {nonEmpty: 'something'}] | ||
if (config.hasOwnProperty('nonEmpty') && typeof config.nonEmpty === 'string') { | ||
if (config.hasOwnProperty('nonEmpty')) { | ||
options.nonEmpty = config.nonEmpty; | ||
} | ||
// [1, {selfClosing: 'something'}] | ||
if (config.hasOwnProperty('selfClosing') && typeof config.selfClosing === 'string') { | ||
if (config.hasOwnProperty('selfClosing')) { | ||
options.selfClosing = config.selfClosing; | ||
@@ -72,2 +72,22 @@ } | ||
/** | ||
* Get the correct 0-indexed column for the closing bracket, given the | ||
* expected location. | ||
* @param {Object} tokens Locations of the opening bracket, closing bracket and last prop | ||
* @param {String} expectedLocation Expected location for the closing bracket | ||
* @return {?Number} The correct column for the closing bracket, or null | ||
*/ | ||
function getCorrectColumn(tokens, expectedLocation) { | ||
switch (expectedLocation) { | ||
case 'props-aligned': | ||
return tokens.lastProp.column; | ||
case 'tag-aligned': | ||
return tokens.opening.column; | ||
case 'line-aligned': | ||
return tokens.openingStartOfLine.column; | ||
default: | ||
return null; | ||
} | ||
} | ||
/** | ||
* Check if the closing bracket is correctly located | ||
@@ -85,7 +105,6 @@ * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop | ||
case 'props-aligned': | ||
return tokens.lastProp.column === tokens.closing.column; | ||
case 'tag-aligned': | ||
return tokens.opening.column === tokens.closing.column; | ||
case 'line-aligned': | ||
return tokens.openingStartOfLine.column === tokens.closing.column; | ||
var correctColumn = getCorrectColumn(tokens, expectedLocation); | ||
return correctColumn === tokens.closing.column; | ||
default: | ||
@@ -137,4 +156,18 @@ return true; | ||
} | ||
context.report(node, MESSAGE, { | ||
location: MESSAGE_LOCATION[expectedLocation] | ||
var data = {location: MESSAGE_LOCATION[expectedLocation], details: ''}; | ||
var correctColumn = getCorrectColumn(tokens, expectedLocation); | ||
if (correctColumn !== null) { | ||
var expectedNextLine = tokens.lastProp && | ||
(tokens.lastProp.line === tokens.closing.line); | ||
data.details = ' (expected column ' + (correctColumn + 1) + | ||
(expectedNextLine ? ' on the next line)' : ')'); | ||
} | ||
context.report({ | ||
node: node, | ||
loc: tokens.closing, | ||
message: MESSAGE, | ||
data: data | ||
}); | ||
@@ -163,6 +196,6 @@ } | ||
nonEmpty: { | ||
enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned'] | ||
enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned', false] | ||
}, | ||
selfClosing: { | ||
enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned'] | ||
enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned', false] | ||
} | ||
@@ -169,0 +202,0 @@ }, |
@@ -13,2 +13,4 @@ /** | ||
var mode = context.options[0] || 'never'; | ||
// -------------------------------------------------------------------------- | ||
@@ -22,13 +24,19 @@ // Public | ||
var callee = node.callee; | ||
if (callee.type !== 'MemberExpression') { | ||
if ( | ||
callee.type !== 'MemberExpression' || | ||
callee.object.type !== 'ThisExpression' || | ||
callee.property.name !== 'setState' | ||
) { | ||
return; | ||
} | ||
if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'setState') { | ||
return; | ||
} | ||
var ancestors = context.getAncestors(callee); | ||
var ancestors = context.getAncestors(callee).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 !== 'componentDidUpdate' | ||
ancestors[i].key.name !== 'componentDidUpdate' || | ||
(mode === 'allow-in-func' && depth > 1) | ||
) { | ||
@@ -38,2 +46,3 @@ continue; | ||
context.report(callee, 'Do not use setState in componentDidUpdate'); | ||
break; | ||
} | ||
@@ -45,2 +54,4 @@ } | ||
module.exports.schema = []; | ||
module.exports.schema = [{ | ||
enum: ['allow-in-func'] | ||
}]; |
@@ -39,9 +39,19 @@ /** | ||
/** | ||
* Checks if a node name match the JSX tag convention. | ||
* @param {String} name - Name of the node to check. | ||
* Checks if a node matches the JSX tag convention. | ||
* @param {Object} node - JSX element being tested. | ||
* @returns {boolean} Whether or not the node name match the JSX tag convention. | ||
*/ | ||
var tagConvention = /^[a-z]|\-/; | ||
function isTagName(name) { | ||
return tagConvention.test(name); | ||
var tagConvention = /^[a-z][^-]*$/; | ||
function isTagName(node) { | ||
if (tagConvention.test(node.parent.name.name)) { | ||
// http://www.w3.org/TR/custom-elements/#type-extension-semantics | ||
return !node.parent.attributes.some(function(attrNode) { | ||
return ( | ||
attrNode.type === 'JSXAttribute' && | ||
attrNode.name.type === 'JSXIdentifier' && | ||
attrNode.name.name === 'is' | ||
); | ||
}); | ||
} | ||
return false; | ||
} | ||
@@ -76,3 +86,3 @@ | ||
var standardName = getStandardName(node.name.name); | ||
if (!isTagName(node.parent.name.name) || !standardName) { | ||
if (!isTagName(node) || !standardName) { | ||
return; | ||
@@ -79,0 +89,0 @@ } |
@@ -183,3 +183,3 @@ /** | ||
function getKeyValue(node) { | ||
var key = node.key; | ||
var key = node.key || node.argument; | ||
return key.type === 'Identifier' ? key.name : key.value; | ||
@@ -186,0 +186,0 @@ } |
@@ -8,2 +8,3 @@ /** | ||
var util = require('util'); | ||
var variableUtil = require('./variable'); | ||
@@ -269,3 +270,3 @@ /** | ||
var variableInScope; | ||
var variables = context.getScope().variables; | ||
var variables = variableUtil.variablesInScope(context); | ||
for (i = 0, j = variables.length; i < j; i++) { | ||
@@ -272,0 +273,0 @@ if (variables[i].name === variableName) { |
{ | ||
"name": "eslint-plugin-react", | ||
"version": "3.8.0", | ||
"version": "3.9.0", | ||
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>", | ||
@@ -26,7 +26,7 @@ "description": "React specific linting rules for ESLint", | ||
"devDependencies": { | ||
"babel-eslint": "4.1.4", | ||
"babel-eslint": "4.1.5", | ||
"coveralls": "2.11.4", | ||
"eslint": "1.8.0", | ||
"eslint": "1.9.0", | ||
"istanbul": "0.4.0", | ||
"mocha": "2.3.3" | ||
"mocha": "2.3.4" | ||
}, | ||
@@ -33,0 +33,0 @@ "keywords": [ |
@@ -55,2 +55,3 @@ ESLint-plugin-React | ||
"react/jsx-indent-props": 1, | ||
"react/jsx-key": 1, | ||
"react/jsx-max-props-per-line": 1, | ||
@@ -92,2 +93,3 @@ "react/jsx-no-bind": 1, | ||
* [jsx-indent-props](docs/rules/jsx-indent-props.md): Validate props indentation in JSX | ||
* [jsx-key](docs/rules/jsx-key.md): Validate JSX has key prop when in array or iterator | ||
* [jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md): Limit maximum of props on a single line in JSX | ||
@@ -94,0 +96,0 @@ * [jsx-no-bind](docs/rules/jsx-no-bind.md): Prevent usage of `.bind()` and arrow functions in JSX props |
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
147866
38
3421
150