Socket
Socket
Sign inDemoInstall

eslint-plugin-react

Package Overview
Dependencies
15
Maintainers
2
Versions
204
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 7.31.10 to 7.32.2

configs/all.js

198

index.js
'use strict';
const fromEntries = require('object.fromentries');
const entries = require('object.entries');
const configAll = require('./configs/all');
const configRecommended = require('./configs/recommended');
const configRuntime = require('./configs/jsx-runtime');
/* eslint-disable global-require */
const allRules = {
'boolean-prop-naming': require('./lib/rules/boolean-prop-naming'),
'button-has-type': require('./lib/rules/button-has-type'),
'default-props-match-prop-types': require('./lib/rules/default-props-match-prop-types'),
'destructuring-assignment': require('./lib/rules/destructuring-assignment'),
'display-name': require('./lib/rules/display-name'),
'forbid-component-props': require('./lib/rules/forbid-component-props'),
'forbid-dom-props': require('./lib/rules/forbid-dom-props'),
'forbid-elements': require('./lib/rules/forbid-elements'),
'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
'function-component-definition': require('./lib/rules/function-component-definition'),
'hook-use-state': require('./lib/rules/hook-use-state'),
'iframe-missing-sandbox': require('./lib/rules/iframe-missing-sandbox'),
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
'jsx-child-element-spacing': require('./lib/rules/jsx-child-element-spacing'),
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
'jsx-closing-tag-location': require('./lib/rules/jsx-closing-tag-location'),
'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
'jsx-curly-newline': require('./lib/rules/jsx-curly-newline'),
'jsx-equals-spacing': require('./lib/rules/jsx-equals-spacing'),
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
'jsx-first-prop-new-line': require('./lib/rules/jsx-first-prop-new-line'),
'jsx-handler-names': require('./lib/rules/jsx-handler-names'),
'jsx-indent': require('./lib/rules/jsx-indent'),
'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
'jsx-key': require('./lib/rules/jsx-key'),
'jsx-max-depth': require('./lib/rules/jsx-max-depth'),
'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line'),
'jsx-newline': require('./lib/rules/jsx-newline'),
'jsx-no-bind': require('./lib/rules/jsx-no-bind'),
'jsx-no-comment-textnodes': require('./lib/rules/jsx-no-comment-textnodes'),
'jsx-no-constructed-context-values': require('./lib/rules/jsx-no-constructed-context-values'),
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
'jsx-no-leaked-render': require('./lib/rules/jsx-no-leaked-render'),
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
'jsx-no-script-url': require('./lib/rules/jsx-no-script-url'),
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
'jsx-no-useless-fragment': require('./lib/rules/jsx-no-useless-fragment'),
'jsx-one-expression-per-line': require('./lib/rules/jsx-one-expression-per-line'),
'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
'jsx-curly-brace-presence': require('./lib/rules/jsx-curly-brace-presence'),
'jsx-pascal-case': require('./lib/rules/jsx-pascal-case'),
'jsx-fragments': require('./lib/rules/jsx-fragments'),
'jsx-props-no-multi-spaces': require('./lib/rules/jsx-props-no-multi-spaces'),
'jsx-props-no-spreading': require('./lib/rules/jsx-props-no-spreading'),
'jsx-sort-default-props': require('./lib/rules/jsx-sort-default-props'),
'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
'jsx-tag-spacing': require('./lib/rules/jsx-tag-spacing'),
'jsx-uses-react': require('./lib/rules/jsx-uses-react'),
'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
'jsx-wrap-multilines': require('./lib/rules/jsx-wrap-multilines'),
'no-invalid-html-attribute': require('./lib/rules/no-invalid-html-attribute'),
'no-access-state-in-setstate': require('./lib/rules/no-access-state-in-setstate'),
'no-adjacent-inline-elements': require('./lib/rules/no-adjacent-inline-elements'),
'no-array-index-key': require('./lib/rules/no-array-index-key'),
'no-arrow-function-lifecycle': require('./lib/rules/no-arrow-function-lifecycle'),
'no-children-prop': require('./lib/rules/no-children-prop'),
'no-danger': require('./lib/rules/no-danger'),
'no-danger-with-children': require('./lib/rules/no-danger-with-children'),
'no-deprecated': require('./lib/rules/no-deprecated'),
'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
'no-find-dom-node': require('./lib/rules/no-find-dom-node'),
'no-is-mounted': require('./lib/rules/no-is-mounted'),
'no-multi-comp': require('./lib/rules/no-multi-comp'),
'no-namespace': require('./lib/rules/no-namespace'),
'no-set-state': require('./lib/rules/no-set-state'),
'no-string-refs': require('./lib/rules/no-string-refs'),
'no-redundant-should-component-update': require('./lib/rules/no-redundant-should-component-update'),
'no-render-return-value': require('./lib/rules/no-render-return-value'),
'no-this-in-sfc': require('./lib/rules/no-this-in-sfc'),
'no-typos': require('./lib/rules/no-typos'),
'no-unescaped-entities': require('./lib/rules/no-unescaped-entities'),
'no-unknown-property': require('./lib/rules/no-unknown-property'),
'no-unsafe': require('./lib/rules/no-unsafe'),
'no-unstable-nested-components': require('./lib/rules/no-unstable-nested-components'),
'no-unused-class-component-methods': require('./lib/rules/no-unused-class-component-methods'),
'no-unused-prop-types': require('./lib/rules/no-unused-prop-types'),
'no-unused-state': require('./lib/rules/no-unused-state'),
'no-will-update-set-state': require('./lib/rules/no-will-update-set-state'),
'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
'prefer-exact-props': require('./lib/rules/prefer-exact-props'),
'prefer-read-only-props': require('./lib/rules/prefer-read-only-props'),
'prefer-stateless-function': require('./lib/rules/prefer-stateless-function'),
'prop-types': require('./lib/rules/prop-types'),
'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
'require-default-props': require('./lib/rules/require-default-props'),
'require-optimization': require('./lib/rules/require-optimization'),
'require-render-return': require('./lib/rules/require-render-return'),
'self-closing-comp': require('./lib/rules/self-closing-comp'),
'sort-comp': require('./lib/rules/sort-comp'),
'sort-prop-types': require('./lib/rules/sort-prop-types'),
'state-in-constructor': require('./lib/rules/state-in-constructor'),
'static-property-placement': require('./lib/rules/static-property-placement'),
'style-prop-object': require('./lib/rules/style-prop-object'),
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children'),
};
/* eslint-enable */
const allRules = require('./lib/rules');
function filterRules(rules, predicate) {
return fromEntries(entries(rules).filter((entry) => predicate(entry[1])));
}
// for legacy config system
const plugins = [
'react',
];
function configureAsError(rules) {
return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2]));
}
const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated);
const activeRulesConfig = configureAsError(activeRules);
const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated);
module.exports = {
deprecatedRules,
deprecatedRules: configAll.plugins.react.deprecatedRules,
rules: allRules,
configs: {
recommended: {
plugins: [
'react',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
rules: {
'react/display-name': 2,
'react/jsx-key': 2,
'react/jsx-no-comment-textnodes': 2,
'react/jsx-no-duplicate-props': 2,
'react/jsx-no-target-blank': 2,
'react/jsx-no-undef': 2,
'react/jsx-uses-react': 2,
'react/jsx-uses-vars': 2,
'react/no-children-prop': 2,
'react/no-danger-with-children': 2,
'react/no-deprecated': 2,
'react/no-direct-mutation-state': 2,
'react/no-find-dom-node': 2,
'react/no-is-mounted': 2,
'react/no-render-return-value': 2,
'react/no-string-refs': 2,
'react/no-unescaped-entities': 2,
'react/no-unknown-property': 2,
'react/no-unsafe': 0,
'react/prop-types': 2,
'react/react-in-jsx-scope': 2,
'react/require-render-return': 2,
},
},
all: {
plugins: [
'react',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
rules: activeRulesConfig,
},
'jsx-runtime': {
plugins: [
'react',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
jsxPragma: null, // for @typescript/eslint-parser
},
rules: {
'react/react-in-jsx-scope': 0,
'react/jsx-uses-react': 0,
},
},
recommended: Object.assign({}, configRecommended, {
parserOptions: configRecommended.languageOptions.parserOptions,
plugins,
}),
all: Object.assign({}, configAll, {
parserOptions: configAll.languageOptions.parserOptions,
plugins,
}),
'jsx-runtime': Object.assign({}, configRuntime, {
parserOptions: configRuntime.languageOptions.parserOptions,
plugins,
}),
},
};

@@ -8,2 +8,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -366,7 +368,5 @@ const propsUtil = require('../util/props');

const list = components.list();
values(components.list()).forEach((component) => {
const annotation = getComponentTypeAnnotation(component);
Object.keys(list).forEach((component) => {
const annotation = getComponentTypeAnnotation(list[component]);
if (annotation) {

@@ -385,3 +385,3 @@ let propType;

validatePropNaming(
list[component].node,
component.node,
prop.properties || prop.members

@@ -393,4 +393,4 @@ );

if (list[component].invalidProps && list[component].invalidProps.length > 0) {
reportInvalidNaming(list[component]);
if (component.invalidProps && component.invalidProps.length > 0) {
reportInvalidNaming(component);
}

@@ -397,0 +397,0 @@ });

@@ -9,2 +9,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -95,11 +97,11 @@ const docsUrl = require('../util/docsUrl');

'Program:exit'() {
const list = components.list();
// If no defaultProps could be found, we don't report anything.
Object.keys(list).filter((component) => list[component].defaultProps).forEach((component) => {
reportInvalidDefaultProps(
list[component].declaredPropTypes,
list[component].defaultProps || {}
);
});
values(components.list())
.filter((component) => component.defaultProps)
.forEach((component) => {
reportInvalidDefaultProps(
component.declaredPropTypes,
component.defaultProps || {}
);
});
},

@@ -106,0 +108,0 @@ };

@@ -11,2 +11,3 @@ /**

const report = require('../util/report');
const testReactVersion = require('../util/version').testReactVersion;

@@ -98,2 +99,4 @@ const DEFAULT_OPTION = 'always';

// set to save renamed var of useContext
const contextSet = new Set();
/**

@@ -133,3 +136,3 @@ * @param {ASTNode} node We expect either an ArrowFunctionExpression,

const contextName = sfcParams.contextName();
// props.aProp || context.aProp
// props.aProp
const isPropUsed = (

@@ -140,3 +143,3 @@ (propsName && node.object.name === propsName)

&& !isAssignmentLHS(node);
if (isPropUsed && configuration === 'always') {
if (isPropUsed && configuration === 'always' && !node.optional) {
report(context, messages.useDestructAssignment, 'useDestructAssignment', {

@@ -149,2 +152,17 @@ node,

}
// const foo = useContext(aContext);
// foo.aProp
const isContextUsed = contextSet.has(node.object.name) && !isAssignmentLHS(node);
const optional = node.optional
// the below is for the old typescript-eslint parser
|| context.getSourceCode().getText(node).slice(node.object.range[1] - node.range[0], node.object.range[1] - node.range[0] + 1) === '?';
if (isContextUsed && configuration === 'always' && !optional) {
report(context, messages.useDestructAssignment, 'useDestructAssignment', {
node,
data: {
type: node.object.name,
},
});
}
}

@@ -184,4 +202,5 @@

const hasHooks = testReactVersion(context, '>= 16.9');
return {
FunctionDeclaration: handleStatelessComponent,

@@ -221,4 +240,9 @@

const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
const identifier = (node.init && node.id && node.id.type === 'Identifier');
// let {foo} = props;
const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
const destructuringSFC = destructuring && node.init.name === 'props';
// let {foo} = useContext(aContext);
const destructuringUseContext = hasHooks && destructuring && node.init.callee && node.init.callee.name === 'useContext';
// let foo = useContext(aContext);
const assignUseContext = hasHooks && identifier && node.init.callee && node.init.callee.name === 'useContext';
// let {foo} = this.props;

@@ -229,2 +253,15 @@ const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (

if (SFCComponent && assignUseContext) {
contextSet.add(node.id.name);
}
if (SFCComponent && destructuringUseContext && configuration === 'never') {
report(context, messages.noDestructAssignment, 'noDestructAssignment', {
node,
data: {
type: node.init.callee.name,
},
});
}
if (SFCComponent && destructuringSFC && configuration === 'never') {

@@ -231,0 +268,0 @@ report(context, messages.noDestructAssignment, 'noDestructAssignment', {

@@ -42,3 +42,3 @@ /**

items: {
oneOf: [{
anyOf: [{
type: 'string',

@@ -45,0 +45,0 @@ }, {

@@ -57,3 +57,3 @@ /**

items: {
oneOf: [{
anyOf: [{
type: 'string',

@@ -60,0 +60,0 @@ }, {

@@ -135,3 +135,3 @@ /**

namedComponents: {
oneOf: [
anyOf: [
{

@@ -158,3 +158,3 @@ enum: [

unnamedComponents: {
oneOf: [
anyOf: [
{ enum: ['arrow-function', 'function-expression'] },

@@ -161,0 +161,0 @@ {

@@ -11,2 +11,3 @@ /**

const report = require('../util/report');
const getMessageData = require('../util/message');

@@ -17,4 +18,11 @@ // ------------------------------------------------------------------------------

function isNodeDestructuring(node) {
return node && (node.type === 'ArrayPattern' || node.type === 'ObjectPattern');
}
const messages = {
useStateErrorMessage: 'useState call is not destructured into value + setter pair',
useStateErrorMessageOrAddOption: 'useState call is not destructured into value + setter pair (you can allow destructuring by enabling "allowDestructuredState" option)',
suggestPair: 'Destructure useState call into value + setter pair',
suggestMemo: 'Replace useState call with useMemo',
};

@@ -31,3 +39,12 @@

messages,
schema: [],
schema: [{
type: 'object',
properties: {
allowDestructuredState: {
default: false,
type: 'boolean',
},
},
additionalProperties: false,
}],
type: 'suggestion',

@@ -37,128 +54,154 @@ hasSuggestions: true,

create: Components.detect((context, components, util) => ({
CallExpression(node) {
const isImmediateReturn = node.parent
&& node.parent.type === 'ReturnStatement';
create: Components.detect((context, components, util) => {
const configuration = context.options[0] || {};
const allowDestructuredState = configuration.allowDestructuredState || false;
if (isImmediateReturn || !util.isReactHookCall(node, ['useState'])) {
return;
}
return {
CallExpression(node) {
const isImmediateReturn = node.parent
&& node.parent.type === 'ReturnStatement';
const isDestructuringDeclarator = node.parent
&& node.parent.type === 'VariableDeclarator'
&& node.parent.id.type === 'ArrayPattern';
if (isImmediateReturn || !util.isReactHookCall(node, ['useState'])) {
return;
}
if (!isDestructuringDeclarator) {
report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{ node }
);
return;
}
const isDestructuringDeclarator = node.parent
&& node.parent.type === 'VariableDeclarator'
&& node.parent.id.type === 'ArrayPattern';
const variableNodes = node.parent.id.elements;
const valueVariable = variableNodes[0];
const setterVariable = variableNodes[1];
if (!isDestructuringDeclarator) {
report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{
node,
suggest: false,
}
);
return;
}
const valueVariableName = valueVariable
? valueVariable.name
: undefined;
const variableNodes = node.parent.id.elements;
const valueVariable = variableNodes[0];
const setterVariable = variableNodes[1];
const isOnlyValueDestructuring = isNodeDestructuring(valueVariable) && !isNodeDestructuring(setterVariable);
const setterVariableName = setterVariable
? setterVariable.name
: undefined;
if (allowDestructuredState && isOnlyValueDestructuring) {
return;
}
const caseCandidateMatch = valueVariableName ? valueVariableName.match(/(^[a-z]+)(.*)/) : undefined;
const upperCaseCandidatePrefix = caseCandidateMatch ? caseCandidateMatch[1] : undefined;
const caseCandidateSuffix = caseCandidateMatch ? caseCandidateMatch[2] : undefined;
const expectedSetterVariableNames = upperCaseCandidatePrefix ? [
`set${upperCaseCandidatePrefix.charAt(0).toUpperCase()}${upperCaseCandidatePrefix.slice(1)}${caseCandidateSuffix}`,
`set${upperCaseCandidatePrefix.toUpperCase()}${caseCandidateSuffix}`,
] : [];
const valueVariableName = valueVariable
? valueVariable.name
: undefined;
const isSymmetricGetterSetterPair = valueVariable
&& setterVariable
&& expectedSetterVariableNames.indexOf(setterVariableName) !== -1
&& variableNodes.length === 2;
const setterVariableName = setterVariable
? setterVariable.name
: undefined;
if (!isSymmetricGetterSetterPair) {
const suggestions = [
{
desc: 'Destructure useState call into value + setter pair',
fix: (fixer) => {
if (expectedSetterVariableNames.length === 0) {
return;
const caseCandidateMatch = valueVariableName ? valueVariableName.match(/(^[a-z]+)(.*)/) : undefined;
const upperCaseCandidatePrefix = caseCandidateMatch ? caseCandidateMatch[1] : undefined;
const caseCandidateSuffix = caseCandidateMatch ? caseCandidateMatch[2] : undefined;
const expectedSetterVariableNames = upperCaseCandidatePrefix ? [
`set${upperCaseCandidatePrefix.charAt(0).toUpperCase()}${upperCaseCandidatePrefix.slice(1)}${caseCandidateSuffix}`,
`set${upperCaseCandidatePrefix.toUpperCase()}${caseCandidateSuffix}`,
] : [];
const isSymmetricGetterSetterPair = valueVariable
&& setterVariable
&& expectedSetterVariableNames.indexOf(setterVariableName) !== -1
&& variableNodes.length === 2;
if (!isSymmetricGetterSetterPair) {
const suggestions = [
Object.assign(
getMessageData('suggestPair', messages.suggestPair),
{
fix(fixer) {
if (expectedSetterVariableNames.length > 0) {
return fixer.replaceTextRange(
node.parent.id.range,
`[${valueVariableName}, ${expectedSetterVariableNames[0]}]`
);
}
},
}
),
];
const fix = fixer.replaceTextRange(
node.parent.id.range,
`[${valueVariableName}, ${expectedSetterVariableNames[0]}]`
);
const defaultReactImports = components.getDefaultReactImports();
const defaultReactImportSpecifier = defaultReactImports
? defaultReactImports[0]
: undefined;
return fix;
},
},
];
const defaultReactImportName = defaultReactImportSpecifier
? defaultReactImportSpecifier.local.name
: undefined;
const defaultReactImports = components.getDefaultReactImports();
const defaultReactImportSpecifier = defaultReactImports
? defaultReactImports[0]
: undefined;
const namedReactImports = components.getNamedReactImports();
const useStateReactImportSpecifier = namedReactImports
? namedReactImports.find((specifier) => specifier.imported.name === 'useState')
: undefined;
const defaultReactImportName = defaultReactImportSpecifier
? defaultReactImportSpecifier.local.name
: undefined;
const isSingleGetter = valueVariable && variableNodes.length === 1;
const isUseStateCalledWithSingleArgument = node.arguments.length === 1;
if (isSingleGetter && isUseStateCalledWithSingleArgument) {
const useMemoReactImportSpecifier = namedReactImports
&& namedReactImports.find((specifier) => specifier.imported.name === 'useMemo');
const namedReactImports = components.getNamedReactImports();
const useStateReactImportSpecifier = namedReactImports
? namedReactImports.find((specifier) => specifier.imported.name === 'useState')
: undefined;
let useMemoCode;
if (useMemoReactImportSpecifier) {
useMemoCode = useMemoReactImportSpecifier.local.name;
} else if (defaultReactImportName) {
useMemoCode = `${defaultReactImportName}.useMemo`;
} else {
useMemoCode = 'useMemo';
}
const isSingleGetter = valueVariable && variableNodes.length === 1;
const isUseStateCalledWithSingleArgument = node.arguments.length === 1;
if (isSingleGetter && isUseStateCalledWithSingleArgument) {
const useMemoReactImportSpecifier = namedReactImports
&& namedReactImports.find((specifier) => specifier.imported.name === 'useMemo');
suggestions.unshift(Object.assign(
getMessageData('suggestMemo', messages.suggestMemo),
{
fix: (fixer) => [
// Add useMemo import, if necessary
useStateReactImportSpecifier
&& (!useMemoReactImportSpecifier || defaultReactImportName)
&& fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'),
// Convert single-value destructure to simple assignment
fixer.replaceTextRange(node.parent.id.range, valueVariableName),
// Convert useState call to useMemo + arrow function + dependency array
fixer.replaceTextRange(
node.range,
`${useMemoCode}(() => ${context.getSourceCode().getText(node.arguments[0])}, [])`
),
].filter(Boolean),
}
));
}
let useMemoCode;
if (useMemoReactImportSpecifier) {
useMemoCode = useMemoReactImportSpecifier.local.name;
} else if (defaultReactImportName) {
useMemoCode = `${defaultReactImportName}.useMemo`;
} else {
useMemoCode = 'useMemo';
if (isOnlyValueDestructuring) {
report(
context,
messages.useStateErrorMessageOrAddOption,
'useStateErrorMessageOrAddOption',
{
node: node.parent.id,
suggest: false,
}
);
return;
}
suggestions.unshift({
desc: 'Replace useState call with useMemo',
fix: (fixer) => [
// Add useMemo import, if necessary
useStateReactImportSpecifier
&& (!useMemoReactImportSpecifier || defaultReactImportName)
&& fixer.insertTextAfter(useStateReactImportSpecifier, ', useMemo'),
// Convert single-value destructure to simple assignment
fixer.replaceTextRange(node.parent.id.range, valueVariableName),
// Convert useState call to useMemo + arrow function + dependency array
fixer.replaceTextRange(
node.range,
`${useMemoCode}(() => ${context.getSourceCode().getText(node.arguments[0])}, [])`
),
].filter(Boolean),
});
report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{
node: node.parent.id,
suggest: suggestions,
}
);
}
report(
context,
messages.useStateErrorMessage,
'useStateErrorMessage',
{
node: node.parent.id,
suggest: suggestions,
}
);
}
},
})),
},
};
}),
};

@@ -33,3 +33,3 @@ /**

schema: [{
oneOf: [
anyOf: [
{

@@ -36,0 +36,0 @@ enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned'],

@@ -53,3 +53,3 @@ /**

{
oneOf: [
anyOf: [
{

@@ -56,0 +56,0 @@ type: 'object',

@@ -59,3 +59,3 @@ /**

{
oneOf: [
anyOf: [
{

@@ -62,0 +62,0 @@ enum: ['consistent', 'never'],

@@ -71,3 +71,3 @@ /**

basicConfigOrBoolean: {
oneOf: [{
anyOf: [{
$ref: '#/definitions/basicConfig',

@@ -81,3 +81,3 @@ }, {

items: [{
oneOf: [{
anyOf: [{
allOf: [{

@@ -84,0 +84,0 @@ $ref: '#/definitions/basicConfig',

@@ -58,3 +58,3 @@ /**

schema: [{
oneOf: [{
anyOf: [{
enum: ['tab', 'first'],

@@ -67,3 +67,3 @@ }, {

indentMode: {
oneOf: [{
anyOf: [{
enum: ['tab', 'first'],

@@ -70,0 +70,0 @@ }, {

@@ -61,3 +61,3 @@ /**

schema: [{
oneOf: [{
anyOf: [{
enum: ['tab'],

@@ -64,0 +64,0 @@ }, {

@@ -152,6 +152,16 @@ /**

const isArrFn = node && node.type === 'ArrowFunctionExpression';
if (isArrFn && (node.body.type === 'JSXElement' || node.body.type === 'JSXFragment')) {
const shouldCheckNode = (n) => n && (n.type === 'JSXElement' || n.type === 'JSXFragment');
if (isArrFn && shouldCheckNode(node.body)) {
checkIteratorElement(node.body);
}
if (node.body.type === 'ConditionalExpression') {
if (shouldCheckNode(node.body.consequent)) {
checkIteratorElement(node.body.consequent);
}
if (shouldCheckNode(node.body.alternate)) {
checkIteratorElement(node.body.alternate);
}
} else if (node.body.type === 'LogicalExpression' && shouldCheckNode(node.body.right)) {
checkIteratorElement(node.body.right);
}
}

@@ -158,0 +168,0 @@

@@ -123,7 +123,3 @@ /**

baseDepth += 1;
(children || []).forEach((node) => {
if (!hasJSX(node)) {
return;
}
(children || []).filter((node) => hasJSX(node)).forEach((node) => {
if (baseDepth > maxDepth) {

@@ -130,0 +126,0 @@ report(node, baseDepth);

@@ -75,2 +75,11 @@ /**

function isBlockCommentInCurlyBraces(element) {
const elementRawValue = sourceCode.getText(element);
return /^\s*{\/\*/.test(elementRawValue);
}
function isNonBlockComment(element) {
return !isBlockCommentInCurlyBraces(element) && (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer');
}
return {

@@ -97,3 +106,11 @@ 'Program:exit'() {

if (allowMultilines && (isMultilined(element) || isMultilined(secondAdjacentSibling))) {
if (isBlockCommentInCurlyBraces(element)) return;
if (
allowMultilines
&& (
isMultilined(element)
|| isMultilined(elements.slice(index + 2).find(isNonBlockComment))
)
) {
if (!isWithoutNewLine) return;

@@ -120,2 +137,3 @@

if (isWithoutNewLine === prevent) return;
const messageId = prevent

@@ -122,0 +140,0 @@ ? 'prevent'

@@ -141,2 +141,3 @@ /**

messages,
schema: {},
},

@@ -143,0 +144,0 @@

@@ -10,2 +10,3 @@ /**

const report = require('../util/report');
const testReactVersion = require('../util/version').testReactVersion;
const isParenthesized = require('../util/ast').isParenthesized;

@@ -64,5 +65,23 @@

}
if (node.parent && node.parent.type === 'ConditionalExpression' && node.parent.consequent.value === false) {
return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!'}${nodeText}`;
}
return `${getIsCoerceValidNestedLogicalExpression(node) ? '' : '!!'}${nodeText}`;
}).join(' && ');
if (rightNode.parent && rightNode.parent.type === 'ConditionalExpression' && rightNode.parent.consequent.value === false) {
const consequentVal = rightNode.parent.consequent.raw || rightNode.parent.consequent.name;
const alternateVal = rightNode.parent.alternate.raw || rightNode.parent.alternate.name;
if (rightNode.parent.test && rightNode.parent.test.type === 'LogicalExpression') {
return fixer.replaceText(reportedNode, `${newText} ? ${consequentVal} : ${alternateVal}`);
}
return fixer.replaceText(reportedNode, `${newText} && ${alternateVal}`);
}
if (rightNode.type === 'ConditionalExpression') {
return fixer.replaceText(reportedNode, `${newText} && (${rightSideText})`);
}
if (rightNode.type === 'Literal') {
return null;
}
return fixer.replaceText(reportedNode, `${newText} && ${rightSideText}`);

@@ -135,2 +154,5 @@ }

if (testReactVersion(context, '>= 18') && leftSide.type === 'Literal' && leftSide.value === '') {
return;
}
report(context, messages.noPotentialLeakedRender, 'noPotentialLeakedRender', {

@@ -137,0 +159,0 @@ node,

@@ -68,3 +68,9 @@ /**

function getStringFromValue(value) {
/**
* Get the string(s) from a value
* @param {ASTNode} value The AST node being checked.
* @param {ASTNode} targetValue The AST node being checked.
* @returns {String | String[] | null} The string value, or null if not a string.
*/
function getStringFromValue(value, targetValue) {
if (value) {

@@ -79,7 +85,15 @@ if (value.type === 'Literal') {

const expr = value.expression;
return expr && (
expr.type === 'ConditionalExpression'
? [expr.consequent.value, expr.alternate.value]
: expr.value
);
if (expr && expr.type === 'ConditionalExpression') {
const relValues = [expr.consequent.value, expr.alternate.value];
if (targetValue.type === 'JSXExpressionContainer' && targetValue.expression && targetValue.expression.type === 'ConditionalExpression') {
const targetTestCond = targetValue.expression.test.name;
const relTestCond = value.expression.test.name;
if (targetTestCond === relTestCond) {
const targetBlankIndex = [targetValue.expression.consequent.value, targetValue.expression.alternate.value].indexOf('_blank');
return relValues[targetBlankIndex];
}
}
return relValues;
}
return expr.value;
}

@@ -92,2 +106,3 @@ }

const relIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'rel'));
const targetIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'target'));
if (relIndex === -1 || (warnOnSpreadAttributes && relIndex < spreadAttributeIndex)) {

@@ -98,3 +113,4 @@ return false;

const relAttribute = node.attributes[relIndex];
const value = getStringFromValue(relAttribute.value);
const targetAttributeValue = node.attributes[targetIndex] && node.attributes[targetIndex].value;
const value = getStringFromValue(relAttribute.value, targetAttributeValue);
return [].concat(value).every((item) => {

@@ -101,0 +117,0 @@ const tags = typeof item === 'string' ? item.toLowerCase().split(' ') : false;

@@ -97,2 +97,10 @@ /**

messages,
schema: [{
type: 'object',
properties: {
allowExpressions: {
type: 'boolean',
},
},
}],
},

@@ -99,0 +107,0 @@

/**
* @fileoverview Enforce default props alphabetical sorting
* @author Vladimir Kattsov
* @deprecated
*/

@@ -11,3 +12,6 @@

const report = require('../util/report');
const log = require('../util/log');
let isWarnedForDeprecation = false;
// ------------------------------------------------------------------------------

@@ -23,2 +27,4 @@ // Rule Definition

meta: {
deprecated: true,
replacedBy: ['sort-default-props'],
docs: {

@@ -173,4 +179,13 @@ description: 'Enforce defaultProps declarations alphabetical sorting',

},
Program() {
if (isWarnedForDeprecation) {
return;
}
log('The react/jsx-sort-default-props rule is deprecated. It has been renamed to `react/sort-default-props`.');
isWarnedForDeprecation = true;
},
};
},
};

@@ -10,2 +10,4 @@ /**

const includes = require('array-includes');
const toSorted = require('array.prototype.tosorted');
const docsUrl = require('../util/docsUrl');

@@ -243,3 +245,3 @@ const jsxUtil = require('../util/jsx');

.slice(0)
.map((group) => group.slice(0).sort((a, b) => contextCompare(a, b, options)));
.map((group) => toSorted(group, (a, b) => contextCompare(a, b, options)));

@@ -246,0 +248,0 @@ return function fixFunction(fixer) {

@@ -28,2 +28,3 @@ /**

deprecated: true,
replacedBy: ['jsx-tag-spacing'],
docs: {

@@ -30,0 +31,0 @@ description: 'Enforce spacing before closing bracket in JSX',

@@ -192,6 +192,3 @@ /**

}
node.specifiers.forEach((specifier) => {
if (!specifier.imported) {
return;
}
node.specifiers.filter(((s) => s.imported)).forEach((specifier) => {
checkDeprecation(node, `${MODULES[node.source.value][0]}.${specifier.imported.name}`);

@@ -216,6 +213,4 @@ });

}
node.id.properties.forEach((property) => {
if (property.type !== 'RestElement' && property.key) {
checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`);
}
node.id.properties.filter((p) => p.type !== 'RestElement' && p.key).forEach((property) => {
checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`);
});

@@ -222,0 +217,0 @@ },

@@ -9,2 +9,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -145,9 +147,7 @@ const componentUtil = require('../util/componentUtil');

'Program:exit'() {
const list = components.list();
Object.keys(list).forEach((key) => {
if (!isValid(list[key])) {
reportMutations(list[key]);
}
});
values(components.list())
.filter((component) => !isValid(component))
.forEach((component) => {
reportMutations(component);
});
},

@@ -154,0 +154,0 @@ };

@@ -11,2 +11,3 @@ /**

const report = require('../util/report');
const getMessageData = require('../util/message');

@@ -236,2 +237,7 @@ // ------------------------------------------------------------------------------

spaceDelimited: '”{{attributeName}}β€œ attribute values should be space delimited.',
suggestRemoveDefault: '"remove {{attributeName}}"',
suggestRemoveEmpty: '"remove empty attribute {{attributeName}}"',
suggestRemoveInvalid: 'β€œremove invalid attribute {{reportingValue}}”',
suggestRemoveWhitespaces: 'remove whitespaces in β€œ{{reportingValue}}”',
suggestRemoveNonString: 'remove non-string value in β€œ{{reportingValue}}”',
};

@@ -259,5 +265,8 @@

data: { attributeName },
fix(fixer) {
return fixer.remove(parentNode);
},
suggest: [
Object.assign(
getMessageData('suggestRemoveNonString', messages.suggestRemoveNonString),
{ fix(fixer) { return fixer.remove(parentNode); } }
),
],
});

@@ -271,5 +280,8 @@ return;

data: { attributeName },
fix(fixer) {
return fixer.remove(parentNode);
},
suggest: [
Object.assign(
getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty),
{ fix(fixer) { return fixer.remove(node.parent); } }
),
],
});

@@ -283,12 +295,19 @@ return;

const reportingValue = singlePart.reportingValue;
const suggest = [
Object.assign(
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid),
{ fix(fixer) { return fixer.removeRange(singlePart.range); } }
),
];
if (!allowedTags) {
const data = {
attributeName,
reportingValue,
};
report(context, messages.neverValid, 'neverValid', {
node,
data: {
attributeName,
reportingValue,
},
fix(fixer) {
return fixer.removeRange(singlePart.range);
},
data,
suggest,
});

@@ -303,5 +322,3 @@ } else if (!allowedTags.has(parentNodeName)) {

},
fix(fixer) {
return fixer.removeRange(singlePart.range);
},
suggest,
});

@@ -333,2 +350,3 @@ }

},
suggest: false,
});

@@ -347,5 +365,8 @@ }

data: { attributeName },
fix(fixer) {
return fixer.removeRange(whitespacePart.range);
},
suggest: [
Object.assign(
getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces),
{ fix(fixer) { return fixer.removeRange(whitespacePart.range); } }
),
],
});

@@ -356,5 +377,8 @@ } else if (whitespacePart.value !== '\u0020') {

data: { attributeName },
fix(fixer) {
return fixer.replaceTextRange(whitespacePart.range, '\u0020');
},
suggest: [
Object.assign(
getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces),
{ fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); } }
),
],
});

@@ -370,6 +394,2 @@ }

function fix(fixer) {
return fixer.remove(node);
}
const parentNodeName = node.parent.name.name;

@@ -387,3 +407,8 @@ if (!COMPONENT_ATTRIBUTE_MAP.has(attribute) || !COMPONENT_ATTRIBUTE_MAP.get(attribute).has(parentNodeName)) {

},
fix,
suggest: [
Object.assign(
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault),
{ fix(fixer) { return fixer.remove(node); } }
),
],
});

@@ -393,2 +418,4 @@ return;

function fix(fixer) { return fixer.remove(node); }
if (!node.value) {

@@ -398,3 +425,8 @@ report(context, messages.emptyIsMeaningless, 'emptyIsMeaningless', {

data: { attributeName: attribute },
fix,
suggest: [
Object.assign(
getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty),
{ fix }
),
],
});

@@ -420,12 +452,19 @@ return;

data: { attributeName: attribute },
fix,
suggest: [
Object.assign(
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault),
{ fix }
),
],
});
return;
}
if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') {
} else if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') {
report(context, messages.onlyStrings, 'onlyStrings', {
node,
data: { attributeName: attribute },
fix,
suggest: [
Object.assign(
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault),
{ fix }
),
],
});

@@ -458,7 +497,10 @@ }

},
suggest: [
Object.assign(
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid),
{ fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); } }
),
],
});
return;
}
if (!validTagSet.has(node.arguments[0].value)) {
} else if (!validTagSet.has(node.arguments[0].value)) {
report(context, messages.notValidFor, 'notValidFor', {

@@ -471,2 +513,3 @@ node: value,

},
suggest: false,
});

@@ -512,2 +555,3 @@ }

},
suggest: false,
});

@@ -525,2 +569,3 @@

},
suggest: false,
});

@@ -552,3 +597,2 @@

meta: {
fixable: 'code',
docs: {

@@ -567,2 +611,4 @@ description: 'Disallow usage of invalid attributes',

}],
type: 'suggestion',
hasSuggestions: true, // eslint-disable-line eslint-plugin/require-meta-has-suggestions
},

@@ -569,0 +615,0 @@

@@ -8,2 +8,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -68,11 +70,10 @@ const docsUrl = require('../util/docsUrl');

const list = components.list();
Object.keys(list).filter((component) => !isIgnored(list[component])).forEach((component, i) => {
if (i >= 1) {
values(components.list())
.filter((component) => !isIgnored(component))
.slice(1)
.forEach((component) => {
report(context, messages.onlyOneComponent, 'onlyOneComponent', {
node: list[component].node,
node: component.node,
});
}
});
});
},

@@ -79,0 +80,0 @@ };

@@ -8,2 +8,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -79,6 +81,7 @@ const docsUrl = require('../util/docsUrl');

'Program:exit'() {
const list = components.list();
Object.keys(list).filter((component) => !isValid(list[component])).forEach((component) => {
reportSetStateUsages(list[component]);
});
values(components.list())
.filter((component) => !isValid(component))
.forEach((component) => {
reportSetStateUsages(component);
});
},

@@ -85,0 +88,0 @@ };

@@ -250,7 +250,5 @@ /**

node.properties.forEach((property) => {
if (property.type !== 'SpreadElement') {
reportErrorIfPropertyCasingTypo(property.value, property.key, false);
reportErrorIfLifecycleMethodCasingTypo(property);
}
node.properties.filter((property) => property.type !== 'SpreadElement').forEach((property) => {
reportErrorIfPropertyCasingTypo(property.value, property.key, false);
reportErrorIfLifecycleMethodCasingTypo(property);
});

@@ -257,0 +255,0 @@ },

@@ -55,3 +55,3 @@ /**

items: {
oneOf: [{
anyOf: [{
type: 'string',

@@ -58,0 +58,0 @@ }, {

@@ -45,2 +45,3 @@ /**

'line',
'marker',
'mask',

@@ -206,3 +207,3 @@ 'path',

// See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
'dir', 'draggable', 'hidden', 'id', 'lang', 'nonce', 'part', 'slot', 'style', 'title', 'translate',
'dir', 'draggable', 'hidden', 'id', 'lang', 'nonce', 'part', 'slot', 'style', 'title', 'translate', 'inert',
// Element specific attributes

@@ -549,3 +550,3 @@ // See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes (includes global attributes too)

if (tagName === 'fbt') { return; } // fbt nodes are bonkers, let's not go there
if (tagName === 'fbt' || tagName === 'fbs') { return; } // fbt/fbs nodes are bonkers, let's not go there

@@ -552,0 +553,0 @@ if (!isValidHTMLTagInJSX(node)) { return; }

@@ -248,7 +248,7 @@ /**

if (node.init && isThisExpression(node.init) && node.id.type === 'ObjectPattern') {
node.id.properties.forEach((prop) => {
if (prop.type === 'Property' && isKeyLiteralLike(prop, prop.key)) {
node.id.properties
.filter((prop) => prop.type === 'Property' && isKeyLiteralLike(prop, prop.key))
.forEach((prop) => {
addUsedProperty(prop.key);
}
});
});
}

@@ -255,0 +255,0 @@ },

@@ -8,2 +8,4 @@ /**

const values = require('object.values');
// As for exceptions for props.children or props.className (and alike) look at

@@ -163,10 +165,8 @@ // https://github.com/jsx-eslint/eslint-plugin-react/issues/7

'Program:exit'() {
const list = components.list();
// Report undeclared proptypes for all classes
Object.keys(list).filter((component) => mustBeValidated(list[component])).forEach((component) => {
if (!mustBeValidated(list[component])) {
return;
}
reportUnusedPropTypes(list[component]);
});
values(components.list())
.filter((component) => mustBeValidated(component))
.forEach((component) => {
reportUnusedPropTypes(component);
});
},

@@ -173,0 +173,0 @@ };

@@ -8,2 +8,5 @@ /**

const flatMap = require('array.prototype.flatmap');
const values = require('object.values');
const Components = require('../util/Components');

@@ -53,14 +56,9 @@ const docsUrl = require('../util/docsUrl');

'Program:exit'() {
const list = components.list();
flatMap(
values(components.list()),
(component) => component.declaredPropTypes || []
).forEach((declaredPropTypes) => {
Object.keys(declaredPropTypes).forEach((propName) => {
const prop = declaredPropTypes[propName];
Object.keys(list).forEach((key) => {
const component = list[key];
if (!component.declaredPropTypes) {
return;
}
Object.keys(component.declaredPropTypes).forEach((propName) => {
const prop = component.declaredPropTypes[propName];
if (!prop.node || !isFlowPropertyType(prop.node)) {

@@ -67,0 +65,0 @@ return;

@@ -10,2 +10,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -367,25 +369,21 @@ const testReactVersion = require('../util/version').testReactVersion;

const list = components.list();
Object.keys(list).forEach((component) => {
if (
hasOtherProperties(list[component].node)
|| list[component].useThis
|| list[component].useRef
|| list[component].invalidReturn
|| list[component].hasChildContextTypes
|| list[component].useDecorators
|| (
!componentUtil.isES5Component(list[component].node, context)
&& !componentUtil.isES6Component(list[component].node, context)
values(list)
.filter((component) => (
!hasOtherProperties(component.node)
&& !component.useThis
&& !component.useRef
&& !component.invalidReturn
&& !component.hasChildContextTypes
&& !component.useDecorators
&& !component.hasSCU
&& (
componentUtil.isES5Component(component.node, context)
|| componentUtil.isES6Component(component.node, context)
)
) {
return;
}
if (list[component].hasSCU) {
return;
}
report(context, messages.componentShouldBePure, 'componentShouldBePure', {
node: list[component].node,
))
.forEach((component) => {
report(context, messages.componentShouldBePure, 'componentShouldBePure', {
node: component.node,
});
});
});
},

@@ -392,0 +390,0 @@ };

@@ -11,2 +11,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -192,5 +194,7 @@ const docsUrl = require('../util/docsUrl');

// Report undeclared proptypes for all classes
Object.keys(list).filter((component) => mustBeValidated(list[component])).forEach((component) => {
reportUndeclaredPropTypes(list[component]);
});
values(list)
.filter((component) => mustBeValidated(component))
.forEach((component) => {
reportUndeclaredPropTypes(component);
});
},

@@ -197,0 +201,0 @@ };

@@ -8,2 +8,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -229,8 +231,8 @@ const componentUtil = require('../util/componentUtil');

'Program:exit'() {
const list = components.list();
// Report missing shouldComponentUpdate for all components
Object.keys(list).filter((component) => !list[component].hasSCU).forEach((component) => {
reportMissingOptimization(list[component]);
});
values(components.list())
.filter((component) => !component.hasSCU)
.forEach((component) => {
reportMissingOptimization(component);
});
},

@@ -237,0 +239,0 @@ };

@@ -8,2 +8,4 @@ /**

const values = require('object.values');
const Components = require('../util/Components');

@@ -86,18 +88,16 @@ const astUtil = require('../util/ast');

'Program:exit'() {
const list = components.list();
Object.keys(list).forEach((component) => {
if (
!findRenderMethod(list[component].node)
|| list[component].hasReturnStatement
|| (
!componentUtil.isES5Component(list[component].node, context)
&& !componentUtil.isES6Component(list[component].node, context)
values(components.list())
.filter((component) => (
findRenderMethod(component.node)
&& !component.hasReturnStatement
&& (
componentUtil.isES5Component(component.node, context)
|| componentUtil.isES6Component(component.node, context)
)
) {
return;
}
report(context, messages.noRenderReturn, 'noRenderReturn', {
node: findRenderMethod(list[component].node),
))
.forEach((component) => {
report(context, messages.noRenderReturn, 'noRenderReturn', {
node: findRenderMethod(component.node),
});
});
});
},

@@ -104,0 +104,0 @@ };

@@ -10,2 +10,3 @@ /**

const entries = require('object.entries');
const values = require('object.values');
const arrayIncludes = require('array-includes');

@@ -435,5 +436,4 @@

'Program:exit'() {
const list = components.list();
Object.keys(list).forEach((component) => {
const properties = astUtil.getComponentProperties(list[component].node);
values(components.list()).forEach((component) => {
const properties = astUtil.getComponentProperties(component.node);
checkPropsOrder(properties);

@@ -440,0 +440,0 @@ });

@@ -11,3 +11,3 @@ /**

const propWrapperUtil = require('../util/propWrapper');
// const propTypesSortUtil = require('../util/propTypesSort');
const propTypesSortUtil = require('../util/propTypesSort');
const report = require('../util/report');

@@ -33,3 +33,3 @@

},
// fixable: 'code',
fixable: 'code',

@@ -111,13 +111,13 @@ messages,

// function fix(fixer) {
// return propTypesSortUtil.fixPropTypesSort(
// fixer,
// context,
// declarations,
// ignoreCase,
// requiredFirst,
// callbacksLast,
// sortShapeProp
// );
// }
function fix(fixer) {
return propTypesSortUtil.fixPropTypesSort(
fixer,
context,
declarations,
ignoreCase,
requiredFirst,
callbacksLast,
sortShapeProp
);
}

@@ -156,3 +156,3 @@ const callbackPropsLastSeen = new WeakSet();

node: curr,
// fix
fix,
});

@@ -175,3 +175,3 @@ }

node: prev,
// fix
fix,
});

@@ -188,3 +188,3 @@ }

node: curr,
// fix
fix,
});

@@ -269,5 +269,4 @@ }

},
};
},
};

@@ -112,3 +112,3 @@ /**

let component = list[getId(node)];
while (!component) {
while (!component || component.confidence < 1) {
node = node.parent;

@@ -185,3 +185,3 @@ if (!node) {

const list = Lists.get(this);
return Object.keys(list).filter((i) => list[i].confidence >= 2).length;
return values(list).filter((component) => component.confidence >= 2).length;
}

@@ -482,3 +482,2 @@

if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
const isMethod = parent.type === 'Property' && parent.method;
const isPropertyAssignment = parent.type === 'AssignmentExpression'

@@ -568,2 +567,14 @@ && parent.left.type === 'MemberExpression';

if (
node.parent.type === 'Property' && (
(node.parent.method && !node.parent.computed) // case: { f() { return ... } }
|| (!node.id && !node.parent.computed) // case: { f: () => ... }
)
) {
if (isFirstLetterCapitalized(node.parent.key.name) && utils.isReturningJSX(node)) {
return node;
}
return undefined;
}
// Case like `React.memo(() => <></>)` or `React.forwardRef(...)`

@@ -583,6 +594,2 @@ const pragmaComponentWrapper = utils.getPragmaComponentWrapper(node);

if (isMethod && !isFirstLetterCapitalized(node.parent.key.name)) {
return utils.isReturningJSX(node) ? node : undefined;
}
if (node.id) {

@@ -861,9 +868,4 @@ return isFirstLetterCapitalized(node.id.name) ? node : undefined;

const component = utils.getParentComponent();
if (
!component
|| (component.parent && component.parent.type === 'JSXExpressionContainer')
) {
// Ban the node if we cannot find a parent component
components.add(node, 0);
const component = utils.getStatelessComponent(node);
if (!component) {
return;

@@ -880,3 +882,3 @@ }

node = utils.getParentComponent();
node = utils.getStatelessComponent(node);
if (!node) {

@@ -894,9 +896,4 @@ return;

const component = utils.getParentComponent();
if (
!component
|| (component.parent && component.parent.type === 'JSXExpressionContainer')
) {
// Ban the node if we cannot find a parent component
components.add(node, 0);
const component = utils.getStatelessComponent(node);
if (!component) {
return;

@@ -903,0 +900,0 @@ }

@@ -7,2 +7,4 @@ /**

const toSorted = require('array.prototype.tosorted');
const astUtil = require('./ast');

@@ -120,5 +122,33 @@

*/
const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start and end range
function fixPropTypesSort(fixer, context, declarations, ignoreCase, requiredFirst, callbacksLast, sortShapeProp) {
function sortInSource(allNodes, source) {
const originalSource = source;
const sourceCode = context.getSourceCode();
for (let i = 0; i < allNodes.length; i++) {
const node = allNodes[i];
let commentAfter = [];
let commentBefore = [];
let newStart = 0;
let newEnd = 0;
try {
commentBefore = sourceCode.getCommentsBefore(node);
commentAfter = sourceCode.getCommentsAfter(node);
} catch (e) { /**/ }
if (commentAfter.length === 0 || commentBefore.length === 0) {
newStart = node.range[0];
newEnd = node.range[1];
}
const firstCommentBefore = commentBefore[0];
if (commentBefore.length >= 1) {
newStart = firstCommentBefore.range[0];
}
const lastCommentAfter = commentAfter[commentAfter.length - 1];
if (commentAfter.length >= 1) {
newEnd = lastCommentAfter.range[1];
}
commentnodeMap.set(node, { start: newStart, end: newEnd, hasComment: true });
}
const nodeGroups = allNodes.reduce((acc, curr) => {

@@ -134,9 +164,14 @@ if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') {

nodeGroups.forEach((nodes) => {
const sortedAttributes = nodes
.slice()
.sort((a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast));
const sortedAttributes = toSorted(
nodes,
(a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast)
);
source = nodes.reduceRight((acc, attr, index) => {
const sortedAttr = sortedAttributes[index];
let sortedAttrText = context.getSourceCode().getText(sortedAttr);
const sourceCodeText = sourceCode.getText();
let sortedAttrText = sourceCodeText.substring(
commentnodeMap.get(sortedAttr).start,
commentnodeMap.get(sortedAttr).end
);
if (sortShapeProp && isShapeProp(sortedAttr.value)) {

@@ -152,3 +187,3 @@ const shape = getShapeProperties(sortedAttr.value);

}
return `${acc.slice(0, attr.range[0])}${sortedAttrText}${acc.slice(attr.range[1])}`;
return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrText}${acc.slice(commentnodeMap.get(attr).end)}`;
}, source);

@@ -161,4 +196,4 @@ });

const rangeStart = declarations[0].range[0];
const rangeEnd = declarations[declarations.length - 1].range[1];
const rangeStart = commentnodeMap.get(declarations[0]).start;
const rangeEnd = commentnodeMap.get(declarations[declarations.length - 1]).end;
return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd));

@@ -165,0 +200,0 @@ }

'use strict';
const semver = require('semver');
const eslintPkg = require('eslint/package.json');
const getMessageData = require('./message');

@@ -9,3 +8,3 @@ module.exports = function report(context, message, messageId, data) {

Object.assign(
messageId && semver.satisfies(eslintPkg.version, '>= 4.15') ? { messageId } : { message },
getMessageData(messageId, message),
data

@@ -12,0 +11,0 @@ )

@@ -7,2 +7,4 @@ /**

const values = require('object.values');
const astUtil = require('./ast');

@@ -561,9 +563,9 @@ const componentUtil = require('./componentUtil');

'Program:exit'() {
const list = components.list();
Object.keys(list).filter((component) => mustBeValidated(list[component])).forEach((component) => {
handleCustomValidators(list[component]);
});
values(components.list())
.filter((component) => mustBeValidated(component))
.forEach((component) => {
handleCustomValidators(component);
});
},
};
};
{
"name": "eslint-plugin-react",
"version": "7.31.10",
"version": "7.32.2",
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",

@@ -11,2 +11,3 @@ "description": "React specific linting rules for ESLint",

"lint:docs": "markdownlint \"**/*.md\"",
"postlint:docs": "npm run update:eslint-docs -- --check",
"lint": "eslint .",

@@ -19,4 +20,3 @@ "postlint": "npm run type-check",

"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js",
"generate-list-of-rules": "md-magic --path README.md",
"generate-list-of-rules:check": "npm run generate-list-of-rules && git diff --exit-code README.md"
"update:eslint-docs": "eslint-doc-generator"
},

@@ -30,4 +30,5 @@ "repository": {

"dependencies": {
"array-includes": "^3.1.5",
"array.prototype.flatmap": "^1.3.0",
"array-includes": "^3.1.6",
"array.prototype.flatmap": "^1.3.1",
"array.prototype.tosorted": "^1.1.1",
"doctrine": "^2.1.0",

@@ -37,13 +38,13 @@ "estraverse": "^5.3.0",

"minimatch": "^3.1.2",
"object.entries": "^1.1.5",
"object.fromentries": "^2.0.5",
"object.hasown": "^1.1.1",
"object.values": "^1.1.5",
"object.entries": "^1.1.6",
"object.fromentries": "^2.0.6",
"object.hasown": "^1.1.2",
"object.values": "^1.1.6",
"prop-types": "^15.8.1",
"resolve": "^2.0.0-next.3",
"resolve": "^2.0.0-next.4",
"semver": "^6.3.0",
"string.prototype.matchall": "^4.0.7"
"string.prototype.matchall": "^4.0.8"
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@babel/core": "^7.20.12",
"@babel/eslint-parser": "^7.19.1",

@@ -58,15 +59,15 @@ "@babel/plugin-syntax-decorators": "^7.19.0",

"@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4.0.0 || ^5.0.0",
"aud": "^2.0.1",
"aud": "^2.0.2",
"babel-eslint": "^8 || ^9 || ^10.1.0",
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-doc-generator": "^1.4.2",
"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": "^3.0.0",
"eslint-remote-tester-repositories": "^0.0.7",
"eslint-remote-tester-repositories": "^1.0.0",
"eslint-scope": "^3.7.3",
"espree": "^3.5.4",
"istanbul": "^0.4.5",
"ls-engines": "^0.7.0",
"markdown-magic": "^2.6.1",
"ls-engines": "^0.8.0",
"markdownlint-cli": "^0.8.0 || ^0.32.2",

@@ -101,2 +102,3 @@ "mocha": "^5.2.0",

"*.config.js",
".eslint-doc-generatorrc.js",
".eslintrc",

@@ -103,0 +105,0 @@ ".editorconfig",

@@ -18,5 +18,5 @@ # `eslint-plugin-react` <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

It is also possible to install ESLint globally rather than locally (using npm install eslint --global). However, this is not recommended, and any plugins or shareable configs that you use must be installed locally in either case.
It is also possible to install ESLint globally rather than locally (using `npm install -g eslint`). However, this is not recommended, and any plugins or shareable configs that you use must be installed locally in either case.
## Configuration
## Configuration (legacy: `.eslintrc*`)

@@ -113,128 +113,6 @@ Use [our preset](#recommended) to get reasonable defaults:

## List of supported rules
### Shareable configs
βœ”: Enabled in the [`recommended`](#recommended) configuration.\
πŸ”§: 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).
#### Recommended
<!-- 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) | 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
<!-- 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) | 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
- Rules of Hooks: [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks)
- JSX accessibility: [eslint-plugin-jsx-a11y](https://github.com/evcohen/eslint-plugin-jsx-a11y)
- React Native: [eslint-plugin-react-native](https://github.com/Intellicode/eslint-plugin-react-native)
## Shareable configurations
### Recommended
This plugin exports a `recommended` configuration that enforces React good practices.

@@ -252,3 +130,3 @@

### All
#### All

@@ -269,2 +147,261 @@ This plugin also exports an `all` configuration that includes every available rule.

## Configuration (new: `eslint.config.js`)
From [`v8.21.0`](https://github.com/eslint/eslint/releases/tag/v8.21.0), eslint announced a new config system.
In the new system, `.eslintrc*` is no longer used. `eslint.config.js` would be the default config file name.
In eslint `v8`, the legacy system (`.eslintrc*`) would still be supported, while in eslint `v9`, only the new system would be supported.
And from [`v8.23.0`](https://github.com/eslint/eslint/releases/tag/v8.23.0), eslint CLI starts to look up `eslint.config.js`.
**So, if your eslint is `>=8.23.0`, you're 100% ready to use the new config system.**
You might want to check out the official blog posts,
- <https://eslint.org/blog/2022/08/new-config-system-part-1/>
- <https://eslint.org/blog/2022/08/new-config-system-part-2/>
- <https://eslint.org/blog/2022/08/new-config-system-part-3/>
and the [official docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new).
### Plugin
The default export of `eslint-plugin-react` is a plugin object.
```js
const react = require('eslint-plugin-react');
const globals = require('globals');
module.exports = [
…
{
files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
plugins: {
react,
},
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
globals: {
...globals.browser,
},
},
rules: {
// ... any rules you want
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',
},
// ... others are omitted for brevity
},
…
];
```
### Configuring shared settings
Refer to the [official docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuring-shared-settings).
The schema of the `settings.react` object would be identical to that of what's already described above in the legacy config section.
<!-- markdownlint-disable-next-line no-duplicate-heading -->
### Shareable configs
There're also 3 shareable configs.
- `eslint-plugin-react/configs/all`
- `eslint-plugin-react/configs/recommended`
- `eslint-plugin-react/configs/jsx-runtime`
If your eslint.config.js is ESM, include the `.js` extension (e.g. `eslint-plugin-react/recommended.js`). Note that the next semver-major will require omitting the extension for these imports.
**Note**: These configurations will import `eslint-plugin-react` and enable JSX in [`languageOptions.parserOptions`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects).
In the new config system, `plugin:` protocol(e.g. `plugin:react/recommended`) is no longer valid.
As eslint does not automatically import the preset config (shareable config), you explicitly do it by yourself.
```js
const reactRecommended = require('eslint-plugin-react/configs/recommended');
module.exports = [
…
reactRecommended, // This is not a plugin object, but a shareable config object
…
];
```
You can of course add/override some properties.
**Note**: Our shareable configs does not preconfigure `files` or [`languageOptions.globals`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects).
For most of the cases, you probably want to configure some properties by yourself.
```js
const reactRecommended = require('eslint-plugin-react/configs/recommended');
const globals = require('globals');
module.exports = [
…
{
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
...reactRecommended,
languageOptions: {
...reactRecommended.languageOptions,
globals: {
...globals.serviceworker,
...globals.browser;
},
},
},
…
];
```
The above example is same as the example below, as the new config system is based on chaining.
```js
const reactRecommended = require('eslint-plugin-react/configs/recommended');
const globals = require('globals');
module.exports = [
…
{
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
...reactRecommended,
},
{
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
languageOptions: {
globals: {
...globals.serviceworker,
...globals.browser,
},
},
},
…
];
```
## List of supported rules
<!-- begin auto-generated rules list -->
πŸ’Ό [Configurations](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs) enabled in.\
🚫 [Configurations](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs) disabled in.\
πŸƒ Set in the `jsx-runtime` [configuration](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs).\
β˜‘οΈ Set in the `recommended` [configuration](https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs).\
πŸ”§ Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
πŸ’‘ Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).\
❌ Deprecated.
| NameΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β  | Description | πŸ’Ό | 🚫 | πŸ”§ | πŸ’‘ | ❌ |
| :----------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | :- | :- | :- | :- | :- |
| [boolean-prop-naming](docs/rules/boolean-prop-naming.md) | Enforces consistent naming for boolean props | | | | | |
| [button-has-type](docs/rules/button-has-type.md) | Disallow usage of `button` elements without an explicit `type` attribute | | | | | |
| [default-props-match-prop-types](docs/rules/default-props-match-prop-types.md) | Enforce all defaultProps have a corresponding non-required PropType | | | | | |
| [destructuring-assignment](docs/rules/destructuring-assignment.md) | Enforce consistent usage of destructuring assignment of props, state, and context | | | πŸ”§ | | |
| [display-name](docs/rules/display-name.md) | Disallow missing displayName in a React component definition | β˜‘οΈ | | | | |
| [forbid-component-props](docs/rules/forbid-component-props.md) | Disallow certain props on components | | | | | |
| [forbid-dom-props](docs/rules/forbid-dom-props.md) | Disallow certain props on DOM Nodes | | | | | |
| [forbid-elements](docs/rules/forbid-elements.md) | Disallow certain elements | | | | | |
| [forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md) | Disallow using another component's propTypes | | | | | |
| [forbid-prop-types](docs/rules/forbid-prop-types.md) | Disallow certain propTypes | | | | | |
| [function-component-definition](docs/rules/function-component-definition.md) | Enforce a specific function type for function components | | | πŸ”§ | | |
| [hook-use-state](docs/rules/hook-use-state.md) | Ensure destructuring and symmetric naming of useState hook value and setter variables | | | | πŸ’‘ | |
| [iframe-missing-sandbox](docs/rules/iframe-missing-sandbox.md) | Enforce sandbox attribute on iframe elements | | | | | |
| [jsx-boolean-value](docs/rules/jsx-boolean-value.md) | Enforce boolean attributes notation in JSX | | | πŸ”§ | | |
| [jsx-child-element-spacing](docs/rules/jsx-child-element-spacing.md) | Enforce or disallow spaces inside of curly braces in JSX attributes and expressions | | | | | |
| [jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md) | Enforce closing bracket location in JSX | | | πŸ”§ | | |
| [jsx-closing-tag-location](docs/rules/jsx-closing-tag-location.md) | Enforce closing tag location for multiline JSX | | | πŸ”§ | | |
| [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 | | | πŸ”§ | | |
| [jsx-curly-newline](docs/rules/jsx-curly-newline.md) | Enforce consistent linebreaks in curly braces in JSX attributes and expressions | | | πŸ”§ | | |
| [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md) | Enforce or disallow spaces inside of curly braces in JSX attributes and expressions | | | πŸ”§ | | |
| [jsx-equals-spacing](docs/rules/jsx-equals-spacing.md) | Enforce or disallow spaces around equal signs in JSX attributes | | | πŸ”§ | | |
| [jsx-filename-extension](docs/rules/jsx-filename-extension.md) | Disallow file extensions that may contain JSX | | | | | |
| [jsx-first-prop-new-line](docs/rules/jsx-first-prop-new-line.md) | Enforce proper position of the first property in JSX | | | πŸ”§ | | |
| [jsx-fragments](docs/rules/jsx-fragments.md) | Enforce shorthand or standard form for React fragments | | | πŸ”§ | | |
| [jsx-handler-names](docs/rules/jsx-handler-names.md) | Enforce event handler naming conventions in JSX | | | | | |
| [jsx-indent](docs/rules/jsx-indent.md) | Enforce JSX indentation | | | πŸ”§ | | |
| [jsx-indent-props](docs/rules/jsx-indent-props.md) | Enforce props indentation in JSX | | | πŸ”§ | | |
| [jsx-key](docs/rules/jsx-key.md) | Disallow missing `key` props in iterators/collection literals | β˜‘οΈ | | | | |
| [jsx-max-depth](docs/rules/jsx-max-depth.md) | Enforce JSX maximum depth | | | | | |
| [jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md) | Enforce maximum of props on a single line in JSX | | | πŸ”§ | | |
| [jsx-newline](docs/rules/jsx-newline.md) | Require or prevent a new line after jsx elements and expressions. | | | πŸ”§ | | |
| [jsx-no-bind](docs/rules/jsx-no-bind.md) | Disallow `.bind()` or arrow functions in JSX props | | | | | |
| [jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md) | Disallow comments from being inserted as text nodes | β˜‘οΈ | | | | |
| [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 | | | | | |
| [jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md) | Disallow duplicate properties in JSX | β˜‘οΈ | | | | |
| [jsx-no-leaked-render](docs/rules/jsx-no-leaked-render.md) | Disallow problematic leaked values from being rendered | | | πŸ”§ | | |
| [jsx-no-literals](docs/rules/jsx-no-literals.md) | Disallow usage of string literals in JSX | | | | | |
| [jsx-no-script-url](docs/rules/jsx-no-script-url.md) | Disallow usage of `javascript:` URLs | | | | | |
| [jsx-no-target-blank](docs/rules/jsx-no-target-blank.md) | Disallow `target="_blank"` attribute without `rel="noreferrer"` | β˜‘οΈ | | πŸ”§ | | |
| [jsx-no-undef](docs/rules/jsx-no-undef.md) | Disallow undeclared variables in JSX | β˜‘οΈ | | | | |
| [jsx-no-useless-fragment](docs/rules/jsx-no-useless-fragment.md) | Disallow unnecessary fragments | | | πŸ”§ | | |
| [jsx-one-expression-per-line](docs/rules/jsx-one-expression-per-line.md) | Require one JSX element per line | | | πŸ”§ | | |
| [jsx-pascal-case](docs/rules/jsx-pascal-case.md) | Enforce PascalCase for user-defined JSX components | | | | | |
| [jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md) | Disallow multiple spaces between inline JSX props | | | πŸ”§ | | |
| [jsx-props-no-spreading](docs/rules/jsx-props-no-spreading.md) | Disallow JSX prop spreading | | | | | |
| [jsx-sort-default-props](docs/rules/jsx-sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting | | | | | ❌ |
| [jsx-sort-props](docs/rules/jsx-sort-props.md) | Enforce props alphabetical sorting | | | πŸ”§ | | |
| [jsx-space-before-closing](docs/rules/jsx-space-before-closing.md) | Enforce spacing before closing bracket in JSX | | | πŸ”§ | | ❌ |
| [jsx-tag-spacing](docs/rules/jsx-tag-spacing.md) | Enforce whitespace in and around the JSX opening and closing brackets | | | πŸ”§ | | |
| [jsx-uses-react](docs/rules/jsx-uses-react.md) | Disallow React to be incorrectly marked as unused | β˜‘οΈ | πŸƒ | | | |
| [jsx-uses-vars](docs/rules/jsx-uses-vars.md) | Disallow variables used in JSX to be incorrectly marked as unused | β˜‘οΈ | | | | |
| [jsx-wrap-multilines](docs/rules/jsx-wrap-multilines.md) | Disallow missing parentheses around multiline JSX | | | πŸ”§ | | |
| [no-access-state-in-setstate](docs/rules/no-access-state-in-setstate.md) | Disallow when this.state is accessed within setState | | | | | |
| [no-adjacent-inline-elements](docs/rules/no-adjacent-inline-elements.md) | Disallow adjacent inline elements not separated by whitespace. | | | | | |
| [no-array-index-key](docs/rules/no-array-index-key.md) | Disallow usage of Array index in keys | | | | | |
| [no-arrow-function-lifecycle](docs/rules/no-arrow-function-lifecycle.md) | Lifecycle methods should be methods on the prototype, not class fields | | | πŸ”§ | | |
| [no-children-prop](docs/rules/no-children-prop.md) | Disallow passing of children as props | β˜‘οΈ | | | | |
| [no-danger](docs/rules/no-danger.md) | Disallow usage of dangerous JSX properties | | | | | |
| [no-danger-with-children](docs/rules/no-danger-with-children.md) | Disallow when a DOM element is using both children and dangerouslySetInnerHTML | β˜‘οΈ | | | | |
| [no-deprecated](docs/rules/no-deprecated.md) | Disallow usage of deprecated methods | β˜‘οΈ | | | | |
| [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md) | Disallow usage of setState in componentDidMount | | | | | |
| [no-did-update-set-state](docs/rules/no-did-update-set-state.md) | Disallow usage of setState in componentDidUpdate | | | | | |
| [no-direct-mutation-state](docs/rules/no-direct-mutation-state.md) | Disallow direct mutation of this.state | β˜‘οΈ | | | | |
| [no-find-dom-node](docs/rules/no-find-dom-node.md) | Disallow usage of findDOMNode | β˜‘οΈ | | | | |
| [no-invalid-html-attribute](docs/rules/no-invalid-html-attribute.md) | Disallow usage of invalid attributes | | | | πŸ’‘ | |
| [no-is-mounted](docs/rules/no-is-mounted.md) | Disallow usage of isMounted | β˜‘οΈ | | | | |
| [no-multi-comp](docs/rules/no-multi-comp.md) | Disallow multiple component definition per file | | | | | |
| [no-namespace](docs/rules/no-namespace.md) | Enforce that namespaces are not used in React elements | | | | | |
| [no-object-type-as-default-prop](docs/rules/no-object-type-as-default-prop.md) | Disallow usage of referential-type variables as default param in functional component | | | | | |
| [no-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md) | Disallow usage of shouldComponentUpdate when extending React.PureComponent | | | | | |
| [no-render-return-value](docs/rules/no-render-return-value.md) | Disallow usage of the return value of ReactDOM.render | β˜‘οΈ | | | | |
| [no-set-state](docs/rules/no-set-state.md) | Disallow usage of setState | | | | | |
| [no-string-refs](docs/rules/no-string-refs.md) | Disallow using string references | β˜‘οΈ | | | | |
| [no-this-in-sfc](docs/rules/no-this-in-sfc.md) | Disallow `this` from being used in stateless functional components | | | | | |
| [no-typos](docs/rules/no-typos.md) | Disallow common typos | | | | | |
| [no-unescaped-entities](docs/rules/no-unescaped-entities.md) | Disallow unescaped HTML entities from appearing in markup | β˜‘οΈ | | | | |
| [no-unknown-property](docs/rules/no-unknown-property.md) | Disallow usage of unknown DOM property | β˜‘οΈ | | πŸ”§ | | |
| [no-unsafe](docs/rules/no-unsafe.md) | Disallow usage of unsafe lifecycle methods | | β˜‘οΈ | | | |
| [no-unstable-nested-components](docs/rules/no-unstable-nested-components.md) | Disallow creating unstable components inside components | | | | | |
| [no-unused-class-component-methods](docs/rules/no-unused-class-component-methods.md) | Disallow declaring unused methods of component class | | | | | |
| [no-unused-prop-types](docs/rules/no-unused-prop-types.md) | Disallow definitions of unused propTypes | | | | | |
| [no-unused-state](docs/rules/no-unused-state.md) | Disallow definitions of unused state | | | | | |
| [no-will-update-set-state](docs/rules/no-will-update-set-state.md) | Disallow usage of setState in componentWillUpdate | | | | | |
| [prefer-es6-class](docs/rules/prefer-es6-class.md) | Enforce ES5 or ES6 class for React Components | | | | | |
| [prefer-exact-props](docs/rules/prefer-exact-props.md) | Prefer exact proptype definitions | | | | | |
| [prefer-read-only-props](docs/rules/prefer-read-only-props.md) | Enforce that props are read-only | | | πŸ”§ | | |
| [prefer-stateless-function](docs/rules/prefer-stateless-function.md) | Enforce stateless components to be written as a pure function | | | | | |
| [prop-types](docs/rules/prop-types.md) | Disallow missing props validation in a React component definition | β˜‘οΈ | | | | |
| [react-in-jsx-scope](docs/rules/react-in-jsx-scope.md) | Disallow missing React when using JSX | β˜‘οΈ | πŸƒ | | | |
| [require-default-props](docs/rules/require-default-props.md) | Enforce a defaultProps definition for every prop that is not a required prop | | | | | |
| [require-optimization](docs/rules/require-optimization.md) | Enforce React components to have a shouldComponentUpdate method | | | | | |
| [require-render-return](docs/rules/require-render-return.md) | Enforce ES5 or ES6 class for returning value in render function | β˜‘οΈ | | | | |
| [self-closing-comp](docs/rules/self-closing-comp.md) | Disallow extra closing tags for components without children | | | πŸ”§ | | |
| [sort-comp](docs/rules/sort-comp.md) | Enforce component methods order | | | | | |
| [sort-default-props](docs/rules/sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting | | | | | |
| [sort-prop-types](docs/rules/sort-prop-types.md) | Enforce propTypes declarations alphabetical sorting | | | πŸ”§ | | |
| [state-in-constructor](docs/rules/state-in-constructor.md) | Enforce class component state initialization style | | | | | |
| [static-property-placement](docs/rules/static-property-placement.md) | Enforces where React component static properties should be positioned. | | | | | |
| [style-prop-object](docs/rules/style-prop-object.md) | Enforce style prop value is an object | | | | | |
| [void-dom-elements-no-children](docs/rules/void-dom-elements-no-children.md) | Disallow void DOM elements (e.g. `<img />`, `<br />`) from receiving children | | | | | |
<!-- end auto-generated rules list -->
## Other useful plugins
- Rules of Hooks: [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks)
- JSX accessibility: [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)
- React Native: [eslint-plugin-react-native](https://github.com/Intellicode/eslint-plugin-react-native)
## License

@@ -271,0 +408,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚑️ by Socket Inc