eslint-plugin-react
Advanced tools
Comparing version 5.0.1 to 5.1.0
@@ -6,2 +6,32 @@ # Change Log | ||
## [5.1.0] - 2016-05-10 | ||
### Added | ||
* Add `jsx-no-target-blank` rule ([#582][] @Gasparila) | ||
* Add `allowAllCaps` and `ignore` options to `jsx-pascal-case` ([#575][]) | ||
* Add class properties support to `require-render-return` ([#564][]) | ||
### Fixed | ||
* Fix `jsx-closing-bracket-location` fixer ([#533][] @dtinth) | ||
* Fix `require-render-return` to only check valid render methods ([#563][]) | ||
* Fix detection to allow simple `this` usage in fonctional components ([#576][]) | ||
* Fix `forbid-prop-types` crash ([#579][]) | ||
* Fix comment handling in `jsx-curly-spacing` ([#584][]) | ||
### Changed | ||
* Update dependencies | ||
* Documentation improvements (@coryhouse, [#581][] @scurker, [#588][]) | ||
[5.1.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v5.0.1...v5.1.0 | ||
[#582]: https://github.com/yannickcr/eslint-plugin-react/pull/582 | ||
[#575]: https://github.com/yannickcr/eslint-plugin-react/issues/575 | ||
[#564]: https://github.com/yannickcr/eslint-plugin-react/issues/564 | ||
[#533]: https://github.com/yannickcr/eslint-plugin-react/issues/533 | ||
[#563]: https://github.com/yannickcr/eslint-plugin-react/issues/563 | ||
[#576]: https://github.com/yannickcr/eslint-plugin-react/issues/576 | ||
[#579]: https://github.com/yannickcr/eslint-plugin-react/issues/579 | ||
[#584]: https://github.com/yannickcr/eslint-plugin-react/pull/584 | ||
[#559]: https://github.com/yannickcr/eslint-plugin-react/pull/559 | ||
[#581]: https://github.com/yannickcr/eslint-plugin-react/pull/581 | ||
[#588]: https://github.com/yannickcr/eslint-plugin-react/issues/588 | ||
## [5.0.1] - 2016-04-18 | ||
@@ -8,0 +38,0 @@ ### Fixed |
@@ -46,3 +46,4 @@ 'use strict'; | ||
'require-render-return': require('./lib/rules/require-render-return'), | ||
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line') | ||
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'), | ||
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank') | ||
}, | ||
@@ -49,0 +50,0 @@ configs: { |
@@ -56,2 +56,5 @@ /** | ||
declarations.forEach(function(declaration) { | ||
if (declaration.type !== 'Property') { | ||
return; | ||
} | ||
var target; | ||
@@ -58,0 +61,0 @@ var value = declaration.value; |
@@ -145,14 +145,27 @@ /** | ||
var lastAttributeEndPos; | ||
var lastAttributeStartPos; | ||
/** | ||
* Get an unique ID for a given JSXOpeningElement | ||
* | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {String} Unique ID (based on its range) | ||
*/ | ||
function getOpeningElementId(node) { | ||
return node.range.join(':'); | ||
} | ||
var lastAttributeNode = {}; | ||
return { | ||
JSXAttribute: function(node) { | ||
lastAttributeEndPos = node.end; | ||
lastAttributeStartPos = node.start; | ||
lastAttributeNode[getOpeningElementId(node.parent)] = node; | ||
}, | ||
JSXSpreadAttribute: function(node) { | ||
lastAttributeNode[getOpeningElementId(node.parent)] = node; | ||
}, | ||
'JSXOpeningElement:exit': function(node) { | ||
var cachedLastAttributeEndPos = lastAttributeEndPos; | ||
var cachedLastAttributeStartPos = lastAttributeStartPos; | ||
var attributeNode = lastAttributeNode[getOpeningElementId(node)]; | ||
var cachedLastAttributeEndPos = attributeNode ? attributeNode.end : null; | ||
var cachedLastAttributeStartPos = attributeNode ? attributeNode.start : null; | ||
var expectedNextLine; | ||
@@ -162,5 +175,2 @@ var tokens = getTokensLocations(node); | ||
lastAttributeStartPos = null; | ||
lastAttributeEndPos = null; | ||
if (hasCorrectLocation(tokens, expectedLocation)) { | ||
@@ -193,4 +203,4 @@ return; | ||
} | ||
return fixer.replaceTextRange([node.name.loc.end.column + 1, node.end], | ||
(expectedNextLine ? '\n' : '') + closingTag); | ||
return fixer.replaceTextRange([node.name.range[1], node.end], | ||
(expectedNextLine ? '\n' : ' ') + closingTag); | ||
case 'after-props': | ||
@@ -204,17 +214,7 @@ return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], | ||
case 'tag-aligned': | ||
var tagSpaces = new Array(node.start); | ||
var tagSpaces = new Array(+correctColumn + 1); | ||
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], | ||
'\n' + tagSpaces.join(' ') + closingTag); | ||
case 'line-aligned': | ||
var walkNode = node; | ||
var lineSpaces = 0; | ||
while ((walkNode = walkNode.parent)) { | ||
if (walkNode.type === 'VariableDeclaration' || | ||
walkNode.type === 'ReturnStatement' || | ||
walkNode.type === 'ExpressionStatement') { | ||
lineSpaces = walkNode.loc.start.column + 1; | ||
break; | ||
} | ||
} | ||
lineSpaces = new Array(lineSpaces); | ||
var lineSpaces = new Array(+correctColumn + 1); | ||
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], | ||
@@ -221,0 +221,0 @@ '\n' + lineSpaces.join(' ') + closingTag); |
@@ -38,12 +38,2 @@ /** | ||
/** | ||
* Determines whether two adjacent tokens have whitespace between them. | ||
* @param {Object} left - The left token object. | ||
* @param {Object} right - The right token object. | ||
* @returns {boolean} Whether or not there is space between the tokens. | ||
*/ | ||
function isSpaced(left, right) { | ||
return left.range[1] < right.range[0]; | ||
} | ||
/** | ||
* Reports that there shouldn't be a newline after the first token | ||
@@ -165,3 +155,3 @@ * @param {ASTNode} node - The node to report in the event of an error. | ||
if (spaced) { | ||
if (!isSpaced(first, second)) { | ||
if (!sourceCode.isSpaceBetweenTokens(first, second)) { | ||
reportRequiredBeginningSpace(node, first); | ||
@@ -172,3 +162,3 @@ } else if (!multiline && isMultiline(first, second)) { | ||
if (!isSpaced(penultimate, last)) { | ||
if (!sourceCode.isSpaceBetweenTokens(penultimate, last)) { | ||
reportRequiredEndingSpace(node, last); | ||
@@ -183,7 +173,7 @@ } else if (!multiline && isMultiline(penultimate, last)) { | ||
// "never" setting if we get here. | ||
if (isSpaced(first, second) && !(multiline && isMultiline(first, second))) { | ||
if (sourceCode.isSpaceBetweenTokens(first, second) && !(multiline && isMultiline(first, second))) { | ||
reportNoBeginningSpace(node, first); | ||
} | ||
if (isSpaced(penultimate, last) && !(multiline && isMultiline(penultimate, last))) { | ||
if (sourceCode.isSpaceBetweenTokens(penultimate, last) && !(multiline && isMultiline(penultimate, last))) { | ||
reportNoEndingSpace(node, last); | ||
@@ -200,10 +190,6 @@ } | ||
var first = context.getFirstToken(node); | ||
var second = context.getFirstToken(node, 1); | ||
var penultimate = sourceCode.getLastToken(node, 1); | ||
var last = sourceCode.getLastToken(node); | ||
var second = context.getTokenAfter(first); | ||
var penultimate = sourceCode.getTokenBefore(last); | ||
if (first === penultimate && second === last) { | ||
return; | ||
} | ||
validateBraceSpacing(node, first, second, penultimate, last); | ||
@@ -210,0 +196,0 @@ } |
@@ -14,2 +14,3 @@ /** | ||
var COMPAT_TAG_REGEX = /^[a-z]|\-/; | ||
var ALL_CAPS_TAG_REGEX = /^[A-Z0-9]+$/; | ||
@@ -22,2 +23,6 @@ // ------------------------------------------------------------------------------ | ||
var configuration = context.options[0] || {}; | ||
var allowAllCaps = configuration.allowAllCaps || false; | ||
var ignore = configuration.ignore || []; | ||
return { | ||
@@ -41,4 +46,6 @@ JSXOpeningElement: function(node) { | ||
var isCompatTag = COMPAT_TAG_REGEX.test(node.name); | ||
var isAllowedAllCaps = allowAllCaps && ALL_CAPS_TAG_REGEX.test(node.name); | ||
var isIgnored = ignore.indexOf(node.name) !== -1; | ||
if (!isPascalCase && !isCompatTag) { | ||
if (!isPascalCase && !isCompatTag && !isAllowedAllCaps && !isIgnored) { | ||
context.report({ | ||
@@ -53,1 +60,14 @@ node: node, | ||
}; | ||
module.exports.schema = [{ | ||
type: 'object', | ||
properties: { | ||
allowAllCaps: { | ||
type: 'boolean' | ||
}, | ||
ignore: { | ||
type: 'array' | ||
} | ||
}, | ||
additionalProperties: false | ||
}]; |
@@ -14,3 +14,3 @@ /** | ||
var DEFAULTS = { | ||
extentions: ['.jsx'] | ||
extensions: ['.jsx'] | ||
}; | ||
@@ -42,7 +42,7 @@ | ||
function getExtentionsConfig() { | ||
return context.options[0] && context.options[0].extensions || DEFAULTS.extentions; | ||
function getExtensionsConfig() { | ||
return context.options[0] && context.options[0].extensions || DEFAULTS.extensions; | ||
} | ||
var forbiddenExtensions = getExtentionsConfig().reduce(function (extensions, extension) { | ||
var forbiddenExtensions = getExtensionsConfig().reduce(function (extensions, extension) { | ||
extensions[extension] = true; | ||
@@ -49,0 +49,0 @@ return extensions; |
@@ -25,2 +25,50 @@ /** | ||
/** | ||
* 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; | ||
} | ||
return node.key.name; | ||
} | ||
/** | ||
* 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 { | ||
@@ -35,4 +83,4 @@ ReturnStatement: function(node) { | ||
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 | ||
@@ -46,2 +94,9 @@ ) { | ||
ArrowFunctionExpression: function(node) { | ||
if (node.expression === false || getPropertyName(node.parent) !== 'render') { | ||
return; | ||
} | ||
markReturnStatementPresent(node); | ||
}, | ||
'Program:exit': function() { | ||
@@ -52,2 +107,3 @@ var list = components.list(); | ||
!list.hasOwnProperty(component) || | ||
!hasRenderMethod(list[component].node) || | ||
list[component].hasReturnStatement || | ||
@@ -54,0 +110,0 @@ (!utils.isES5Component(list[component].node) && !utils.isES6Component(list[component].node)) |
@@ -427,7 +427,7 @@ /** | ||
ThisExpression: function(node) { | ||
node = utils.getParentComponent(); | ||
if (!node || !/Function/.test(node.type)) { | ||
var component = utils.getParentComponent(); | ||
if (!component || !/Function/.test(component.type) || !node.parent.property) { | ||
return; | ||
} | ||
// Ban functions with a ThisExpression | ||
// Ban functions accessing a property on a ThisExpression | ||
components.add(node, 0); | ||
@@ -434,0 +434,0 @@ }, |
{ | ||
"name": "eslint-plugin-react", | ||
"version": "5.0.1", | ||
"version": "5.1.0", | ||
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>", | ||
@@ -26,5 +26,5 @@ "description": "React specific linting rules for ESLint", | ||
"devDependencies": { | ||
"babel-eslint": "6.0.2", | ||
"babel-eslint": "6.0.4", | ||
"coveralls": "2.11.9", | ||
"eslint": "2.8.0", | ||
"eslint": "2.9.0", | ||
"istanbul": "0.4.3", | ||
@@ -31,0 +31,0 @@ "mocha": "2.4.5" |
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
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
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
5220
222259
1
0