eslint-plugin-react
Advanced tools
Comparing version 7.30.1 to 7.30.2
@@ -175,5 +175,3 @@ /** | ||
function runCheck(proptypes, addInvalidProp) { | ||
proptypes = proptypes || []; | ||
proptypes.forEach((prop) => { | ||
(proptypes || []).forEach((prop) => { | ||
if (config.validateNested && nestedPropTypes(prop)) { | ||
@@ -180,0 +178,0 @@ runCheck(prop.value.arguments[0].properties, addInvalidProp); |
@@ -34,3 +34,3 @@ /** | ||
docs: { | ||
description: 'Forbid "button" element without an explicit "type" attribute', | ||
description: 'Disallow usage of `button` elements without an explicit `type` attribute', | ||
category: 'Possible Errors', | ||
@@ -37,0 +37,0 @@ recommended: false, |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'Enforce all defaultProps are defined and not "required" in propTypes.', | ||
description: 'Enforce all defaultProps have a corresponding non-required PropType', | ||
category: 'Best Practices', | ||
@@ -28,0 +28,0 @@ url: docsUrl('default-props-match-prop-types'), |
@@ -14,2 +14,3 @@ /** | ||
const docsUrl = require('../util/docsUrl'); | ||
const testReactVersion = require('../util/version').testReactVersion; | ||
const propsUtil = require('../util/props'); | ||
@@ -29,3 +30,3 @@ const report = require('../util/report'); | ||
docs: { | ||
description: 'Prevent missing displayName in a React component definition', | ||
description: 'Disallow missing displayName in a React component definition', | ||
category: 'Best Practices', | ||
@@ -64,2 +65,13 @@ recommended: true, | ||
/** | ||
* Checks if React.forwardRef is nested inside React.memo | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {Boolean} True if React.forwardRef is nested inside React.memo, false if not. | ||
*/ | ||
function isNestedMemo(node) { | ||
const argumentIsCallExpression = node.arguments && node.arguments[0] && node.arguments[0].type === 'CallExpression'; | ||
return node.type === 'CallExpression' && argumentIsCallExpression && utils.isPragmaComponentWrapper(node); | ||
} | ||
/** | ||
* Reports missing display name for a given component | ||
@@ -69,2 +81,9 @@ * @param {Object} component The component to process | ||
function reportMissingDisplayName(component) { | ||
if ( | ||
testReactVersion(context, '^0.14.10 || ^15.7.0 || >= 16.12.0') | ||
&& isNestedMemo(component.node) | ||
) { | ||
return; | ||
} | ||
report(context, messages.noDisplayName, 'noDisplayName', { | ||
@@ -71,0 +90,0 @@ node: component.node, |
@@ -28,3 +28,3 @@ /** | ||
docs: { | ||
description: 'Forbid certain props on components', | ||
description: 'Disallow certain props on components', | ||
category: 'Best Practices', | ||
@@ -31,0 +31,0 @@ recommended: false, |
@@ -21,2 +21,17 @@ /** | ||
/** | ||
* @param {Map<string, object>} forbidMap // { disallowList: null | string[], message: null | string } | ||
* @param {string} prop | ||
* @param {string} tagName | ||
* @returns {boolean} | ||
*/ | ||
function isForbidden(forbidMap, prop, tagName) { | ||
const options = forbidMap.get(prop); | ||
return options && ( | ||
typeof tagName === 'undefined' | ||
|| !options.disallowList | ||
|| options.disallowList.indexOf(tagName) !== -1 | ||
); | ||
} | ||
const messages = { | ||
@@ -29,3 +44,3 @@ propIsForbidden: 'Prop "{{prop}}" is forbidden on DOM Nodes', | ||
docs: { | ||
description: 'Forbid certain props on DOM Nodes', | ||
description: 'Disallow certain props on DOM Nodes', | ||
category: 'Best Practices', | ||
@@ -52,2 +67,9 @@ recommended: false, | ||
}, | ||
disallowedFor: { | ||
type: 'array', | ||
uniqueItems: true, | ||
items: { | ||
type: 'string', | ||
}, | ||
}, | ||
message: { | ||
@@ -71,12 +93,8 @@ type: 'string', | ||
const propName = typeof value === 'string' ? value : value.propName; | ||
const options = { | ||
return [propName, { | ||
disallowList: typeof value === 'string' ? null : (value.disallowedFor || null), | ||
message: typeof value === 'string' ? null : value.message, | ||
}; | ||
return [propName, options]; | ||
}]; | ||
})); | ||
function isForbidden(prop) { | ||
return forbid.has(prop); | ||
} | ||
return { | ||
@@ -92,3 +110,3 @@ JSXAttribute(node) { | ||
if (!isForbidden(prop)) { | ||
if (!isForbidden(forbid, prop, tag)) { | ||
return; | ||
@@ -95,0 +113,0 @@ } |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'Forbid certain elements', | ||
description: 'Disallow certain elements', | ||
category: 'Best Practices', | ||
@@ -28,0 +28,0 @@ recommended: false, |
@@ -19,3 +19,3 @@ /** | ||
docs: { | ||
description: 'Forbid using another component\'s propTypes', | ||
description: 'Disallow using another component\'s propTypes', | ||
category: 'Best Practices', | ||
@@ -22,0 +22,0 @@ recommended: false, |
@@ -31,3 +31,3 @@ /** | ||
docs: { | ||
description: 'Forbid certain propTypes', | ||
description: 'Disallow certain propTypes', | ||
category: 'Best Practices', | ||
@@ -64,3 +64,24 @@ recommended: false, | ||
const checkChildContextTypes = configuration.checkChildContextTypes || false; | ||
let propTypesPackageName = null; | ||
let reactPackageName = null; | ||
let isForeignPropTypesPackage = false; | ||
function isPropTypesPackage(node) { | ||
return ( | ||
node.type === 'Identifier' | ||
&& ( | ||
node.name === null | ||
|| node.name === propTypesPackageName | ||
|| !isForeignPropTypesPackage | ||
) | ||
) || ( | ||
node.type === 'MemberExpression' | ||
&& ( | ||
node.object.name === null | ||
|| node.object.name === reactPackageName | ||
|| !isForeignPropTypesPackage | ||
) | ||
); | ||
} | ||
function isForbidden(type) { | ||
@@ -118,2 +139,5 @@ const forbid = configuration.forbid || DEFAULTS; | ||
if (value.type === 'CallExpression') { | ||
if (!isPropTypesPackage(value.callee)) { | ||
return; | ||
} | ||
value.arguments.forEach((arg) => { | ||
@@ -125,2 +149,5 @@ const name = arg.type === 'MemberExpression' ? arg.property.name : arg.name; | ||
} | ||
if (!isPropTypesPackage(value)) { | ||
return; | ||
} | ||
if (value.property) { | ||
@@ -164,5 +191,31 @@ target = value.property.name; | ||
return { | ||
ImportDeclaration(node) { | ||
if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types" | ||
if (node.specifiers.length > 0) { | ||
propTypesPackageName = node.specifiers[0].local.name; | ||
} | ||
} else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react" | ||
if (node.specifiers.length > 0) { | ||
reactPackageName = node.specifiers[0].local.name; // guard against accidental anonymous `import "react"` | ||
} | ||
if (node.specifiers.length >= 1) { | ||
const propTypesSpecifier = node.specifiers.find((specifier) => ( | ||
specifier.imported && specifier.imported.name === 'PropTypes' | ||
)); | ||
if (propTypesSpecifier) { | ||
propTypesPackageName = propTypesSpecifier.local.name; | ||
} | ||
} | ||
} else { // package is not imported from "react" or "prop-types" | ||
// eslint-disable-next-line no-lonely-if | ||
if (node.specifiers.some((x) => x.local.name === 'PropTypes')) { // assert: node.specifiers.length > 1 | ||
isForeignPropTypesPackage = true; | ||
} | ||
} | ||
}, | ||
'ClassProperty, PropertyDefinition'(node) { | ||
if ( | ||
!propsUtil.isPropTypesDeclaration(node) | ||
&& !isPropTypesPackage(node) | ||
&& !shouldCheckContextTypes(node) | ||
@@ -179,2 +232,3 @@ && !shouldCheckChildContextTypes(node) | ||
!propsUtil.isPropTypesDeclaration(node) | ||
&& !isPropTypesPackage(node) | ||
&& !shouldCheckContextTypes(node) | ||
@@ -191,2 +245,10 @@ && !shouldCheckChildContextTypes(node) | ||
if ( | ||
node.callee.object | ||
&& !isPropTypesPackage(node.callee.object) | ||
&& !propsUtil.isPropTypesDeclaration(node.callee) | ||
) { | ||
return; | ||
} | ||
if ( | ||
node.arguments.length > 0 | ||
@@ -202,2 +264,3 @@ && (node.callee.name === 'shape' || astUtil.getPropertyName(node.callee) === 'shape') | ||
!propsUtil.isPropTypesDeclaration(node) | ||
&& !isPropTypesPackage(node) | ||
&& !shouldCheckContextTypes(node) | ||
@@ -224,2 +287,3 @@ && !shouldCheckChildContextTypes(node) | ||
!propsUtil.isPropTypesDeclaration(property) | ||
&& !isPropTypesPackage(property) | ||
&& !shouldCheckContextTypes(property) | ||
@@ -226,0 +290,0 @@ && !shouldCheckChildContextTypes(property) |
@@ -121,3 +121,3 @@ /** | ||
docs: { | ||
description: 'Standardize the way function component get defined', | ||
description: 'Enforce a specific function type for function components', | ||
category: 'Stylistic Issues', | ||
@@ -124,0 +124,0 @@ recommended: false, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Ensure symmetric naming of useState hook value and setter variables', | ||
description: 'Ensure destructuring and symmetric naming of useState hook value and setter variables', | ||
category: 'Best Practices', | ||
@@ -26,0 +26,0 @@ recommended: false, |
@@ -25,2 +25,6 @@ /** | ||
const errorData = new WeakMap(); | ||
/** | ||
* @param {object} exceptions | ||
* @returns {object} | ||
*/ | ||
function getErrorData(exceptions) { | ||
@@ -34,3 +38,8 @@ if (!errorData.has(exceptions)) { | ||
} | ||
/** | ||
* @param {string} configuration | ||
* @param {Set<string>} exceptions | ||
* @param {string} propName | ||
* @returns {boolean} propName | ||
*/ | ||
function isAlways(configuration, exceptions, propName) { | ||
@@ -43,3 +52,8 @@ const isException = exceptions.has(propName); | ||
} | ||
/** | ||
* @param {string} configuration | ||
* @param {Set<string>} exceptions | ||
* @param {string} propName | ||
* @returns {boolean} propName | ||
*/ | ||
function isNever(configuration, exceptions, propName) { | ||
@@ -115,3 +129,6 @@ const isException = exceptions.has(propName); | ||
if (isAlways(configuration, exceptions, propName) && value === null) { | ||
if ( | ||
isAlways(configuration, exceptions, propName) | ||
&& value === null | ||
) { | ||
const data = getErrorData(exceptions); | ||
@@ -127,3 +144,8 @@ const messageId = data.exceptionsMessage ? 'setBoolean' : 'setBoolean_noMessage'; | ||
} | ||
if (isNever(configuration, exceptions, propName) && value && value.type === 'JSXExpressionContainer' && value.expression.value === true) { | ||
if ( | ||
isNever(configuration, exceptions, propName) | ||
&& value | ||
&& value.type === 'JSXExpressionContainer' | ||
&& value.expression.value === true | ||
) { | ||
const data = getErrorData(exceptions); | ||
@@ -130,0 +152,0 @@ const messageId = data.exceptionsMessage ? 'omitBoolean' : 'omitBoolean_noMessage'; |
@@ -50,3 +50,3 @@ 'use strict'; | ||
docs: { | ||
description: 'Ensures inline tags are not rendered without spaces between them', | ||
description: 'Enforce or disallow spaces inside of curly braces in JSX attributes and expressions', | ||
category: 'Stylistic Issues', | ||
@@ -60,10 +60,3 @@ recommended: false, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: {}, | ||
default: {}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
schema: [], | ||
}, | ||
@@ -70,0 +63,0 @@ create(context) { |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Validate closing bracket location in JSX', | ||
description: 'Enforce closing bracket location in JSX', | ||
category: 'Stylistic Issues', | ||
@@ -169,3 +169,3 @@ recommended: false, | ||
function getIndentation(tokens, expectedLocation, correctColumn) { | ||
correctColumn = correctColumn || 0; | ||
const newColumn = correctColumn || 0; | ||
let indentation; | ||
@@ -184,3 +184,3 @@ let spaces = []; | ||
} | ||
if (indentation.length + 1 < correctColumn) { | ||
if (indentation.length + 1 < newColumn) { | ||
// Non-whitespace characters were included in the column offset | ||
@@ -187,0 +187,0 @@ spaces = new Array(+correctColumn + 1 - indentation.length); |
@@ -24,3 +24,3 @@ /** | ||
docs: { | ||
description: 'Validate closing tag location for multiline JSX', | ||
description: 'Enforce closing tag location for multiline JSX', | ||
category: 'Stylistic Issues', | ||
@@ -27,0 +27,0 @@ recommended: false, |
@@ -42,3 +42,3 @@ /** | ||
docs: { | ||
description: 'Disallow unnecessary JSX expressions when literals alone are sufficient or enfore JSX expressions on literals in JSX children or attributes', | ||
description: 'Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes', | ||
category: 'Stylistic Issues', | ||
@@ -142,4 +142,4 @@ recommended: false, | ||
const htmlEntities = text.match(HTML_ENTITY_REGEX()); | ||
return htmlEntities.reduce((acc, htmlEntitiy) => ( | ||
acc.replace(HTML_ENTITY, htmlEntitiy) | ||
return htmlEntities.reduce((acc, htmlEntity) => ( | ||
acc.replace(HTML_ENTITY, htmlEntity) | ||
), withCurlyBraces); | ||
@@ -249,4 +249,4 @@ } | ||
// Bail out if there is any character that needs to be escaped in JSX | ||
// because escaping decreases readiblity and the original code may be more | ||
// readible anyway or intentional for other specific reasons | ||
// because escaping decreases readability and the original code may be more | ||
// readable anyway or intentional for other specific reasons | ||
function lintUnnecessaryCurly(JSXExpressionNode) { | ||
@@ -253,0 +253,0 @@ const expression = JSXExpressionNode.expression; |
@@ -49,3 +49,3 @@ /** | ||
docs: { | ||
description: 'Enforce consistent line breaks inside jsx curly', | ||
description: 'Enforce consistent linebreaks in curly braces in JSX attributes and expressions', | ||
category: 'Stylistic Issues', | ||
@@ -52,0 +52,0 @@ recommended: false, |
@@ -40,3 +40,3 @@ /** | ||
docs: { | ||
description: 'Enforce or disallow spaces inside of curly braces in JSX attributes', | ||
description: 'Enforce or disallow spaces inside of curly braces in JSX attributes and expressions', | ||
category: 'Stylistic Issues', | ||
@@ -43,0 +43,0 @@ recommended: false, |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'Disallow or enforce spaces around equal signs in JSX attributes', | ||
description: 'Enforce or disallow spaces around equal signs in JSX attributes', | ||
category: 'Stylistic Issues', | ||
@@ -28,0 +28,0 @@ recommended: false, |
@@ -33,3 +33,3 @@ /** | ||
docs: { | ||
description: 'Restrict file extensions that may contain JSX', | ||
description: 'Disallow file extensions that may contain JSX', | ||
category: 'Stylistic Issues', | ||
@@ -36,0 +36,0 @@ recommended: false, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Ensure proper position of the first property in JSX', | ||
description: 'Enforce proper position of the first property in JSX', | ||
category: 'Stylistic Issues', | ||
@@ -26,0 +26,0 @@ recommended: false, |
@@ -48,3 +48,3 @@ /** | ||
docs: { | ||
description: 'Validate props indentation in JSX', | ||
description: 'Enforce props indentation in JSX', | ||
category: 'Stylistic Issues', | ||
@@ -176,6 +176,12 @@ recommended: false, | ||
function checkNodesIndent(nodes, indent) { | ||
let nestedIndent = indent; | ||
nodes.forEach((node) => { | ||
const nodeIndent = getNodeIndent(node); | ||
if (line.isUsingOperator && !line.currentOperator && indentSize !== 'first' && !ignoreTernaryOperator) { | ||
indent += indentSize; | ||
if ( | ||
line.isUsingOperator | ||
&& !line.currentOperator | ||
&& indentSize !== 'first' | ||
&& !ignoreTernaryOperator | ||
) { | ||
nestedIndent += indentSize; | ||
line.isUsingOperator = false; | ||
@@ -185,5 +191,5 @@ } | ||
node.type !== 'ArrayExpression' && node.type !== 'ObjectExpression' | ||
&& nodeIndent !== indent && astUtil.isNodeFirstInLine(context, node) | ||
&& nodeIndent !== nestedIndent && astUtil.isNodeFirstInLine(context, node) | ||
) { | ||
report(node, indent, nodeIndent); | ||
report(node, nestedIndent, nodeIndent); | ||
} | ||
@@ -190,0 +196,0 @@ }); |
@@ -51,3 +51,3 @@ /** | ||
docs: { | ||
description: 'Validate JSX indentation', | ||
description: 'Enforce JSX indentation', | ||
category: 'Stylistic Issues', | ||
@@ -172,5 +172,2 @@ recommended: false, | ||
function getNodeIndent(node, byLastLine, excludeCommas) { | ||
byLastLine = byLastLine || false; | ||
excludeCommas = excludeCommas || false; | ||
let src = context.getSourceCode().getText(node, node.loc.start.column + extraColumnStart); | ||
@@ -177,0 +174,0 @@ const lines = src.split('\n'); |
@@ -37,3 +37,3 @@ /** | ||
docs: { | ||
description: 'Report missing `key` props in iterators/collection literals', | ||
description: 'Disallow missing `key` props in iterators/collection literals', | ||
category: 'Possible Errors', | ||
@@ -90,4 +90,24 @@ recommended: true, | ||
function getReturnStatement(body) { | ||
return body.filter((item) => item.type === 'ReturnStatement')[0]; | ||
function getReturnStatements(node) { | ||
const returnStatements = arguments[1] || []; | ||
if (node.type === 'IfStatement') { | ||
if (node.consequent) { | ||
getReturnStatements(node.consequent, returnStatements); | ||
} | ||
if (node.alternate) { | ||
getReturnStatements(node.alternate, returnStatements); | ||
} | ||
} else if (Array.isArray(node.body)) { | ||
node.body.forEach((item) => { | ||
if (item.type === 'IfStatement') { | ||
getReturnStatements(item, returnStatements); | ||
} | ||
if (item.type === 'ReturnStatement') { | ||
returnStatements.push(item); | ||
} | ||
}); | ||
} | ||
return returnStatements; | ||
} | ||
@@ -194,6 +214,7 @@ | ||
if (fn.body.type === 'BlockStatement') { | ||
const returnStatement = getReturnStatement(fn.body.body); | ||
if (returnStatement && returnStatement.argument) { | ||
checkIteratorElement(returnStatement.argument); | ||
} | ||
getReturnStatements(fn.body) | ||
.filter((returnStatement) => returnStatement && returnStatement.argument) | ||
.forEach((returnStatement) => { | ||
checkIteratorElement(returnStatement.argument); | ||
}); | ||
} | ||
@@ -200,0 +221,0 @@ } |
@@ -26,3 +26,3 @@ /** | ||
docs: { | ||
description: 'Validate JSX maximum depth', | ||
description: 'Enforce JSX maximum depth', | ||
category: 'Stylistic Issues', | ||
@@ -29,0 +29,0 @@ recommended: false, |
@@ -29,3 +29,3 @@ /** | ||
docs: { | ||
description: 'Limit maximum of props on a single line in JSX', | ||
description: 'Enforce maximum of props on a single line in JSX', | ||
category: 'Stylistic Issues', | ||
@@ -32,0 +32,0 @@ recommended: false, |
@@ -19,4 +19,9 @@ /** | ||
prevent: 'JSX element should not start in a new line', | ||
allowMultilines: 'Multiline JSX elements should start in a new line', | ||
}; | ||
function isMultilined(node) { | ||
return node.loc.start.line !== node.loc.end.line; | ||
} | ||
module.exports = { | ||
@@ -41,4 +46,25 @@ meta: { | ||
}, | ||
allowMultilines: { | ||
default: false, | ||
type: 'boolean', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
if: { | ||
properties: { | ||
allowMultilines: { | ||
const: true, | ||
}, | ||
}, | ||
}, | ||
then: { | ||
properties: { | ||
prevent: { | ||
const: true, | ||
}, | ||
}, | ||
required: [ | ||
'prevent', | ||
], | ||
}, | ||
}, | ||
@@ -50,2 +76,3 @@ ], | ||
const sourceCode = context.getSourceCode(); | ||
return { | ||
@@ -56,2 +83,6 @@ 'Program:exit'() { | ||
if (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer') { | ||
const configuration = context.options[0] || {}; | ||
const prevent = configuration.prevent || false; | ||
const allowMultilines = configuration.allowMultilines || false; | ||
const firstAdjacentSibling = elements[index + 1]; | ||
@@ -69,6 +100,24 @@ const secondAdjacentSibling = elements[index + 2]; | ||
const prevent = !!(context.options[0] || {}).prevent; | ||
if (allowMultilines && (isMultilined(element) || isMultilined(secondAdjacentSibling))) { | ||
if (!isWithoutNewLine) return; | ||
const regex = /(\n)(?!.*\1)/g; | ||
const replacement = '\n\n'; | ||
const messageId = 'allowMultilines'; | ||
report(context, messages[messageId], messageId, { | ||
node: secondAdjacentSibling, | ||
fix(fixer) { | ||
return fixer.replaceText( | ||
firstAdjacentSibling, | ||
sourceCode.getText(firstAdjacentSibling) | ||
.replace(regex, replacement) | ||
); | ||
}, | ||
}); | ||
return; | ||
} | ||
if (isWithoutNewLine === prevent) return; | ||
const messageId = prevent | ||
@@ -75,0 +124,0 @@ ? 'prevent' |
@@ -29,3 +29,3 @@ /** | ||
docs: { | ||
description: 'Prevents usage of Function.prototype.bind and arrow functions in React component props', | ||
description: 'Disallow `.bind()` or arrow functions in JSX props', | ||
category: 'Best Practices', | ||
@@ -73,2 +73,5 @@ recommended: false, | ||
/** | ||
* @param {string | number} blockStart | ||
*/ | ||
function setBlockVariableNameSet(blockStart) { | ||
@@ -85,3 +88,2 @@ blockVariableNameSets[blockStart] = { | ||
const nodeType = node.type; | ||
if ( | ||
@@ -117,2 +119,7 @@ !configuration.allowBind | ||
/** | ||
* @param {string | number} violationType | ||
* @param {any} variableName | ||
* @param {string | number} blockStart | ||
*/ | ||
function addVariableNameToSet(violationType, variableName, blockStart) { | ||
@@ -119,0 +126,0 @@ blockVariableNameSets[blockStart][violationType].add(variableName); |
@@ -39,3 +39,3 @@ /** | ||
docs: { | ||
description: 'Comments inside children section of tag should be placed inside braces', | ||
description: 'Disallow comments from being inserted as text nodes', | ||
category: 'Possible Errors', | ||
@@ -48,7 +48,3 @@ recommended: true, | ||
schema: [{ | ||
type: 'object', | ||
properties: {}, | ||
additionalProperties: false, | ||
}], | ||
schema: [], | ||
}, | ||
@@ -55,0 +51,0 @@ |
@@ -134,3 +134,3 @@ /** | ||
docs: { | ||
description: 'Prevents JSX context provider values from taking values that will cause needless rerenders.', | ||
description: 'Disallows JSX context provider values from taking values that will cause needless rerenders', | ||
category: 'Best Practices', | ||
@@ -137,0 +137,0 @@ recommended: false, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Enforce no duplicate props', | ||
description: 'Disallow duplicate properties in JSX', | ||
category: 'Possible Errors', | ||
@@ -26,0 +26,0 @@ recommended: true, |
@@ -43,2 +43,11 @@ /** | ||
function extractExpressionBetweenLogicalAnds(node) { | ||
if (node.type !== 'LogicalExpression') return [node]; | ||
if (node.operator !== '&&') return [node]; | ||
return [].concat( | ||
extractExpressionBetweenLogicalAnds(node.left), | ||
extractExpressionBetweenLogicalAnds(node.right) | ||
); | ||
} | ||
function ruleFixer(context, fixStrategy, fixer, reportedNode, leftNode, rightNode) { | ||
@@ -49,10 +58,12 @@ const sourceCode = context.getSourceCode(); | ||
if (fixStrategy === COERCE_STRATEGY) { | ||
let leftSideText = sourceCode.getText(leftNode); | ||
if (isParenthesized(context, leftNode)) { | ||
leftSideText = `(${leftSideText})`; | ||
} | ||
const expressions = extractExpressionBetweenLogicalAnds(leftNode); | ||
const newText = expressions.map((node) => { | ||
let nodeText = sourceCode.getText(node); | ||
if (isParenthesized(context, node)) { | ||
nodeText = `(${nodeText})`; | ||
} | ||
return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!!'}${nodeText}`; | ||
}).join(' && '); | ||
const shouldPrefixDoubleNegation = leftNode.type !== 'UnaryExpression'; | ||
return fixer.replaceText(reportedNode, `${shouldPrefixDoubleNegation ? '!!' : ''}${leftSideText} && ${rightSideText}`); | ||
return fixer.replaceText(reportedNode, `${newText} && ${rightSideText}`); | ||
} | ||
@@ -77,3 +88,3 @@ | ||
docs: { | ||
description: 'Prevent problematic leaked values from being rendered', | ||
description: 'Disallow problematic leaked values from being rendered', | ||
category: 'Possible Errors', | ||
@@ -80,0 +91,0 @@ recommended: false, |
@@ -30,3 +30,3 @@ /** | ||
docs: { | ||
description: 'Prevent using string literals in React component definition', | ||
description: 'Disallow usage of string literals in JSX', | ||
category: 'Stylistic Issues', | ||
@@ -74,3 +74,4 @@ recommended: false, | ||
function defaultMessageId() { | ||
if (config.noAttributeStrings) { | ||
const ancestorIsJSXElement = arguments.length >= 1 && arguments[0]; | ||
if (config.noAttributeStrings && !ancestorIsJSXElement) { | ||
return 'noStringsInAttributes'; | ||
@@ -84,13 +85,2 @@ } | ||
function reportLiteralNode(node, messageId) { | ||
messageId = messageId || defaultMessageId(); | ||
report(context, messages[messageId], messageId, { | ||
node, | ||
data: { | ||
text: context.getSourceCode().getText(node).trim(), | ||
}, | ||
}); | ||
} | ||
function getParentIgnoringBinaryExpressions(node) { | ||
@@ -105,5 +95,7 @@ let current = node; | ||
function getValidation(node) { | ||
if (config.allowedStrings.has(trimIfString(node.value))) { | ||
const values = [trimIfString(node.raw), trimIfString(node.value)]; | ||
if (values.some((value) => config.allowedStrings.has(value))) { | ||
return false; | ||
} | ||
const parent = getParentIgnoringBinaryExpressions(node); | ||
@@ -114,3 +106,3 @@ | ||
if (config.noAttributeStrings) { | ||
return parent.type === 'JSXAttribute'; | ||
return parent.type === 'JSXAttribute' || parent.type === 'JSXElement'; | ||
} | ||
@@ -154,2 +146,14 @@ if (!config.noAttributeStrings) { | ||
function reportLiteralNode(node, messageId) { | ||
const ancestorIsJSXElement = hasJSXElementParentOrGrandParent(node); | ||
messageId = messageId || defaultMessageId(ancestorIsJSXElement); | ||
report(context, messages[messageId], messageId, { | ||
node, | ||
data: { | ||
text: context.getSourceCode().getText(node).trim(), | ||
}, | ||
}); | ||
} | ||
// -------------------------------------------------------------------------- | ||
@@ -156,0 +160,0 @@ // Public |
@@ -53,3 +53,3 @@ /** | ||
docs: { | ||
description: 'Forbid `javascript:` URLs', | ||
description: 'Disallow usage of `javascript:` URLs', | ||
category: 'Best Practices', | ||
@@ -56,0 +56,0 @@ recommended: false, |
@@ -77,3 +77,8 @@ /** | ||
} | ||
return value.expression && value.expression.value; | ||
const expr = value.expression; | ||
return expr && ( | ||
expr.type === 'ConditionalExpression' | ||
? [expr.consequent.value, expr.alternate.value] | ||
: expr.value | ||
); | ||
} | ||
@@ -92,8 +97,11 @@ } | ||
const value = getStringFromValue(relAttribute.value); | ||
const tags = value && typeof value === 'string' && value.toLowerCase().split(' '); | ||
const noreferrer = tags && tags.indexOf('noreferrer') >= 0; | ||
if (noreferrer) { | ||
return true; | ||
} | ||
return allowReferrer && tags && tags.indexOf('noopener') >= 0; | ||
return [].concat(value).every((item) => { | ||
const tags = typeof item === 'string' ? item.toLowerCase().split(' ') : false; | ||
const noreferrer = tags && tags.indexOf('noreferrer') >= 0; | ||
if (noreferrer) { | ||
return true; | ||
} | ||
const noopener = tags && tags.indexOf('noopener') >= 0; | ||
return allowReferrer && noopener; | ||
}); | ||
} | ||
@@ -110,3 +118,3 @@ | ||
docs: { | ||
description: 'Forbid `target="_blank"` attribute without `rel="noreferrer"`', | ||
description: 'Disallow `target="_blank"` attribute without `rel="noreferrer"`', | ||
category: 'Best Practices', | ||
@@ -113,0 +121,0 @@ recommended: true, |
@@ -27,3 +27,3 @@ /** | ||
docs: { | ||
description: 'Limit to one expression per line in JSX', | ||
description: 'Require one JSX element per line', | ||
category: 'Stylistic Issues', | ||
@@ -30,0 +30,0 @@ recommended: false, |
@@ -44,3 +44,3 @@ /** | ||
docs: { | ||
description: 'Prevent JSX prop spreading', | ||
description: 'Disallow JSX prop spreading', | ||
category: 'Best Practices', | ||
@@ -47,0 +47,0 @@ recommended: false, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Enforce default props alphabetical sorting', | ||
description: 'Enforce defaultProps declarations alphabetical sorting', | ||
category: 'Stylistic Issues', | ||
@@ -26,0 +26,0 @@ recommended: false, |
@@ -49,2 +49,10 @@ /** | ||
let attributeMap; | ||
// attributeMap = [endrange, true||false if comment in between nodes exists, it needs to be sorted to end] | ||
function shouldSortToEnd(node) { | ||
const attr = attributeMap.get(node); | ||
return !!attr && !!attr[1]; | ||
} | ||
function contextCompare(a, b, options) { | ||
@@ -54,2 +62,11 @@ let aProp = propName(a); | ||
const aSortToEnd = shouldSortToEnd(a); | ||
const bSortToEnd = shouldSortToEnd(b); | ||
if (aSortToEnd && !bSortToEnd) { | ||
return 1; | ||
} | ||
if (!aSortToEnd && bSortToEnd) { | ||
return -1; | ||
} | ||
if (options.reservedFirst) { | ||
@@ -123,9 +140,25 @@ const aIsReserved = isReservedPropName(aProp, options.reservedList); | ||
* @param {Array<JSXSpreadAttribute|JSXAttribute>} attributes | ||
* @param {Object} context The context of the rule | ||
* @return {Array<Array<JSXAttribute>>} | ||
*/ | ||
function getGroupsOfSortableAttributes(attributes) { | ||
function getGroupsOfSortableAttributes(attributes, context) { | ||
const sourceCode = context.getSourceCode(); | ||
const sortableAttributeGroups = []; | ||
let groupCount = 0; | ||
function addtoSortableAttributeGroups(attribute) { | ||
sortableAttributeGroups[groupCount - 1].push(attribute); | ||
} | ||
for (let i = 0; i < attributes.length; i++) { | ||
const attribute = attributes[i]; | ||
const nextAttribute = attributes[i + 1]; | ||
const attributeline = attribute.loc.start.line; | ||
let comment = []; | ||
try { | ||
comment = sourceCode.getCommentsAfter(attribute); | ||
} catch (e) { /**/ } | ||
const lastAttr = attributes[i - 1]; | ||
const attrIsSpread = attribute.type === 'JSXSpreadAttribute'; | ||
// If we have no groups or if the last attribute was JSXSpreadAttribute | ||
@@ -136,4 +169,3 @@ // then we start a new group. Append attributes to the group until we | ||
!lastAttr | ||
|| (lastAttr.type === 'JSXSpreadAttribute' | ||
&& attributes[i].type !== 'JSXSpreadAttribute') | ||
|| (lastAttr.type === 'JSXSpreadAttribute' && !attrIsSpread) | ||
) { | ||
@@ -143,4 +175,36 @@ groupCount += 1; | ||
} | ||
if (attributes[i].type !== 'JSXSpreadAttribute') { | ||
sortableAttributeGroups[groupCount - 1].push(attributes[i]); | ||
if (!attrIsSpread) { | ||
if (comment.length === 0) { | ||
attributeMap.set(attribute, [attribute.range[1], false]); | ||
addtoSortableAttributeGroups(attribute); | ||
} else { | ||
const firstComment = comment[0]; | ||
const commentline = firstComment.loc.start.line; | ||
if (comment.length === 1) { | ||
if (attributeline + 1 === commentline && nextAttribute) { | ||
attributeMap.set(attribute, [nextAttribute.range[1], true]); | ||
addtoSortableAttributeGroups(attribute); | ||
i += 1; | ||
} else if (attributeline === commentline) { | ||
if (firstComment.type === 'Block') { | ||
attributeMap.set(attribute, [nextAttribute.range[1], true]); | ||
i += 1; | ||
} else { | ||
attributeMap.set(attribute, [firstComment.range[1], false]); | ||
} | ||
addtoSortableAttributeGroups(attribute); | ||
} | ||
} else if (comment.length > 1 && attributeline + 1 === comment[1].loc.start.line && nextAttribute) { | ||
const commentNextAttribute = sourceCode.getCommentsAfter(nextAttribute); | ||
attributeMap.set(attribute, [nextAttribute.range[1], true]); | ||
if ( | ||
commentNextAttribute.length === 1 | ||
&& nextAttribute.loc.start.line === commentNextAttribute[0].loc.start.line | ||
) { | ||
attributeMap.set(attribute, [commentNextAttribute[0].range[1], true]); | ||
} | ||
addtoSortableAttributeGroups(attribute); | ||
i += 1; | ||
} | ||
} | ||
} | ||
@@ -151,3 +215,3 @@ } | ||
const generateFixerFunction = (node, context, reservedList) => { | ||
function generateFixerFunction(node, context, reservedList) { | ||
const sourceCode = context.getSourceCode(); | ||
@@ -179,3 +243,3 @@ const attributes = node.attributes.slice(0); | ||
}; | ||
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes); | ||
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context); | ||
const sortedAttributeGroups = sortableAttributeGroups | ||
@@ -189,9 +253,9 @@ .slice(0) | ||
// Replace each unsorted attribute with the sorted one. | ||
sortableAttributeGroups.forEach((sortableGroup, ii) => { | ||
sortableGroup.forEach((attr, jj) => { | ||
const sortedAttr = sortedAttributeGroups[ii][jj]; | ||
const sortedAttrText = sourceCode.getText(sortedAttr); | ||
const sortedAttrText = source.substring(sortedAttr.range[0], attributeMap.get(sortedAttr)[0]); | ||
const attrrangeEnd = attributeMap.get(attr)[0]; | ||
fixers.push({ | ||
range: [attr.range[0], attr.range[1]], | ||
range: [attr.range[0], attrrangeEnd], | ||
text: sortedAttrText, | ||
@@ -213,3 +277,3 @@ }); | ||
}; | ||
}; | ||
} | ||
@@ -343,11 +407,13 @@ /** | ||
const reservedFirstError = validateReservedFirstConfig(context, reservedFirst); | ||
let reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST; | ||
const reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST; | ||
const locale = configuration.locale || 'auto'; | ||
return { | ||
Program() { | ||
attributeMap = new WeakMap(); | ||
}, | ||
JSXOpeningElement(node) { | ||
// `dangerouslySetInnerHTML` is only "reserved" on DOM components | ||
if (reservedFirst && !jsxUtil.isDOMComponent(node)) { | ||
reservedList = reservedList.filter((prop) => prop !== 'dangerouslySetInnerHTML'); | ||
} | ||
const nodeReservedList = reservedFirst && !jsxUtil.isDOMComponent(node) ? reservedList.filter((prop) => prop !== 'dangerouslySetInnerHTML') : reservedList; | ||
@@ -365,4 +431,2 @@ node.attributes.reduce((memo, decl, idx, attrs) => { | ||
const currentIsCallback = isCallbackPropName(currentPropName); | ||
const previousIsMultiline = isMultilineProp(memo); | ||
const currentIsMultiline = isMultilineProp(decl); | ||
@@ -380,4 +444,4 @@ if (ignoreCase) { | ||
const previousIsReserved = isReservedPropName(previousPropName, reservedList); | ||
const currentIsReserved = isReservedPropName(currentPropName, reservedList); | ||
const previousIsReserved = isReservedPropName(previousPropName, nodeReservedList); | ||
const currentIsReserved = isReservedPropName(currentPropName, nodeReservedList); | ||
@@ -388,3 +452,3 @@ if (previousIsReserved && !currentIsReserved) { | ||
if (!previousIsReserved && currentIsReserved) { | ||
reportNodeAttribute(decl, 'listReservedPropsFirst', node, context, reservedList); | ||
reportNodeAttribute(decl, 'listReservedPropsFirst', node, context, nodeReservedList); | ||
@@ -402,3 +466,3 @@ return memo; | ||
// Encountered a non-callback prop after a callback prop | ||
reportNodeAttribute(memo, 'listCallbacksLast', node, context, reservedList); | ||
reportNodeAttribute(memo, 'listCallbacksLast', node, context, nodeReservedList); | ||
@@ -414,3 +478,3 @@ return memo; | ||
if (!currentValue && previousValue) { | ||
reportNodeAttribute(decl, 'listShorthandFirst', node, context, reservedList); | ||
reportNodeAttribute(decl, 'listShorthandFirst', node, context, nodeReservedList); | ||
@@ -426,3 +490,3 @@ return memo; | ||
if (currentValue && !previousValue) { | ||
reportNodeAttribute(memo, 'listShorthandLast', node, context, reservedList); | ||
reportNodeAttribute(memo, 'listShorthandLast', node, context, nodeReservedList); | ||
@@ -433,2 +497,4 @@ return memo; | ||
const previousIsMultiline = isMultilineProp(memo); | ||
const currentIsMultiline = isMultilineProp(decl); | ||
if (multiline === 'first') { | ||
@@ -441,9 +507,7 @@ if (previousIsMultiline && !currentIsMultiline) { | ||
// Encountered a non-multiline prop before a multiline prop | ||
reportNodeAttribute(decl, 'listMultilineFirst', node, context, reservedList); | ||
reportNodeAttribute(decl, 'listMultilineFirst', node, context, nodeReservedList); | ||
return memo; | ||
} | ||
} | ||
if (multiline === 'last') { | ||
} else if (multiline === 'last') { | ||
if (!previousIsMultiline && currentIsMultiline) { | ||
@@ -455,3 +519,3 @@ // Entering the multiline prop section | ||
// Encountered a non-multiline prop after a multiline prop | ||
reportNodeAttribute(memo, 'listMultilineLast', node, context, reservedList); | ||
reportNodeAttribute(memo, 'listMultilineLast', node, context, nodeReservedList); | ||
@@ -470,3 +534,3 @@ return memo; | ||
) { | ||
reportNodeAttribute(decl, 'sortPropsByAlpha', node, context, reservedList); | ||
reportNodeAttribute(decl, 'sortPropsByAlpha', node, context, nodeReservedList); | ||
@@ -473,0 +537,0 @@ return memo; |
@@ -29,3 +29,3 @@ /** | ||
docs: { | ||
description: 'Validate spacing before closing bracket in JSX', | ||
description: 'Enforce spacing before closing bracket in JSX', | ||
category: 'Stylistic Issues', | ||
@@ -32,0 +32,0 @@ recommended: false, |
@@ -261,3 +261,3 @@ /** | ||
docs: { | ||
description: 'Validate whitespace in and around the JSX opening and closing brackets', | ||
description: 'Enforce whitespace in and around the JSX opening and closing brackets', | ||
category: 'Stylistic Issues', | ||
@@ -264,0 +264,0 @@ recommended: false, |
@@ -16,5 +16,6 @@ /** | ||
module.exports = { | ||
// eslint-disable-next-line eslint-plugin/prefer-message-ids -- https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/292 | ||
meta: { | ||
docs: { | ||
description: 'Prevent React to be marked as unused', | ||
description: 'Disallow React to be incorrectly marked as unused', | ||
category: 'Best Practices', | ||
@@ -21,0 +22,0 @@ recommended: true, |
@@ -18,5 +18,6 @@ /** | ||
module.exports = { | ||
// eslint-disable-next-line eslint-plugin/prefer-message-ids -- https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/292 | ||
meta: { | ||
docs: { | ||
description: 'Prevent variables used in JSX to be marked as unused', | ||
description: 'Disallow variables used in JSX to be incorrectly marked as unused', | ||
category: 'Best Practices', | ||
@@ -23,0 +24,0 @@ recommended: true, |
@@ -40,3 +40,3 @@ /** | ||
docs: { | ||
description: 'Prevent missing parentheses around multilines JSX', | ||
description: 'Disallow missing parentheses around multiline JSX', | ||
category: 'Stylistic Issues', | ||
@@ -43,0 +43,0 @@ recommended: false, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Reports when this.state is accessed within setState', | ||
description: 'Disallow when this.state is accessed within setState', | ||
category: 'Possible Errors', | ||
@@ -134,3 +134,3 @@ recommended: false, | ||
// Storing all variables containg this.state | ||
// Storing all variables containing this.state | ||
if (current.type === 'VariableDeclarator') { | ||
@@ -137,0 +137,0 @@ vars.push({ |
@@ -82,3 +82,3 @@ /** | ||
docs: { | ||
description: 'Prevent adjacent inline elements not separated by whitespace.', | ||
description: 'Disallow adjacent inline elements not separated by whitespace.', | ||
category: 'Best Practices', | ||
@@ -85,0 +85,0 @@ recommended: false, |
@@ -47,3 +47,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of Array index in keys', | ||
description: 'Disallow usage of Array index in keys', | ||
category: 'Best Practices', | ||
@@ -50,0 +50,0 @@ recommended: false, |
@@ -43,3 +43,3 @@ /** | ||
docs: { | ||
description: 'Prevent passing of children as props.', | ||
description: 'Disallow passing of children as props', | ||
category: 'Best Practices', | ||
@@ -46,0 +46,0 @@ recommended: true, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Report when a DOM element is using both children and dangerouslySetInnerHTML', | ||
description: 'Disallow when a DOM element is using both children and dangerouslySetInnerHTML', | ||
category: 'Possible Errors', | ||
@@ -26,0 +26,0 @@ recommended: true, |
@@ -32,3 +32,3 @@ /** | ||
* @param {String} name - Name of the attribute to check. | ||
* @returns {boolean} Whether or not the attribute is dnagerous. | ||
* @returns {boolean} Whether or not the attribute is dangerous. | ||
*/ | ||
@@ -50,3 +50,3 @@ function isDangerous(name) { | ||
docs: { | ||
description: 'Prevent usage of dangerous JSX props', | ||
description: 'Disallow usage of dangerous JSX properties', | ||
category: 'Best Practices', | ||
@@ -53,0 +53,0 @@ recommended: false, |
@@ -95,3 +95,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of deprecated methods', | ||
description: 'Disallow usage of deprecated methods', | ||
category: 'Best Practices', | ||
@@ -98,0 +98,0 @@ recommended: true, |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'Prevent direct mutation of this.state', | ||
description: 'Disallow direct mutation of this.state', | ||
category: 'Possible Errors', | ||
@@ -60,3 +60,3 @@ recommended: true, | ||
/** | ||
* Walks throughs the MemberExpression to the top-most property. | ||
* Walks through the MemberExpression to the top-most property. | ||
* @param {Object} node The node to process | ||
@@ -63,0 +63,0 @@ * @returns {Object} The outer-most MemberExpression |
@@ -22,3 +22,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of findDOMNode', | ||
description: 'Disallow usage of findDOMNode', | ||
category: 'Best Practices', | ||
@@ -25,0 +25,0 @@ recommended: true, |
@@ -536,3 +536,3 @@ /** | ||
docs: { | ||
description: 'Forbid attribute with an invalid values`', | ||
description: 'Disallow usage of invalid attributes', | ||
category: 'Possible Errors', | ||
@@ -539,0 +539,0 @@ url: docsUrl('no-invalid-html-attribute'), |
@@ -22,3 +22,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of isMounted', | ||
description: 'Disallow usage of isMounted', | ||
category: 'Best Practices', | ||
@@ -25,0 +25,0 @@ recommended: true, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Prevent multiple component definition per file', | ||
description: 'Disallow multiple component definition per file', | ||
category: 'Stylistic Issues', | ||
@@ -26,0 +26,0 @@ recommended: false, |
@@ -32,6 +32,3 @@ /** | ||
schema: [{ | ||
type: 'object', | ||
additionalProperties: false, | ||
}], | ||
schema: [], | ||
}, | ||
@@ -38,0 +35,0 @@ |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Flag shouldComponentUpdate when extending PureComponent', | ||
description: 'Disallow usage of shouldComponentUpdate when extending React.PureComponent', | ||
category: 'Possible Errors', | ||
@@ -26,0 +26,0 @@ recommended: false, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of the return value of React.render', | ||
description: 'Disallow usage of the return value of ReactDOM.render', | ||
category: 'Best Practices', | ||
@@ -26,0 +26,0 @@ recommended: true, |
@@ -23,3 +23,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of setState', | ||
description: 'Disallow usage of setState', | ||
category: 'Stylistic Issues', | ||
@@ -26,0 +26,0 @@ recommended: false, |
@@ -24,3 +24,3 @@ /** | ||
docs: { | ||
description: 'Prevent string definitions for references and prevent referencing this.refs', | ||
description: 'Disallow using string references', | ||
category: 'Best Practices', | ||
@@ -27,0 +27,0 @@ recommended: true, |
@@ -22,3 +22,3 @@ /** | ||
docs: { | ||
description: 'Report "this" being used in stateless components', | ||
description: 'Disallow `this` from being used in stateless functional components', | ||
category: 'Possible Errors', | ||
@@ -25,0 +25,0 @@ recommended: false, |
@@ -34,3 +34,3 @@ /** | ||
docs: { | ||
description: 'Prevent common typos', | ||
description: 'Disallow common typos', | ||
category: 'Stylistic Issues', | ||
@@ -37,0 +37,0 @@ recommended: false, |
@@ -41,3 +41,3 @@ /** | ||
docs: { | ||
description: 'Detect unescaped HTML entities, which might represent malformed tags', | ||
description: 'Disallow unescaped HTML entities from appearing in markup', | ||
category: 'Possible Errors', | ||
@@ -85,3 +85,3 @@ recommended: true, | ||
// HTML entites are already escaped in node.value (as well as node.raw), | ||
// HTML entities are already escaped in node.value (as well as node.raw), | ||
// so pull the raw text from context.getSourceCode() | ||
@@ -88,0 +88,0 @@ for (let i = node.loc.start.line; i <= node.loc.end.line; i++) { |
@@ -224,3 +224,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of unknown DOM property', | ||
description: 'Disallow usage of unknown DOM property', | ||
category: 'Possible Errors', | ||
@@ -227,0 +227,0 @@ recommended: true, |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'Prevent usage of unsafe lifecycle methods', | ||
description: 'Disallow usage of unsafe lifecycle methods', | ||
category: 'Best Practices', | ||
@@ -28,0 +28,0 @@ recommended: false, |
@@ -271,3 +271,3 @@ /** | ||
docs: { | ||
description: 'Prevent creating unstable components inside components', | ||
description: 'Disallow creating unstable components inside components', | ||
category: 'Possible Errors', | ||
@@ -436,3 +436,3 @@ recommended: false, | ||
// Do not mark components declared inside hooks (or falsly '() => null' clean-up methods) | ||
// Do not mark components declared inside hooks (or falsy '() => null' clean-up methods) | ||
|| isReturnStatementOfHook(node, context) | ||
@@ -446,3 +446,3 @@ | ||
// Prevent falsely reporting deteceted "components" which do not return JSX | ||
// Prevent falsely reporting detected "components" which do not return JSX | ||
|| isStatelessComponentReturningNull(node) | ||
@@ -449,0 +449,0 @@ ) { |
@@ -104,3 +104,3 @@ /** | ||
docs: { | ||
description: 'Prevent declaring unused methods of component class', | ||
description: 'Disallow declaring unused methods of component class', | ||
category: 'Best Practices', | ||
@@ -111,8 +111,3 @@ recommended: false, | ||
messages, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
additionalProperties: false, | ||
}, | ||
], | ||
schema: [], | ||
}, | ||
@@ -119,0 +114,0 @@ |
@@ -26,3 +26,3 @@ /** | ||
docs: { | ||
description: 'Prevent definitions of unused prop types', | ||
description: 'Disallow definitions of unused propTypes', | ||
category: 'Best Practices', | ||
@@ -29,0 +29,0 @@ recommended: false, |
@@ -83,3 +83,3 @@ /** | ||
docs: { | ||
description: 'Prevent definition of unused state fields', | ||
description: 'Disallow definitions of unused state', | ||
category: 'Best Practices', | ||
@@ -86,0 +86,0 @@ recommended: false, |
@@ -38,3 +38,3 @@ /** | ||
docs: { | ||
description: 'Require read-only props.', | ||
description: 'Enforce that props are read-only', | ||
category: 'Stylistic Issues', | ||
@@ -41,0 +41,0 @@ recommended: false, |
@@ -171,3 +171,3 @@ /** | ||
* @param {Array} ctorParams - The params to check against super call. | ||
* @returns {boolean} true if the construtor body is redundant | ||
* @returns {boolean} true if the constructor body is redundant | ||
*/ | ||
@@ -174,0 +174,0 @@ function isRedundantSuperCall(body, ctorParams) { |
@@ -26,3 +26,3 @@ /** | ||
docs: { | ||
description: 'Prevent missing props validation in a React component definition', | ||
description: 'Disallow missing props validation in a React component definition', | ||
category: 'Best Practices', | ||
@@ -29,0 +29,0 @@ recommended: true, |
@@ -24,3 +24,3 @@ /** | ||
docs: { | ||
description: 'Prevent missing React when using JSX', | ||
description: 'Disallow missing React when using JSX', | ||
category: 'Possible Errors', | ||
@@ -27,0 +27,0 @@ recommended: true, |
@@ -30,3 +30,3 @@ /** | ||
docs: { | ||
description: 'Enforce a defaultProps definition for every prop that is not a required prop.', | ||
description: 'Enforce a defaultProps definition for every prop that is not a required prop', | ||
category: 'Best Practices', | ||
@@ -129,2 +129,6 @@ url: docsUrl('require-default-props'), | ||
if (!props) { | ||
return; | ||
} | ||
if (props.type === 'Identifier') { | ||
@@ -131,0 +135,0 @@ const hasOptionalProp = values(propTypes).some((propType) => !propType.isRequired); |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'Prevent extra closing tags for components without children', | ||
description: 'Disallow extra closing tags for components without children', | ||
category: 'Stylistic Issues', | ||
@@ -28,0 +28,0 @@ recommended: false, |
@@ -25,3 +25,3 @@ /** | ||
docs: { | ||
description: 'State initialization in an ES6 class component should be in a constructor', | ||
description: 'Enforce class component state initialization style', | ||
category: 'Stylistic Issues', | ||
@@ -28,0 +28,0 @@ recommended: false, |
@@ -61,3 +61,3 @@ /** | ||
docs: { | ||
description: 'Defines where React component static properties should be positioned.', | ||
description: 'Enforces where React component static properties should be positioned.', | ||
category: 'Stylistic Issues', | ||
@@ -64,0 +64,0 @@ recommended: false, |
@@ -53,3 +53,3 @@ /** | ||
docs: { | ||
description: 'Prevent passing of children to void DOM elements (e.g. `<br />`).', | ||
description: 'Disallow void DOM elements (e.g. `<img />`, `<br />`) from receiving children', | ||
category: 'Best Practices', | ||
@@ -56,0 +56,0 @@ recommended: false, |
@@ -47,3 +47,3 @@ /** | ||
/** | ||
* Find a return statment in the current node | ||
* Find a return statement in the current node | ||
* | ||
@@ -50,0 +50,0 @@ * @param {ASTNode} node The AST node being checked |
@@ -389,3 +389,3 @@ /** | ||
/** | ||
* It will check wheater memo/forwardRef is wrapping existing component or | ||
* It will check whether memo/forwardRef is wrapping existing component or | ||
* creating a new one. | ||
@@ -507,3 +507,2 @@ * @param {object} node | ||
&& !utils.isReturningJSX(node) | ||
&& !utils.isReturningOnlyNull(node) | ||
) { | ||
@@ -513,2 +512,52 @@ return undefined; | ||
// case: any = () => { return => null } | ||
// case: any = () => null | ||
if (node.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { | ||
if (isFirstLetterCapitalized(node.parent.left.name)) { | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
// case: any = () => () => null | ||
if (node.parent.type === 'ArrowFunctionExpression' && node.parent.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { | ||
if (isFirstLetterCapitalized(node.parent.parent.left.name)) { | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
// case: { any: () => () => null } | ||
if (node.parent.type === 'ArrowFunctionExpression' && node.parent.parent.type === 'Property' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { | ||
if (isFirstLetterCapitalized(node.parent.parent.key.name)) { | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
// case: any = function() {return function() {return null;};} | ||
if (node.parent.type === 'ReturnStatement') { | ||
if (isFirstLetterCapitalized(node.id && node.id.name)) { | ||
return node; | ||
} | ||
const functionExpr = node.parent.parent.parent; | ||
if (functionExpr.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { | ||
if (isFirstLetterCapitalized(functionExpr.parent.left.name)) { | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
} | ||
// case: { any: function() {return function() {return null;};} } | ||
if (node.parent.type === 'ReturnStatement') { | ||
const functionExpr = node.parent.parent.parent; | ||
if (functionExpr.parent.type === 'Property' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { | ||
if (isFirstLetterCapitalized(functionExpr.parent.key.name)) { | ||
return node; | ||
} | ||
return undefined; | ||
} | ||
} | ||
// for case abc = { [someobject.somekey]: props => { ... return not-jsx } } | ||
@@ -515,0 +564,0 @@ if (node.parent && node.parent.key && node.parent.key.type === 'MemberExpression' && !utils.isReturningJSX(node) && !utils.isReturningOnlyNull(node)) { |
@@ -9,3 +9,3 @@ 'use strict'; | ||
function isFirstLetterCapitalized(word) { | ||
if (!word) { | ||
if (!word || word.charAt(0) === '_') { | ||
return false; | ||
@@ -12,0 +12,0 @@ } |
@@ -48,3 +48,3 @@ /** | ||
docs: { | ||
description: `Prevent usage of setState in ${methodName}`, | ||
description: `Disallow usage of setState in ${methodName}`, | ||
category: 'Best Practices', | ||
@@ -51,0 +51,0 @@ recommended: false, |
@@ -69,3 +69,3 @@ /** | ||
* @param {Boolean=} requiredFirst whether or not to sort required elements first. | ||
* @param {Boolean=} callbacksLast whether or not to sort callbacks after everyting else. | ||
* @param {Boolean=} callbacksLast whether or not to sort callbacks after everything else. | ||
* @returns {Number} the sort order of the two elements. | ||
@@ -116,3 +116,3 @@ */ | ||
* @param {Boolean=} requiredFirst whether or not to sort required elements first. | ||
* @param {Boolean=} callbacksLast whether or not to sort callbacks after everyting else. | ||
* @param {Boolean=} callbacksLast whether or not to sort callbacks after everything else. | ||
* @param {Boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape. | ||
@@ -119,0 +119,0 @@ * @returns {Object|*|{range, text}} the sort order of the two elements. |
{ | ||
"name": "eslint-plugin-react", | ||
"version": "7.30.1", | ||
"version": "7.30.2", | ||
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>", | ||
@@ -9,2 +9,4 @@ "description": "React specific linting rules for ESLint", | ||
"prepack": "npmignore --auto --commentLines=autogenerated", | ||
"prelint": "npm run lint:docs", | ||
"lint:docs": "markdownlint \"**/*.md\"", | ||
"lint": "eslint .", | ||
@@ -43,11 +45,12 @@ "postlint": "npm run type-check", | ||
"devDependencies": { | ||
"@babel/core": "^7.18.5", | ||
"@babel/eslint-parser": "^7.18.2", | ||
"@babel/plugin-syntax-decorators": "^7.17.12", | ||
"@babel/plugin-syntax-do-expressions": "^7.16.7", | ||
"@babel/plugin-syntax-function-bind": "^7.16.7", | ||
"@babel/preset-react": "^7.17.12", | ||
"@babel/core": "^7.18.13", | ||
"@babel/eslint-parser": "^7.18.9", | ||
"@babel/plugin-syntax-decorators": "^7.18.6", | ||
"@babel/plugin-syntax-do-expressions": "^7.18.6", | ||
"@babel/plugin-syntax-function-bind": "^7.18.6", | ||
"@babel/preset-react": "^7.18.6", | ||
"@technote-space/doctoc": "~2.4", | ||
"@types/eslint": "=7.2.10", | ||
"@types/estree": "0.0.51", | ||
"@types/node": "^16.11.35", | ||
"@types/estree": "0.0.52", | ||
"@types/node": "^4.9.5", | ||
"@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4.0.0 || ^5.0.0", | ||
@@ -58,5 +61,5 @@ "aud": "^2.0.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1", | ||
"eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1 || ^5.0.5", | ||
"eslint-plugin-import": "^2.26.0", | ||
"eslint-remote-tester": "^2.1.4", | ||
"eslint-remote-tester": "^3.0.0", | ||
"eslint-remote-tester-repositories": "^0.0.6", | ||
@@ -68,2 +71,3 @@ "eslint-scope": "^3.7.3", | ||
"markdown-magic": "^2.6.0", | ||
"markdownlint-cli": "^0.8.0 || ^0.32.2", | ||
"mocha": "^5.2.0", | ||
@@ -70,0 +74,0 @@ "npmignore": "^0.3.0", |
251
README.md
@@ -1,12 +0,16 @@ | ||
`eslint-plugin-react` | ||
# `eslint-plugin-react` <sup>[![Version Badge][npm-version-svg]][package-url]</sup> | ||
=================== | ||
[![Maintenance Status][status-image]][status-url] [![NPM version][npm-image]][npm-url] [![Dependency Status][deps-image]][deps-url] [![Code Climate][climate-image]][climate-url] [![Tidelift][tidelift-image]][tidelift-url] | ||
[![github actions][actions-image]][actions-url] | ||
[![Maintenance Status][status-image]][status-url] | ||
[![NPM version][npm-image]][npm-url] | ||
[![Tidelift][tidelift-image]][tidelift-url] | ||
React specific linting rules for `eslint` | ||
# Installation | ||
## Installation | ||
```sh | ||
$ npm install eslint eslint-plugin-react --save-dev | ||
npm install eslint eslint-plugin-react --save-dev | ||
``` | ||
@@ -16,5 +20,4 @@ | ||
# Configuration | ||
## Configuration | ||
Use [our preset](#recommended) to get reasonable defaults: | ||
@@ -110,118 +113,119 @@ | ||
# List of supported rules | ||
## List of supported rules | ||
✔: Enabled in the [`recommended`](#recommended) configuration.\ | ||
🔧: Fixable with [`eslint --fix`](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems). | ||
🔧: Fixable with [`eslint --fix`](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems).\ | ||
💡: Provides editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). | ||
<!-- AUTO-GENERATED-CONTENT:START (BASIC_RULES) --> | ||
| ✔ | 🔧 | Rule | Description | | ||
| :---: | :---: | :--- | :--- | | ||
| | | [react/boolean-prop-naming](docs/rules/boolean-prop-naming.md) | Enforces consistent naming for boolean props | | ||
| | | [react/button-has-type](docs/rules/button-has-type.md) | Forbid "button" element without an explicit "type" attribute | | ||
| | | [react/default-props-match-prop-types](docs/rules/default-props-match-prop-types.md) | Enforce all defaultProps are defined and not "required" in propTypes. | | ||
| | 🔧 | [react/destructuring-assignment](docs/rules/destructuring-assignment.md) | Enforce consistent usage of destructuring assignment of props, state, and context | | ||
| ✔ | | [react/display-name](docs/rules/display-name.md) | Prevent missing displayName in a React component definition | | ||
| | | [react/forbid-component-props](docs/rules/forbid-component-props.md) | Forbid certain props on components | | ||
| | | [react/forbid-dom-props](docs/rules/forbid-dom-props.md) | Forbid certain props on DOM Nodes | | ||
| | | [react/forbid-elements](docs/rules/forbid-elements.md) | Forbid certain elements | | ||
| | | [react/forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md) | Forbid using another component's propTypes | | ||
| | | [react/forbid-prop-types](docs/rules/forbid-prop-types.md) | Forbid certain propTypes | | ||
| | 🔧 | [react/function-component-definition](docs/rules/function-component-definition.md) | Standardize the way function component get defined | | ||
| | | [react/hook-use-state](docs/rules/hook-use-state.md) | Ensure symmetric naming of useState hook value and setter variables | | ||
| | | [react/iframe-missing-sandbox](docs/rules/iframe-missing-sandbox.md) | Enforce sandbox attribute on iframe elements | | ||
| | | [react/no-access-state-in-setstate](docs/rules/no-access-state-in-setstate.md) | Reports when this.state is accessed within setState | | ||
| | | [react/no-adjacent-inline-elements](docs/rules/no-adjacent-inline-elements.md) | Prevent adjacent inline elements not separated by whitespace. | | ||
| | | [react/no-array-index-key](docs/rules/no-array-index-key.md) | Prevent usage of Array index in keys | | ||
| | 🔧 | [react/no-arrow-function-lifecycle](docs/rules/no-arrow-function-lifecycle.md) | Lifecycle methods should be methods on the prototype, not class fields | | ||
| ✔ | | [react/no-children-prop](docs/rules/no-children-prop.md) | Prevent passing of children as props. | | ||
| | | [react/no-danger](docs/rules/no-danger.md) | Prevent usage of dangerous JSX props | | ||
| ✔ | | [react/no-danger-with-children](docs/rules/no-danger-with-children.md) | Report when a DOM element is using both children and dangerouslySetInnerHTML | | ||
| ✔ | | [react/no-deprecated](docs/rules/no-deprecated.md) | Prevent usage of deprecated methods | | ||
| | | [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md) | Prevent usage of setState in componentDidMount | | ||
| | | [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md) | Prevent usage of setState in componentDidUpdate | | ||
| ✔ | | [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md) | Prevent direct mutation of this.state | | ||
| ✔ | | [react/no-find-dom-node](docs/rules/no-find-dom-node.md) | Prevent usage of findDOMNode | | ||
| | 🔧 | [react/no-invalid-html-attribute](docs/rules/no-invalid-html-attribute.md) | Forbid attribute with an invalid values` | | ||
| ✔ | | [react/no-is-mounted](docs/rules/no-is-mounted.md) | Prevent usage of isMounted | | ||
| | | [react/no-multi-comp](docs/rules/no-multi-comp.md) | Prevent multiple component definition per file | | ||
| | | [react/no-namespace](docs/rules/no-namespace.md) | Enforce that namespaces are not used in React elements | | ||
| | | [react/no-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md) | Flag shouldComponentUpdate when extending PureComponent | | ||
| ✔ | | [react/no-render-return-value](docs/rules/no-render-return-value.md) | Prevent usage of the return value of React.render | | ||
| | | [react/no-set-state](docs/rules/no-set-state.md) | Prevent usage of setState | | ||
| ✔ | | [react/no-string-refs](docs/rules/no-string-refs.md) | Prevent string definitions for references and prevent referencing this.refs | | ||
| | | [react/no-this-in-sfc](docs/rules/no-this-in-sfc.md) | Report "this" being used in stateless components | | ||
| | | [react/no-typos](docs/rules/no-typos.md) | Prevent common typos | | ||
| ✔ | | [react/no-unescaped-entities](docs/rules/no-unescaped-entities.md) | Detect unescaped HTML entities, which might represent malformed tags | | ||
| ✔ | 🔧 | [react/no-unknown-property](docs/rules/no-unknown-property.md) | Prevent usage of unknown DOM property | | ||
| | | [react/no-unsafe](docs/rules/no-unsafe.md) | Prevent usage of unsafe lifecycle methods | | ||
| | | [react/no-unstable-nested-components](docs/rules/no-unstable-nested-components.md) | Prevent creating unstable components inside components | | ||
| | | [react/no-unused-class-component-methods](docs/rules/no-unused-class-component-methods.md) | Prevent declaring unused methods of component class | | ||
| | | [react/no-unused-prop-types](docs/rules/no-unused-prop-types.md) | Prevent definitions of unused prop types | | ||
| | | [react/no-unused-state](docs/rules/no-unused-state.md) | Prevent definition of unused state fields | | ||
| | | [react/no-will-update-set-state](docs/rules/no-will-update-set-state.md) | Prevent usage of setState in componentWillUpdate | | ||
| | | [react/prefer-es6-class](docs/rules/prefer-es6-class.md) | Enforce ES5 or ES6 class for React Components | | ||
| | | [react/prefer-exact-props](docs/rules/prefer-exact-props.md) | Prefer exact proptype definitions | | ||
| | 🔧 | [react/prefer-read-only-props](docs/rules/prefer-read-only-props.md) | Require read-only props. | | ||
| | | [react/prefer-stateless-function](docs/rules/prefer-stateless-function.md) | Enforce stateless components to be written as a pure function | | ||
| ✔ | | [react/prop-types](docs/rules/prop-types.md) | Prevent missing props validation in a React component definition | | ||
| ✔ | | [react/react-in-jsx-scope](docs/rules/react-in-jsx-scope.md) | Prevent missing React when using JSX | | ||
| | | [react/require-default-props](docs/rules/require-default-props.md) | Enforce a defaultProps definition for every prop that is not a required prop. | | ||
| | | [react/require-optimization](docs/rules/require-optimization.md) | Enforce React components to have a shouldComponentUpdate method | | ||
| ✔ | | [react/require-render-return](docs/rules/require-render-return.md) | Enforce ES5 or ES6 class for returning value in render function | | ||
| | 🔧 | [react/self-closing-comp](docs/rules/self-closing-comp.md) | Prevent extra closing tags for components without children | | ||
| | | [react/sort-comp](docs/rules/sort-comp.md) | Enforce component methods order | | ||
| | | [react/sort-prop-types](docs/rules/sort-prop-types.md) | Enforce propTypes declarations alphabetical sorting | | ||
| | | [react/state-in-constructor](docs/rules/state-in-constructor.md) | State initialization in an ES6 class component should be in a constructor | | ||
| | | [react/static-property-placement](docs/rules/static-property-placement.md) | Defines where React component static properties should be positioned. | | ||
| | | [react/style-prop-object](docs/rules/style-prop-object.md) | Enforce style prop value is an object | | ||
| | | [react/void-dom-elements-no-children](docs/rules/void-dom-elements-no-children.md) | Prevent passing of children to void DOM elements (e.g. `<br />`). | | ||
| ✔ | 🔧 | 💡 | Rule | Description | | ||
| :---: | :---: | :---: | :--- | :--- | | ||
| | | | [react/boolean-prop-naming](docs/rules/boolean-prop-naming.md) | Enforces consistent naming for boolean props | | ||
| | | | [react/button-has-type](docs/rules/button-has-type.md) | Disallow usage of `button` elements without an explicit `type` attribute | | ||
| | | | [react/default-props-match-prop-types](docs/rules/default-props-match-prop-types.md) | Enforce all defaultProps have a corresponding non-required PropType | | ||
| | 🔧 | | [react/destructuring-assignment](docs/rules/destructuring-assignment.md) | Enforce consistent usage of destructuring assignment of props, state, and context | | ||
| ✔ | | | [react/display-name](docs/rules/display-name.md) | Disallow missing displayName in a React component definition | | ||
| | | | [react/forbid-component-props](docs/rules/forbid-component-props.md) | Disallow certain props on components | | ||
| | | | [react/forbid-dom-props](docs/rules/forbid-dom-props.md) | Disallow certain props on DOM Nodes | | ||
| | | | [react/forbid-elements](docs/rules/forbid-elements.md) | Disallow certain elements | | ||
| | | | [react/forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md) | Disallow using another component's propTypes | | ||
| | | | [react/forbid-prop-types](docs/rules/forbid-prop-types.md) | Disallow certain propTypes | | ||
| | 🔧 | | [react/function-component-definition](docs/rules/function-component-definition.md) | Enforce a specific function type for function components | | ||
| | | 💡 | [react/hook-use-state](docs/rules/hook-use-state.md) | Ensure destructuring and symmetric naming of useState hook value and setter variables | | ||
| | | | [react/iframe-missing-sandbox](docs/rules/iframe-missing-sandbox.md) | Enforce sandbox attribute on iframe elements | | ||
| | | | [react/no-access-state-in-setstate](docs/rules/no-access-state-in-setstate.md) | Disallow when this.state is accessed within setState | | ||
| | | | [react/no-adjacent-inline-elements](docs/rules/no-adjacent-inline-elements.md) | Disallow adjacent inline elements not separated by whitespace. | | ||
| | | | [react/no-array-index-key](docs/rules/no-array-index-key.md) | Disallow usage of Array index in keys | | ||
| | 🔧 | | [react/no-arrow-function-lifecycle](docs/rules/no-arrow-function-lifecycle.md) | Lifecycle methods should be methods on the prototype, not class fields | | ||
| ✔ | | | [react/no-children-prop](docs/rules/no-children-prop.md) | Disallow passing of children as props | | ||
| | | | [react/no-danger](docs/rules/no-danger.md) | Disallow usage of dangerous JSX properties | | ||
| ✔ | | | [react/no-danger-with-children](docs/rules/no-danger-with-children.md) | Disallow when a DOM element is using both children and dangerouslySetInnerHTML | | ||
| ✔ | | | [react/no-deprecated](docs/rules/no-deprecated.md) | Disallow usage of deprecated methods | | ||
| | | | [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md) | Disallow usage of setState in componentDidMount | | ||
| | | | [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md) | Disallow usage of setState in componentDidUpdate | | ||
| ✔ | | | [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md) | Disallow direct mutation of this.state | | ||
| ✔ | | | [react/no-find-dom-node](docs/rules/no-find-dom-node.md) | Disallow usage of findDOMNode | | ||
| | 🔧 | | [react/no-invalid-html-attribute](docs/rules/no-invalid-html-attribute.md) | Disallow usage of invalid attributes | | ||
| ✔ | | | [react/no-is-mounted](docs/rules/no-is-mounted.md) | Disallow usage of isMounted | | ||
| | | | [react/no-multi-comp](docs/rules/no-multi-comp.md) | Disallow multiple component definition per file | | ||
| | | | [react/no-namespace](docs/rules/no-namespace.md) | Enforce that namespaces are not used in React elements | | ||
| | | | [react/no-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md) | Disallow usage of shouldComponentUpdate when extending React.PureComponent | | ||
| ✔ | | | [react/no-render-return-value](docs/rules/no-render-return-value.md) | Disallow usage of the return value of ReactDOM.render | | ||
| | | | [react/no-set-state](docs/rules/no-set-state.md) | Disallow usage of setState | | ||
| ✔ | | | [react/no-string-refs](docs/rules/no-string-refs.md) | Disallow using string references | | ||
| | | | [react/no-this-in-sfc](docs/rules/no-this-in-sfc.md) | Disallow `this` from being used in stateless functional components | | ||
| | | | [react/no-typos](docs/rules/no-typos.md) | Disallow common typos | | ||
| ✔ | | | [react/no-unescaped-entities](docs/rules/no-unescaped-entities.md) | Disallow unescaped HTML entities from appearing in markup | | ||
| ✔ | 🔧 | | [react/no-unknown-property](docs/rules/no-unknown-property.md) | Disallow usage of unknown DOM property | | ||
| | | | [react/no-unsafe](docs/rules/no-unsafe.md) | Disallow usage of unsafe lifecycle methods | | ||
| | | | [react/no-unstable-nested-components](docs/rules/no-unstable-nested-components.md) | Disallow creating unstable components inside components | | ||
| | | | [react/no-unused-class-component-methods](docs/rules/no-unused-class-component-methods.md) | Disallow declaring unused methods of component class | | ||
| | | | [react/no-unused-prop-types](docs/rules/no-unused-prop-types.md) | Disallow definitions of unused propTypes | | ||
| | | | [react/no-unused-state](docs/rules/no-unused-state.md) | Disallow definitions of unused state | | ||
| | | | [react/no-will-update-set-state](docs/rules/no-will-update-set-state.md) | Disallow usage of setState in componentWillUpdate | | ||
| | | | [react/prefer-es6-class](docs/rules/prefer-es6-class.md) | Enforce ES5 or ES6 class for React Components | | ||
| | | | [react/prefer-exact-props](docs/rules/prefer-exact-props.md) | Prefer exact proptype definitions | | ||
| | 🔧 | | [react/prefer-read-only-props](docs/rules/prefer-read-only-props.md) | Enforce that props are read-only | | ||
| | | | [react/prefer-stateless-function](docs/rules/prefer-stateless-function.md) | Enforce stateless components to be written as a pure function | | ||
| ✔ | | | [react/prop-types](docs/rules/prop-types.md) | Disallow missing props validation in a React component definition | | ||
| ✔ | | | [react/react-in-jsx-scope](docs/rules/react-in-jsx-scope.md) | Disallow missing React when using JSX | | ||
| | | | [react/require-default-props](docs/rules/require-default-props.md) | Enforce a defaultProps definition for every prop that is not a required prop | | ||
| | | | [react/require-optimization](docs/rules/require-optimization.md) | Enforce React components to have a shouldComponentUpdate method | | ||
| ✔ | | | [react/require-render-return](docs/rules/require-render-return.md) | Enforce ES5 or ES6 class for returning value in render function | | ||
| | 🔧 | | [react/self-closing-comp](docs/rules/self-closing-comp.md) | Disallow extra closing tags for components without children | | ||
| | | | [react/sort-comp](docs/rules/sort-comp.md) | Enforce component methods order | | ||
| | | | [react/sort-prop-types](docs/rules/sort-prop-types.md) | Enforce propTypes declarations alphabetical sorting | | ||
| | | | [react/state-in-constructor](docs/rules/state-in-constructor.md) | Enforce class component state initialization style | | ||
| | | | [react/static-property-placement](docs/rules/static-property-placement.md) | Enforces where React component static properties should be positioned. | | ||
| | | | [react/style-prop-object](docs/rules/style-prop-object.md) | Enforce style prop value is an object | | ||
| | | | [react/void-dom-elements-no-children](docs/rules/void-dom-elements-no-children.md) | Disallow void DOM elements (e.g. `<img />`, `<br />`) from receiving children | | ||
<!-- AUTO-GENERATED-CONTENT:END --> | ||
## JSX-specific rules | ||
### JSX-specific rules | ||
<!-- AUTO-GENERATED-CONTENT:START (JSX_RULES) --> | ||
| ✔ | 🔧 | Rule | Description | | ||
| :---: | :---: | :--- | :--- | | ||
| | 🔧 | [react/jsx-boolean-value](docs/rules/jsx-boolean-value.md) | Enforce boolean attributes notation in JSX | | ||
| | | [react/jsx-child-element-spacing](docs/rules/jsx-child-element-spacing.md) | Ensures inline tags are not rendered without spaces between them | | ||
| | 🔧 | [react/jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md) | Validate closing bracket location in JSX | | ||
| | 🔧 | [react/jsx-closing-tag-location](docs/rules/jsx-closing-tag-location.md) | Validate closing tag location for multiline JSX | | ||
| | 🔧 | [react/jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md) | Disallow unnecessary JSX expressions when literals alone are sufficient or enfore JSX expressions on literals in JSX children or attributes | | ||
| | 🔧 | [react/jsx-curly-newline](docs/rules/jsx-curly-newline.md) | Enforce consistent line breaks inside jsx curly | | ||
| | 🔧 | [react/jsx-curly-spacing](docs/rules/jsx-curly-spacing.md) | Enforce or disallow spaces inside of curly braces in JSX attributes | | ||
| | 🔧 | [react/jsx-equals-spacing](docs/rules/jsx-equals-spacing.md) | Disallow or enforce spaces around equal signs in JSX attributes | | ||
| | | [react/jsx-filename-extension](docs/rules/jsx-filename-extension.md) | Restrict file extensions that may contain JSX | | ||
| | 🔧 | [react/jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md) | Ensure proper position of the first property in JSX | | ||
| | 🔧 | [react/jsx-fragments](docs/rules/jsx-fragments.md) | Enforce shorthand or standard form for React fragments | | ||
| | | [react/jsx-handler-names](docs/rules/jsx-handler-names.md) | Enforce event handler naming conventions in JSX | | ||
| | 🔧 | [react/jsx-indent](docs/rules/jsx-indent.md) | Validate JSX indentation | | ||
| | 🔧 | [react/jsx-indent-props](docs/rules/jsx-indent-props.md) | Validate props indentation in JSX | | ||
| ✔ | | [react/jsx-key](docs/rules/jsx-key.md) | Report missing `key` props in iterators/collection literals | | ||
| | | [react/jsx-max-depth](docs/rules/jsx-max-depth.md) | Validate JSX maximum depth | | ||
| | 🔧 | [react/jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md) | Limit maximum of props on a single line in JSX | | ||
| | 🔧 | [react/jsx-newline](docs/rules/jsx-newline.md) | Require or prevent a new line after jsx elements and expressions. | | ||
| | | [react/jsx-no-bind](docs/rules/jsx-no-bind.md) | Prevents usage of Function.prototype.bind and arrow functions in React component props | | ||
| ✔ | | [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md) | Comments inside children section of tag should be placed inside braces | | ||
| | | [react/jsx-no-constructed-context-values](docs/rules/jsx-no-constructed-context-values.md) | Prevents JSX context provider values from taking values that will cause needless rerenders. | | ||
| ✔ | | [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md) | Enforce no duplicate props | | ||
| | 🔧 | [react/jsx-no-leaked-render](docs/rules/jsx-no-leaked-render.md) | Prevent problematic leaked values from being rendered | | ||
| | | [react/jsx-no-literals](docs/rules/jsx-no-literals.md) | Prevent using string literals in React component definition | | ||
| | | [react/jsx-no-script-url](docs/rules/jsx-no-script-url.md) | Forbid `javascript:` URLs | | ||
| ✔ | 🔧 | [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md) | Forbid `target="_blank"` attribute without `rel="noreferrer"` | | ||
| ✔ | | [react/jsx-no-undef](docs/rules/jsx-no-undef.md) | Disallow undeclared variables in JSX | | ||
| | 🔧 | [react/jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md) | Disallow unnecessary fragments | | ||
| | 🔧 | [react/jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md) | Limit to one expression per line in JSX | | ||
| | | [react/jsx-pascal-case](docs/rules/jsx-pascal-case.md) | Enforce PascalCase for user-defined JSX components | | ||
| | 🔧 | [react/jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md) | Disallow multiple spaces between inline JSX props | | ||
| | | [react/jsx-props-no-spreading](docs/rules/jsx-props-no-spreading.md) | Prevent JSX prop spreading | | ||
| | | [react/jsx-sort-default-props](docs/rules/jsx-sort-default-props.md) | Enforce default props alphabetical sorting | | ||
| | 🔧 | [react/jsx-sort-props](docs/rules/jsx-sort-props.md) | Enforce props alphabetical sorting | | ||
| | 🔧 | [react/jsx-space-before-closing](docs/rules/jsx-space-before-closing.md) | Validate spacing before closing bracket in JSX | | ||
| | 🔧 | [react/jsx-tag-spacing](docs/rules/jsx-tag-spacing.md) | Validate whitespace in and around the JSX opening and closing brackets | | ||
| ✔ | | [react/jsx-uses-react](docs/rules/jsx-uses-react.md) | Prevent React to be marked as unused | | ||
| ✔ | | [react/jsx-uses-vars](docs/rules/jsx-uses-vars.md) | Prevent variables used in JSX to be marked as unused | | ||
| | 🔧 | [react/jsx-wrap-multilines](docs/rules/jsx-wrap-multilines.md) | Prevent missing parentheses around multilines JSX | | ||
| ✔ | 🔧 | 💡 | Rule | Description | | ||
| :---: | :---: | :---: | :--- | :--- | | ||
| | 🔧 | | [react/jsx-boolean-value](docs/rules/jsx-boolean-value.md) | Enforce boolean attributes notation in JSX | | ||
| | | | [react/jsx-child-element-spacing](docs/rules/jsx-child-element-spacing.md) | Enforce or disallow spaces inside of curly braces in JSX attributes and expressions | | ||
| | 🔧 | | [react/jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md) | Enforce closing bracket location in JSX | | ||
| | 🔧 | | [react/jsx-closing-tag-location](docs/rules/jsx-closing-tag-location.md) | Enforce closing tag location for multiline JSX | | ||
| | 🔧 | | [react/jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md) | Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes | | ||
| | 🔧 | | [react/jsx-curly-newline](docs/rules/jsx-curly-newline.md) | Enforce consistent linebreaks in curly braces in JSX attributes and expressions | | ||
| | 🔧 | | [react/jsx-curly-spacing](docs/rules/jsx-curly-spacing.md) | Enforce or disallow spaces inside of curly braces in JSX attributes and expressions | | ||
| | 🔧 | | [react/jsx-equals-spacing](docs/rules/jsx-equals-spacing.md) | Enforce or disallow spaces around equal signs in JSX attributes | | ||
| | | | [react/jsx-filename-extension](docs/rules/jsx-filename-extension.md) | Disallow file extensions that may contain JSX | | ||
| | 🔧 | | [react/jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md) | Enforce proper position of the first property in JSX | | ||
| | 🔧 | | [react/jsx-fragments](docs/rules/jsx-fragments.md) | Enforce shorthand or standard form for React fragments | | ||
| | | | [react/jsx-handler-names](docs/rules/jsx-handler-names.md) | Enforce event handler naming conventions in JSX | | ||
| | 🔧 | | [react/jsx-indent](docs/rules/jsx-indent.md) | Enforce JSX indentation | | ||
| | 🔧 | | [react/jsx-indent-props](docs/rules/jsx-indent-props.md) | Enforce props indentation in JSX | | ||
| ✔ | | | [react/jsx-key](docs/rules/jsx-key.md) | Disallow missing `key` props in iterators/collection literals | | ||
| | | | [react/jsx-max-depth](docs/rules/jsx-max-depth.md) | Enforce JSX maximum depth | | ||
| | 🔧 | | [react/jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md) | Enforce maximum of props on a single line in JSX | | ||
| | 🔧 | | [react/jsx-newline](docs/rules/jsx-newline.md) | Require or prevent a new line after jsx elements and expressions. | | ||
| | | | [react/jsx-no-bind](docs/rules/jsx-no-bind.md) | Disallow `.bind()` or arrow functions in JSX props | | ||
| ✔ | | | [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md) | Disallow comments from being inserted as text nodes | | ||
| | | | [react/jsx-no-constructed-context-values](docs/rules/jsx-no-constructed-context-values.md) | Disallows JSX context provider values from taking values that will cause needless rerenders | | ||
| ✔ | | | [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md) | Disallow duplicate properties in JSX | | ||
| | 🔧 | | [react/jsx-no-leaked-render](docs/rules/jsx-no-leaked-render.md) | Disallow problematic leaked values from being rendered | | ||
| | | | [react/jsx-no-literals](docs/rules/jsx-no-literals.md) | Disallow usage of string literals in JSX | | ||
| | | | [react/jsx-no-script-url](docs/rules/jsx-no-script-url.md) | Disallow usage of `javascript:` URLs | | ||
| ✔ | 🔧 | | [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md) | Disallow `target="_blank"` attribute without `rel="noreferrer"` | | ||
| ✔ | | | [react/jsx-no-undef](docs/rules/jsx-no-undef.md) | Disallow undeclared variables in JSX | | ||
| | 🔧 | | [react/jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md) | Disallow unnecessary fragments | | ||
| | 🔧 | | [react/jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md) | Require one JSX element per line | | ||
| | | | [react/jsx-pascal-case](docs/rules/jsx-pascal-case.md) | Enforce PascalCase for user-defined JSX components | | ||
| | 🔧 | | [react/jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md) | Disallow multiple spaces between inline JSX props | | ||
| | | | [react/jsx-props-no-spreading](docs/rules/jsx-props-no-spreading.md) | Disallow JSX prop spreading | | ||
| | | | [react/jsx-sort-default-props](docs/rules/jsx-sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting | | ||
| | 🔧 | | [react/jsx-sort-props](docs/rules/jsx-sort-props.md) | Enforce props alphabetical sorting | | ||
| | 🔧 | | [react/jsx-space-before-closing](docs/rules/jsx-space-before-closing.md) | Enforce spacing before closing bracket in JSX. ❌ This rule is deprecated. | | ||
| | 🔧 | | [react/jsx-tag-spacing](docs/rules/jsx-tag-spacing.md) | Enforce whitespace in and around the JSX opening and closing brackets | | ||
| ✔ | | | [react/jsx-uses-react](docs/rules/jsx-uses-react.md) | Disallow React to be incorrectly marked as unused | | ||
| ✔ | | | [react/jsx-uses-vars](docs/rules/jsx-uses-vars.md) | Disallow variables used in JSX to be incorrectly marked as unused | | ||
| | 🔧 | | [react/jsx-wrap-multilines](docs/rules/jsx-wrap-multilines.md) | Disallow missing parentheses around multiline JSX | | ||
<!-- AUTO-GENERATED-CONTENT:END --> | ||
## Other useful plugins | ||
### Other useful plugins | ||
@@ -232,5 +236,5 @@ - Rules of Hooks: [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks) | ||
# Shareable configurations | ||
## Shareable configurations | ||
## Recommended | ||
### Recommended | ||
@@ -249,3 +253,3 @@ This plugin exports a `recommended` configuration that enforces React good practices. | ||
## All | ||
### All | ||
@@ -266,16 +270,9 @@ This plugin also exports an `all` configuration that includes every available rule. | ||
# License | ||
## License | ||
`eslint-plugin-react` is licensed under the [MIT License](https://opensource.org/licenses/mit-license.php). | ||
[npm-url]: https://npmjs.org/package/eslint-plugin-react | ||
[npm-image]: https://img.shields.io/npm/v/eslint-plugin-react.svg | ||
[deps-url]: https://david-dm.org/jsx-eslint/eslint-plugin-react | ||
[deps-image]: https://img.shields.io/david/dev/jsx-eslint/eslint-plugin-react.svg | ||
[climate-url]: https://codeclimate.com/github/jsx-eslint/eslint-plugin-react | ||
[climate-image]: https://img.shields.io/codeclimate/maintainability/jsx-eslint/eslint-plugin-react.svg | ||
[status-url]: https://github.com/jsx-eslint/eslint-plugin-react/pulse | ||
@@ -285,2 +282,8 @@ [status-image]: https://img.shields.io/github/last-commit/jsx-eslint/eslint-plugin-react.svg | ||
[tidelift-url]: https://tidelift.com/subscription/pkg/npm-eslint-plugin-react?utm_source=npm-eslint-plugin-react&utm_medium=referral&utm_campaign=readme | ||
[tidelift-image]: https://tidelift.com/badges/github/jsx-eslint/eslint-plugin-react?style=flat | ||
[tidelift-image]: https://tidelift.com/badges/package/npm/eslint-plugin-react?style=flat | ||
[package-url]: https://npmjs.org/package/eslint-plugin-react | ||
[npm-version-svg]: https://versionbadg.es/jsx-eslint/eslint-plugin-react.svg | ||
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/jsx-eslint/eslint-plugin-react | ||
[actions-url]: https://github.com/jsx-eslint/eslint-plugin-react/actions |
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
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
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
733297
130
20404
284
30
1