diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index 546e921532..7409a62855 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -227,7 +227,9 @@ module.exports = { if (!node || !Array.isArray(args)) { return; } - args.filter((arg) => arg.type === 'ObjectExpression').forEach((object) => validatePropNaming(node, object.properties)); + args.forEach((object) => object.type === 'ObjectExpression' + && validatePropNaming(node, object.properties) + ); } // -------------------------------------------------------------------------- diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index d57e178c82..63dcc642f1 100644 --- a/lib/rules/default-props-match-prop-types.js +++ b/lib/rules/default-props-match-prop-types.js @@ -94,11 +94,13 @@ module.exports = { const list = components.list(); // If no defaultProps could be found, we don't report anything. - values(list).filter((component) => component.defaultProps).forEach((component) => { - reportInvalidDefaultProps( - component.declaredPropTypes, - component.defaultProps || {} - ); + values(list).forEach((component) => { + if (component.defaultProps) { + reportInvalidDefaultProps( + component.declaredPropTypes, + component.defaultProps || {} + ); + } }); } }; diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index 58b45e86f5..d4ca1cd263 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -13,6 +13,16 @@ const DEFAULT_OPTION = 'always'; function createSFCParams() { const queue = []; + const identifyProps = (params) => { + const props = params[0]; + return props && !props.destructuring && props.name; + }; + + const identifyContext = (params) => { + const context = params[1]; + return context && !context.destructuring && context.name; + }; + return { push(params) { queue.unshift(params); @@ -21,17 +31,11 @@ function createSFCParams() { queue.shift(); }, propsName() { - const found = queue.find((params) => { - const props = params[0]; - return props && !props.destructuring && props.name; - }); + const found = queue.find(identifyProps); return found && found[0] && found[0].name; }, contextName() { - const found = queue.find((params) => { - const context = params[1]; - return context && !context.destructuring && context.name; - }); + const found = queue.find(identifyContext); return found && found[1] && found.name; } }; @@ -87,24 +91,26 @@ module.exports = { * FunctionDeclaration, or FunctionExpression */ function handleStatelessComponent(node) { - const params = evalParams(node.params); - const SFCComponent = components.get(context.getScope(node).block); if (!SFCComponent) { return; } + const params = evalParams(node.params); + sfcParams.push(params); - if (params[0] && params[0].destructuring && components.get(node) && configuration === 'never') { - context.report({ - node, - messageId: 'noDestructPropsInSFCArg' - }); - } else if (params[1] && params[1].destructuring && components.get(node) && configuration === 'never') { - context.report({ - node, - messageId: 'noDestructContextInSFCArg' - }); + if (configuration === 'never') { + if (params[0] && params[0].destructuring && components.get(node)) { + context.report({ + node, + messageId: 'noDestructPropsInSFCArg' + }); + } else if (params[1] && params[1].destructuring && components.get(node)) { + context.report({ + node, + messageId: 'noDestructContextInSFCArg' + }); + } } } @@ -116,15 +122,11 @@ module.exports = { } function handleSFCUsage(node) { - const propsName = sfcParams.propsName(); - const contextName = sfcParams.contextName(); // props.aProp || context.aProp - const isPropUsed = ( - (propsName && node.object.name === propsName) - || (contextName && node.object.name === contextName) - ) - && !isAssignmentLHS(node); - if (isPropUsed && configuration === 'always') { + const isPropUsed = node.object.name && ( + node.object.name === sfcParams.propsName() || node.object.name === sfcParams.contextName() + ) && !isAssignmentLHS(node); + if (isPropUsed) { context.report({ node, messageId: 'useDestructAssignment', @@ -155,8 +157,7 @@ module.exports = { ); if ( - isPropUsed && configuration === 'always' - && !(ignoreClassFields && isInClassProperty(node)) + isPropUsed && !(ignoreClassFields && isInClassProperty(node)) ) { context.report({ node, @@ -183,49 +184,51 @@ module.exports = { 'FunctionExpression:exit': handleStatelessComponentExit, MemberExpression(node) { - const SFCComponent = components.get(context.getScope(node).block); - const classComponent = utils.getParentComponent(node); - if (SFCComponent) { - handleSFCUsage(node); - } - if (classComponent) { - handleClassUsage(node); + if (configuration === 'always') { + const SFCComponent = components.get(context.getScope(node).block); + const classComponent = utils.getParentComponent(node); + if (SFCComponent) { + handleSFCUsage(node); + } + if (classComponent) { + handleClassUsage(node); + } } }, VariableDeclarator(node) { - const classComponent = utils.getParentComponent(node); - const SFCComponent = components.get(context.getScope(node).block); - - const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern'); - // let {foo} = props; - const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context'); - // let {foo} = this.props; - const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && ( - node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state' - ); - - if (SFCComponent && destructuringSFC && configuration === 'never') { - context.report({ - node, - messageId: 'noDestructAssignment', - data: { - type: node.init.name - } - }); - } + if (configuration === 'never') { + const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern'); + // let {foo} = props; + const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context'); + // let {foo} = this.props; + const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && ( + node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state' + ); + + if (destructuringSFC && components.get(context.getScope(node).block)) { + context.report({ + node, + messageId: 'noDestructAssignment', + data: { + type: node.init.name + } + }); + } - if ( - classComponent && destructuringClass && configuration === 'never' - && !(ignoreClassFields && node.parent.type === 'ClassProperty') - ) { - context.report({ - node, - messageId: 'noDestructAssignment', - data: { - type: node.init.property.name - } - }); + if ( + destructuringClass + && !(ignoreClassFields && node.parent.type === 'ClassProperty') + && utils.getParentComponent(node) + ) { + context.report({ + node, + messageId: 'noDestructAssignment', + data: { + type: node.init.property.name + } + }); + } } } }; diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js index 8d5da40322..89265f9ada 100644 --- a/lib/rules/display-name.js +++ b/lib/rules/display-name.js @@ -43,14 +43,16 @@ module.exports = { const config = context.options[0] || {}; const ignoreTranspilerName = config.ignoreTranspilerName || false; + const markFlag = { + hasDisplayName: true + }; + /** * Mark a prop type as declared * @param {ASTNode} node The AST node being checked. */ function markDisplayNameAsDeclared(node) { - components.set(node, { - hasDisplayName: true - }); + components.set(node, markFlag); } /** @@ -230,9 +232,11 @@ module.exports = { 'Program:exit'() { const list = components.list(); // Report missing display name for all components - values(list).filter((component) => !component.hasDisplayName) + values(list) .forEach((component) => { - reportMissingDisplayName(component); + if (!component.hasDisplayName) { + reportMissingDisplayName(component); + } }); } }; diff --git a/lib/rules/forbid-component-props.js b/lib/rules/forbid-component-props.js index d1f61f4171..eb316c8423 100644 --- a/lib/rules/forbid-component-props.js +++ b/lib/rules/forbid-component-props.js @@ -83,8 +83,6 @@ module.exports = { return { JSXAttribute(node) { const parentName = node.parent.name; - // Extract a component name when using a "namespace", e.g. ``. - const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`; const componentName = parentName.name || parentName.property.name; if (componentName && typeof componentName[0] === 'string' && componentName[0] !== componentName[0].toUpperCase()) { // This is a DOM node, not a Component, so exit. @@ -93,6 +91,9 @@ module.exports = { const prop = node.name.name; + // Extract a component name when using a "namespace", e.g. ``. + const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`; + if (!isForbidden(prop, tag)) { return; } diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js index 89b5f7f7f8..39fbcdc002 100644 --- a/lib/rules/jsx-indent.js +++ b/lib/rules/jsx-indent.js @@ -277,13 +277,11 @@ module.exports = { */ function checkNodesIndent(node, indent, excludeCommas) { const nodeIndent = getNodeIndent(node, false, excludeCommas); - const isCorrectRightInLogicalExp = isRightInLogicalExp(node) && (nodeIndent - indent) === indentSize; - const isCorrectAlternateInCondExp = isAlternateInConditionalExp(node) && (nodeIndent - indent) === 0; if ( nodeIndent !== indent && astUtil.isNodeFirstInLine(context, node) - && !isCorrectRightInLogicalExp - && !isCorrectAlternateInCondExp + && !(isRightInLogicalExp(node) && (nodeIndent - indent) === indentSize) + && !(isAlternateInConditionalExp(node) && (nodeIndent - indent) === 0) ) { report(node, indent, nodeIndent); } diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 5586d589ab..c32a654d07 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -19,6 +19,8 @@ const defaultOptions = { checkKeyMustBeforeSpread: false }; +const hasReturnStatementType = (item) => item.type === 'ReturnStatement'; + module.exports = { meta: { docs: { @@ -78,7 +80,7 @@ module.exports = { } function getReturnStatement(body) { - return body.filter((item) => item.type === 'ReturnStatement')[0]; + return body.find(hasReturnStatementType); } function isKeyAfterSpread(attributes) { diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js index 1b14050326..649003cbaa 100644 --- a/lib/rules/jsx-no-bind.js +++ b/lib/rules/jsx-no-bind.js @@ -7,6 +7,7 @@ 'use strict'; +const entries = require('object.entries'); const propName = require('jsx-ast-utils/propName'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); @@ -110,27 +111,34 @@ module.exports = { blockVariableNameSets[blockStart][violationType].add(variableName); } + function onlyBlockStatements(ancestor) { + return ancestor.type === 'BlockStatement'; + } + function getBlockStatementAncestors(node) { - return context.getAncestors(node).reverse().filter( - (ancestor) => ancestor.type === 'BlockStatement' - ); + return context.getAncestors(node).filter(onlyBlockStatements).reverse(); } function reportVariableViolation(node, name, blockStart) { const blockSets = blockVariableNameSets[blockStart]; - const violationTypes = Object.keys(blockSets); - - return violationTypes.find((type) => { - if (blockSets[type].has(name)) { - context.report({ - node, - messageId: type - }); + const violationTypes = entries(blockSets); + + const foundTypeEntry = violationTypes.find((typeEntry) => { + if (typeEntry[1].has(name)) { return true; } return false; }); + + if (foundTypeEntry) { + context.report({ + node, + messageId: foundTypeEntry[0] + }); + } + + return foundTypeEntry; } function findVariableViolation(node, name) { @@ -148,40 +156,43 @@ module.exports = { if (!node.init) { return; } - const blockAncestors = getBlockStatementAncestors(node); - const variableViolationType = getNodeViolationType(node.init); - - if ( - blockAncestors.length > 0 - && variableViolationType - && node.parent.kind === 'const' // only support const right now - ) { - addVariableNameToSet( - variableViolationType, node.id.name, blockAncestors[0].range[0] - ); + + // only support const right now + if (node.parent.kind === 'const') { + const blockAncestors = getBlockStatementAncestors(node); + const variableViolationType = getNodeViolationType(node.init); + + if ( + blockAncestors.length > 0 + && variableViolationType + ) { + addVariableNameToSet( + variableViolationType, node.id.name, blockAncestors[0].range[0] + ); + } } }, JSXAttribute(node) { - const isRef = configuration.ignoreRefs && propName(node) === 'ref'; - if (isRef || !node.value || !node.value.expression) { + if (!node.value || !node.value.expression || (configuration.ignoreRefs && propName(node) === 'ref')) { return; } - const isDOMComponent = jsxUtil.isDOMComponent(node.parent); - if (configuration.ignoreDOMComponents && isDOMComponent) { + if (configuration.ignoreDOMComponents && jsxUtil.isDOMComponent(node.parent)) { return; } const valueNode = node.value.expression; const valueNodeType = valueNode.type; - const nodeViolationType = getNodeViolationType(valueNode); if (valueNodeType === 'Identifier') { findVariableViolation(node, valueNode.name); - } else if (nodeViolationType) { - context.report({ - node, - messageId: nodeViolationType - }); + } else { + const nodeViolationType = getNodeViolationType(valueNode); + if (nodeViolationType) { + context.report({ + node, + messageId: nodeViolationType + }); + } } } }; diff --git a/lib/rules/jsx-no-useless-fragment.js b/lib/rules/jsx-no-useless-fragment.js index cc6187abbd..8554546ac1 100644 --- a/lib/rules/jsx-no-useless-fragment.js +++ b/lib/rules/jsx-no-useless-fragment.js @@ -27,7 +27,7 @@ function isOnlyWhitespace(text) { * @returns {boolean} */ function isNonspaceJSXTextOrJSXCurly(node) { - return (isJSXText(node) && !isOnlyWhitespace(node.raw)) || node.type === 'JSXExpressionContainer'; + return node.type === 'JSXExpressionContainer' || (isJSXText(node) && !isOnlyWhitespace(node.raw)); } /** diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js index 696525dfda..a6f3f11f71 100644 --- a/lib/rules/jsx-pascal-case.js +++ b/lib/rules/jsx-pascal-case.js @@ -25,20 +25,23 @@ function testLowerCase(char) { return char === lowerCase && lowerCase !== char.toUpperCase(); } +const isCharNonAlphanumeric = (char) => !testDigit(char) && char.toLowerCase() === char.toUpperCase(); +const isCharLowerCaseOrDigit = (char) => testLowerCase(char) || testDigit(char); + function testPascalCase(name) { if (!testUpperCase(name.charAt(0))) { return false; } const anyNonAlphaNumeric = Array.prototype.some.call( name.slice(1), - (char) => char.toLowerCase() === char.toUpperCase() && !testDigit(char) + isCharNonAlphanumeric ); if (anyNonAlphaNumeric) { return false; } return Array.prototype.some.call( name.slice(1), - (char) => testLowerCase(char) || testDigit(char) + isCharLowerCaseOrDigit ); } @@ -120,23 +123,25 @@ module.exports = { if (isCompatTag) return undefined; const name = elementType(node); - let checkNames = [name]; + let checkNames; let index = 0; if (name.lastIndexOf(':') > -1) { checkNames = name.split(':'); } else if (name.lastIndexOf('.') > -1) { checkNames = name.split('.'); + } else { + checkNames = [name]; } do { const splitName = checkNames[index]; if (splitName.length === 1) return undefined; - const isPascalCase = testPascalCase(splitName); - const isAllowedAllCaps = allowAllCaps && testAllCaps(splitName); - const isIgnored = ignoreCheck(ignore, splitName); - if (!isPascalCase && !isAllowedAllCaps && !isIgnored) { + if (!testPascalCase(splitName) + && !(allowAllCaps && testAllCaps(splitName)) + && !ignoreCheck(ignore, splitName) + ) { context.report({ node, messageId: allowAllCaps ? 'usePascalOrSnakeCase' : 'usePascalCase', diff --git a/lib/rules/no-access-state-in-setstate.js b/lib/rules/no-access-state-in-setstate.js index a0a910842d..21c6b839e5 100644 --- a/lib/rules/no-access-state-in-setstate.js +++ b/lib/rules/no-access-state-in-setstate.js @@ -157,12 +157,13 @@ module.exports = { while (current.type !== 'Program') { if (isFirstArgumentInSetStateCall(current, node)) { vars - .filter((v) => v.scope === context.getScope() && v.variableName === node.name) .forEach((v) => { - context.report({ - node: v.node, - messageId: 'useCallback' - }); + if (v.scope === context.getScope() && v.variableName === node.name) { + context.report({ + node: v.node, + messageId: 'useCallback' + }); + } }); } current = current.parent; diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index 9b3e6eb617..4999c12ff9 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -47,6 +47,8 @@ module.exports = { some: 1 }; + const pragmaFromContext = pragma.getFromContext(context); + const reactChildMethods = ['map', 'forEach']; function isArrayIndex(node) { @@ -73,7 +75,7 @@ module.exports = { if (obj && obj.name === 'Children') { return true; } - if (obj && obj.object && obj.object.name === pragma.getFromContext(context)) { + if (obj && obj.object && obj.object.name === pragmaFromContext) { return true; } @@ -141,8 +143,10 @@ module.exports = { if (node.type === 'TemplateLiteral') { // key={`foo-${bar}`} - node.expressions.filter(isArrayIndex).forEach(() => { - context.report({node, messageId: 'noArrayIndex'}); + node.expressions.forEach((expressionNode) => { + if (isArrayIndex(expressionNode)) { + context.report({node, messageId: 'noArrayIndex'}); + } }); return; @@ -152,8 +156,10 @@ module.exports = { // key={'foo' + bar} const identifiers = getIdentifiersFromBinaryExpression(node); - identifiers.filter(isArrayIndex).forEach(() => { - context.report({node, messageId: 'noArrayIndex'}); + identifiers.forEach((identifierNode) => { + if (isArrayIndex(identifierNode)) { + context.report({node, messageId: 'noArrayIndex'}); + } }); } } diff --git a/lib/rules/no-set-state.js b/lib/rules/no-set-state.js index abf0c0a337..46b1399d38 100644 --- a/lib/rules/no-set-state.js +++ b/lib/rules/no-set-state.js @@ -80,8 +80,10 @@ module.exports = { 'Program:exit'() { const list = components.list(); - values(list).filter((component) => !isValid(component)).forEach((component) => { - reportSetStateUsages(component); + values(list).forEach((component) => { + if (!isValid(component)) { + reportSetStateUsages(component); + } }); } }; diff --git a/lib/rules/no-string-refs.js b/lib/rules/no-string-refs.js index 920a8a5cf7..e21c44e6d8 100644 --- a/lib/rules/no-string-refs.js +++ b/lib/rules/no-string-refs.js @@ -46,12 +46,12 @@ module.exports = { */ function isRefsUsage(node) { return Boolean( - ( + node.object.type === 'ThisExpression' + && node.property.name === 'refs' + && ( utils.getParentES6Component() || utils.getParentES5Component() ) - && node.object.type === 'ThisExpression' - && node.property.name === 'refs' ); } diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index b59b549bce..cf5682a964 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -112,35 +112,37 @@ module.exports = { return; } - values(props || {}).forEach((prop) => { - // Skip props that check instances - if (prop === true) { - return; - } + if (props) { + values(props).forEach((prop) => { + // Skip props that check instances + if (prop === true) { + return; + } - if ((prop.type === 'shape' || prop.type === 'exact') && configuration.skipShapeProps) { - return; - } + if ((prop.type === 'shape' || prop.type === 'exact') && configuration.skipShapeProps) { + return; + } - if (prop.node && prop.node.typeAnnotation && prop.node.typeAnnotation.typeAnnotation - && prop.node.typeAnnotation.typeAnnotation.type === 'TSNeverKeyword') { - return; - } + if (prop.node && prop.node.typeAnnotation && prop.node.typeAnnotation.typeAnnotation + && prop.node.typeAnnotation.typeAnnotation.type === 'TSNeverKeyword') { + return; + } - if (prop.node && !isIgnored(prop.fullName) && !isPropUsed(component, prop)) { - context.report({ - node: prop.node.key || prop.node, - messageId: 'unusedPropType', - data: { - name: prop.fullName - } - }); - } + if (prop.node && !isIgnored(prop.fullName) && !isPropUsed(component, prop)) { + context.report({ + node: prop.node.key || prop.node, + messageId: 'unusedPropType', + data: { + name: prop.fullName + } + }); + } - if (prop.children) { - reportUnusedPropType(component, prop.children); - } - }); + if (prop.children) { + reportUnusedPropType(component, prop.children); + } + }); + } } /** @@ -159,11 +161,10 @@ module.exports = { 'Program:exit'() { const list = components.list(); // Report undeclared proptypes for all classes - values(list).filter((component) => mustBeValidated(component)).forEach((component) => { - if (!mustBeValidated(component)) { - return; + values(list).forEach((component) => { + if (mustBeValidated(component)) { + reportUnusedPropTypes(component); } - reportUnusedPropTypes(component); }); } }; diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index d91a94c9a9..d6eb9144b0 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -144,14 +144,16 @@ module.exports = { // Takes an ObjectExpression node and adds all named Property nodes to the // current set of state fields. function addStateFields(node) { - node.properties.filter((prop) => ( - prop.type === 'Property' - && (prop.key.type === 'Literal' - || (prop.key.type === 'TemplateLiteral' && prop.key.expressions.length === 0) - || (prop.computed === false && prop.key.type === 'Identifier')) - && getName(prop.key) !== null - )).forEach((prop) => { - classInfo.stateFields.add(prop); + node.properties.forEach((prop) => { + if ( + prop.type === 'Property' + && (prop.key.type === 'Literal' + || (prop.key.type === 'TemplateLiteral' && prop.key.expressions.length === 0) + || (prop.computed === false && prop.key.type === 'Identifier')) + && getName(prop.key) !== null + ) { + classInfo.stateFields.add(prop); + } }); } diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index 312756d8f1..db5f7b073e 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -167,19 +167,19 @@ module.exports = { * @param {Object} component The component to process */ function reportUndeclaredPropTypes(component) { - const undeclareds = component.usedPropTypes.filter((propType) => ( - propType.node - && !isIgnored(propType.allNames[0]) - && !isDeclaredInComponent(component.node, propType.allNames) - )); - undeclareds.forEach((propType) => { - context.report({ - node: propType.node, - messageId: 'missingPropType', - data: { - name: propType.allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]') - } - }); + component.usedPropTypes.forEach((propType) => { + if (propType.node + && !isIgnored(propType.allNames[0]) + && !isDeclaredInComponent(component.node, propType.allNames) + ) { + context.report({ + node: propType.node, + messageId: 'missingPropType', + data: { + name: propType.allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]') + } + }); + } }); } @@ -191,8 +191,10 @@ module.exports = { 'Program:exit'() { const list = components.list(); // Report undeclared proptypes for all classes - values(list).filter((component) => mustBeValidated(component)).forEach((component) => { - reportUndeclaredPropTypes(component); + values(list).forEach((component) => { + if (mustBeValidated(component)) { + reportUndeclaredPropTypes(component); + } }); } }; diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js index aa9ee112fb..ff0f441e67 100644 --- a/lib/rules/require-default-props.js +++ b/lib/rules/require-default-props.js @@ -94,17 +94,17 @@ module.exports = { 'Program:exit'() { const list = components.list(); - values(list).filter((component) => { + values(list).forEach((component) => { if (ignoreFunctionalComponents && (astUtil.isFunction(component.node) || astUtil.isFunctionLikeExpression(component.node))) { - return false; + return; + } + if (component.declaredPropTypes) { + reportPropTypesWithoutDefault( + component.declaredPropTypes, + component.defaultProps || {} + ); } - return component.declaredPropTypes; - }).forEach((component) => { - reportPropTypesWithoutDefault( - component.declaredPropTypes, - component.defaultProps || {} - ); }); } }; diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js index 1445ef6101..7646255fd4 100644 --- a/lib/rules/require-optimization.js +++ b/lib/rules/require-optimization.js @@ -222,8 +222,10 @@ module.exports = { const list = components.list(); // Report missing shouldComponentUpdate for all components - values(list).filter((component) => !component.hasSCU).forEach((component) => { - reportMissingOptimization(component); + values(list).forEach((component) => { + if (!component.hasSCU) { + reportMissingOptimization(component); + } }); } }; diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index 1f70cfc196..ff7b751617 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -49,8 +49,9 @@ module.exports = { function findRenderMethod(node) { const properties = astUtil.getComponentProperties(node); return properties - .filter((property) => astUtil.getPropertyName(property) === 'render' && property.value) - .find((property) => astUtil.isFunctionLikeExpression(property.value)); + .find((property) => astUtil.getPropertyName(property) === 'render' + && property.value && astUtil.isFunctionLikeExpression(property.value) + ); } return { diff --git a/lib/rules/static-property-placement.js b/lib/rules/static-property-placement.js index 318230d2c6..f04d5e416d 100644 --- a/lib/rules/static-property-placement.js +++ b/lib/rules/static-property-placement.js @@ -168,7 +168,7 @@ module.exports = { MethodDefinition: (node) => { // If the function is inside a class and is static getter then check if correctly positioned - if (utils.getParentES6Component() && node.static && node.kind === 'get') { + if (node.static && node.kind === 'get' && utils.getParentES6Component()) { // Report error if needed reportNodeIncorrectlyPositioned(node, STATIC_GETTER); } diff --git a/lib/rules/void-dom-elements-no-children.js b/lib/rules/void-dom-elements-no-children.js index 634eac517b..d5c2cacbea 100644 --- a/lib/rules/void-dom-elements-no-children.js +++ b/lib/rules/void-dom-elements-no-children.js @@ -40,6 +40,22 @@ function isVoidDOMElement(elementName) { return has(VOID_DOM_ELEMENTS, elementName); } +function isChildrenOrDangerProp(prop) { + if (!prop.key) { + return false; + } + + return prop.key.name === 'children' || prop.key.name === 'dangerouslySetInnerHTML'; +} + +function isChildrenOrDangerAttribute(attribute) { + if (!attribute.name) { + return false; + } + + return attribute.name.name === 'children' || attribute.name.name === 'dangerouslySetInnerHTML'; +} + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -82,13 +98,7 @@ module.exports = { const attributes = node.openingElement.attributes; - const hasChildrenAttributeOrDanger = attributes.some((attribute) => { - if (!attribute.name) { - return false; - } - - return attribute.name.name === 'children' || attribute.name.name === 'dangerouslySetInnerHTML'; - }); + const hasChildrenAttributeOrDanger = attributes.some(isChildrenOrDangerAttribute); if (hasChildrenAttributeOrDanger) { // e.g.
@@ -143,13 +153,7 @@ module.exports = { const props = args[1].properties; - const hasChildrenPropOrDanger = props.some((prop) => { - if (!prop.key) { - return false; - } - - return prop.key.name === 'children' || prop.key.name === 'dangerouslySetInnerHTML'; - }); + const hasChildrenPropOrDanger = props.some(isChildrenOrDangerProp); if (hasChildrenPropOrDanger) { // e.g. React.createElement('br', { children: 'Foo' }) diff --git a/lib/util/annotations.js b/lib/util/annotations.js index d52fb214f1..ad8d7acdd1 100644 --- a/lib/util/annotations.js +++ b/lib/util/annotations.js @@ -19,12 +19,20 @@ function isAnnotatedFunctionPropsDeclaration(node, context) { const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0]; - const tokens = context.getFirstTokens(typeNode, 2); const isAnnotated = typeNode.typeAnnotation; const isDestructuredProps = typeNode.type === 'ObjectPattern'; - const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props'); - return (isAnnotated && (isDestructuredProps || isProps)); + if (isAnnotated) { + if (isDestructuredProps) { + return true; + } + + const tokens = context.getFirstTokens(typeNode, 2); + const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props'); + return isProps; + } + + return false; } module.exports = { diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index ff462ee729..44fd6d0d0c 100644 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -447,18 +447,20 @@ module.exports = function usedPropTypesInstructions(context, components, utils) } function handleCustomValidators(component) { - const propTypes = component.declaredPropTypes; - if (!propTypes) { - return; - } + if (mustBeValidated(component)) { + const propTypes = component.declaredPropTypes; + if (!propTypes) { + return; + } - values(propTypes).forEach((val) => { - const node = val.node; + values(propTypes).forEach((val) => { + const node = val.node; - if (node.value && astUtil.isFunctionLikeExpression(node.value)) { - markPropTypesAsUsed(node.value); - } - }); + if (node.value && astUtil.isFunctionLikeExpression(node.value)) { + markPropTypesAsUsed(node.value); + } + }); + } } return { @@ -555,7 +557,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) 'Program:exit'() { const list = components.list(); - values(list).filter((component) => mustBeValidated(component)).forEach(handleCustomValidators); + values(list).forEach(handleCustomValidators); } }; };