Socket
Socket
Sign inDemoInstall

eslint-plugin-react

Package Overview
Dependencies
189
Maintainers
2
Versions
202
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 7.29.4 to 7.30.0

lib/rules/jsx-no-leaked-render.js

1

index.js

@@ -41,2 +41,3 @@ 'use strict';

'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'),

@@ -43,0 +44,0 @@ 'jsx-no-script-url': require('./lib/rules/jsx-no-script-url'),

2

lib/rules/button-has-type.js

@@ -129,3 +129,3 @@ /**

if (typeProp.value.type === 'JSXExpressionContainer') {
if (typeProp.value && typeProp.value.type === 'JSXExpressionContainer') {
checkExpression(node, typeProp.value.expression);

@@ -132,0 +132,0 @@ return;

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

useDestructAssignment: 'Must use destructuring {{type}} assignment',
destructureInSignature: 'Must destructure props in the function signature.',
};

@@ -64,3 +65,3 @@

},
fixable: 'code',
messages,

@@ -80,2 +81,9 @@

},
destructureInSignature: {
type: 'string',
enum: [
'always',
'ignore',
],
},
},

@@ -89,2 +97,3 @@ additionalProperties: false,

const ignoreClassFields = (context.options[1] && (context.options[1].ignoreClassFields === true)) || false;
const destructureInSignature = (context.options[1] && context.options[1].destructureInSignature) || 'ignore';
const sfcParams = createSFCParams();

@@ -237,2 +246,37 @@

}
if (
SFCComponent
&& destructuringSFC
&& configuration === 'always'
&& destructureInSignature === 'always'
&& node.init.name === 'props'
) {
const scopeSetProps = context.getScope().set.get('props');
const propsRefs = scopeSetProps && scopeSetProps.references;
if (!propsRefs) {
return;
}
// Skip if props is used elsewhere
if (propsRefs.length > 1) {
return;
}
report(context, messages.destructureInSignature, 'destructureInSignature', {
node,
fix(fixer) {
const param = SFCComponent.node.params[0];
if (!param) {
return;
}
const replaceRange = [
param.range[0],
param.typeAnnotation ? param.typeAnnotation.range[0] : param.range[1],
];
return [
fixer.replaceTextRange(replaceRange, context.getSourceCode().getText(node.id)),
fixer.remove(node.parent),
];
},
});
}
},

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

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

const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -110,3 +111,3 @@ const propsUtil = require('../util/props');

&& (node.parent.type === 'VariableDeclarator' || node.parent.type === 'Property' || node.parent.method === true)
&& (!node.parent.parent || !utils.isES5Component(node.parent.parent))
&& (!node.parent.parent || !componentUtil.isES5Component(node.parent.parent, context))
);

@@ -197,3 +198,3 @@

ObjectExpression(node) {
if (!utils.isES5Component(node)) {
if (!componentUtil.isES5Component(node, context)) {
return;

@@ -200,0 +201,0 @@ }

@@ -18,4 +18,6 @@ /**

function buildFunction(template, parts) {
return Object.keys(parts)
.reduce((acc, key) => acc.replace(`{${key}}`, () => (parts[key] || '')), template);
return Object.keys(parts).reduce(
(acc, key) => acc.replace(`{${key}}`, () => parts[key] || ''),
template
);
}

@@ -25,4 +27,4 @@

'function-declaration': 'function {name}{typeParams}({params}){returnType} {body}',
'arrow-function': 'var {name}{typeAnnotation} = {typeParams}({params}){returnType} => {body}',
'function-expression': 'var {name}{typeAnnotation} = function{typeParams}({params}){returnType} {body}',
'arrow-function': '{varType} {name}{typeAnnotation} = {typeParams}({params}){returnType} => {body}',
'function-expression': '{varType} {name}{typeAnnotation} = function{typeParams}({params}){returnType} {body}',
};

@@ -37,3 +39,6 @@

if (node.typeParameters) {
return node.typeParameters.params.length === 1 && !node.typeParameters.params[0].constraint;
return (
node.typeParameters.params.length === 1
&& !node.typeParameters.params[0].constraint
);
}

@@ -45,3 +50,6 @@

function hasName(node) {
return node.type === 'FunctionDeclaration' || node.parent.type === 'VariableDeclarator';
return (
node.type === 'FunctionDeclaration'
|| node.parent.type === 'VariableDeclarator'
);
}

@@ -59,3 +67,6 @@

if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
if (
node.type === 'ArrowFunctionExpression'
|| node.type === 'FunctionExpression'
) {
return hasName(node) && node.parent.id.name;

@@ -67,3 +78,6 @@ }

if (node.params.length === 0) return null;
return source.slice(node.params[0].range[0], node.params[node.params.length - 1].range[1]);
return source.slice(
node.params[0].range[0],
node.params[node.params.length - 1].range[1]
);
}

@@ -75,7 +89,3 @@

if (node.body.type !== 'BlockStatement') {
return [
'{',
` return ${source.slice(range[0], range[1])}`,
'}',
].join('\n');
return ['{', ` return ${source.slice(range[0], range[1])}`, '}'].join('\n');
}

@@ -89,3 +99,6 @@

if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
if (
node.type === 'ArrowFunctionExpression'
|| node.type === 'FunctionExpression'
) {
return getNodeText(node.parent.id.typeAnnotation, source);

@@ -96,3 +109,7 @@ }

function isUnfixableBecauseOfExport(node) {
return node.type === 'FunctionDeclaration' && node.parent && node.parent.type === 'ExportDefaultDeclaration';
return (
node.type === 'FunctionDeclaration'
&& node.parent
&& node.parent.type === 'ExportDefaultDeclaration'
);
}

@@ -128,8 +145,18 @@

oneOf: [
{ enum: ['function-declaration', 'arrow-function', 'function-expression'] },
{
enum: [
'function-declaration',
'arrow-function',
'function-expression',
],
},
{
type: 'array',
items: {
type: 'string',
enum: ['function-declaration', 'arrow-function', 'function-expression'],
enum: [
'function-declaration',
'arrow-function',
'function-expression',
],
},

@@ -158,5 +185,10 @@ },

const configuration = context.options[0] || {};
let fileVarType = 'var';
const namedConfig = [].concat(configuration.namedComponents || 'function-declaration');
const unnamedConfig = [].concat(configuration.unnamedComponents || 'function-expression');
const namedConfig = [].concat(
configuration.namedComponents || 'function-declaration'
);
const unnamedConfig = [].concat(
configuration.unnamedComponents || 'function-expression'
);

@@ -169,15 +201,30 @@ function getFixer(node, options) {

if (options.type === 'function-declaration' && typeAnnotation) return;
if (options.type === 'arrow-function' && hasOneUnconstrainedTypeParam(node)) return;
if (options.type === 'function-declaration' && typeAnnotation) {
return;
}
if (options.type === 'arrow-function' && hasOneUnconstrainedTypeParam(node)) {
return;
}
if (isUnfixableBecauseOfExport(node)) return;
if (isFunctionExpressionWithName(node)) return;
let varType = fileVarType;
if (
(node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression')
&& node.parent.type === 'VariableDeclarator'
) {
varType = node.parent.parent.kind;
}
return (fixer) => fixer.replaceTextRange(options.range, buildFunction(options.template, {
typeAnnotation,
typeParams: getNodeText(node.typeParameters, source),
params: getParams(node, source),
returnType: getNodeText(node.returnType, source),
body: getBody(node, source),
name: getName(node),
}));
return (fixer) => fixer.replaceTextRange(
options.range,
buildFunction(options.template, {
typeAnnotation,
typeParams: getNodeText(node.typeParameters, source),
params: getParams(node, source),
returnType: getNodeText(node.returnType, source),
body: getBody(node, source),
name: getName(node),
varType,
})
);
}

@@ -203,5 +250,6 @@

template: NAMED_FUNCTION_TEMPLATES[namedConfig[0]],
range: node.type === 'FunctionDeclaration'
? node.range
: node.parent.parent.range,
range:
node.type === 'FunctionDeclaration'
? node.range
: node.parent.parent.range,
},

@@ -225,9 +273,26 @@ });

// --------------------------------------------------------------------------
const validatePairs = [];
let hasES6OrJsx = false;
return {
FunctionDeclaration(node) { validate(node, 'function-declaration'); },
ArrowFunctionExpression(node) { validate(node, 'arrow-function'); },
FunctionExpression(node) { validate(node, 'function-expression'); },
FunctionDeclaration(node) {
validatePairs.push([node, 'function-declaration']);
},
ArrowFunctionExpression(node) {
validatePairs.push([node, 'arrow-function']);
},
FunctionExpression(node) {
validatePairs.push([node, 'function-expression']);
},
VariableDeclaration(node) {
hasES6OrJsx = hasES6OrJsx || node.kind === 'const' || node.kind === 'let';
},
'Program:exit'() {
if (hasES6OrJsx) fileVarType = 'const';
validatePairs.forEach((pair) => validate(pair[0], pair[1]));
},
'ImportDeclaration, ExportNamedDeclaration, ExportDefaultDeclaration, ExportAllDeclaration, ExportSpecifier, ExportDefaultSpecifier, JSXElement, TSExportAssignment, TSImportEqualsDeclaration'() {
hasES6OrJsx = true;
},
};
}),
};

@@ -69,9 +69,13 @@ /**

const expectedSetterVariableName = valueVariableName ? (
`set${valueVariableName.charAt(0).toUpperCase()}${valueVariableName.slice(1)}`
) : undefined;
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
&& setterVariableName === expectedSetterVariableName
&& expectedSetterVariableNames.indexOf(setterVariableName) !== -1
&& variableNodes.length === 2;

@@ -84,5 +88,9 @@

fix: (fixer) => {
if (expectedSetterVariableNames.length === 0) {
return;
}
const fix = fixer.replaceTextRange(
node.parent.id.range,
`[${valueVariableName}, ${expectedSetterVariableName}]`
`[${valueVariableName}, ${expectedSetterVariableNames[0]}]`
);

@@ -89,0 +97,0 @@

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

const jsxUtil = require('../util/jsx');
const isCreateElement = require('../util/isCreateElement');

@@ -432,3 +431,3 @@ // ------------------------------------------------------------------------------

!fn
|| !jsxUtil.isReturningJSX((n) => isCreateElement(n, context), node, context, true)
|| !jsxUtil.isReturningJSX(node, context, true)
) {

@@ -435,0 +434,0 @@ return;

@@ -73,3 +73,3 @@ /**

if (jsxUtil.isJSX(node)) {
count++;
count += 1;
}

@@ -93,5 +93,3 @@ }

function find(refs, prevRefs) {
let i = refs.length;
while (--i >= 0) {
for (let i = refs.length - 1; i >= 0; i--) {
if (has(refs[i], 'writeExpr')) {

@@ -126,3 +124,3 @@ const writeExpr = refs[i].writeExpr;

function checkDescendant(baseDepth, children) {
baseDepth++;
baseDepth += 1;
(children || []).forEach((node) => {

@@ -129,0 +127,0 @@ if (!hasJSX(node)) {

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

const propName = require('jsx-ast-utils/propName');
const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');

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

create: Components.detect((context) => {
create(context) {
const configuration = context.options[0] || {};

@@ -203,3 +202,3 @@

};
}),
},
};

@@ -82,3 +82,3 @@ /**

const messages = {
NeedsMoreChildren: 'Fragments should contain more than one child - otherwise, there‘s no need for a Fragment at all.',
NeedsMoreChildren: 'Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all.',
ChildOfHtmlElement: 'Passing a fragment to an HTML element is useless.',

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

@@ -158,3 +158,3 @@ /**

}
index++;
index += 1;
} while (index < checkNames.length && !allowNamespace);

@@ -161,0 +161,0 @@ },

@@ -136,3 +136,3 @@ /**

) {
groupCount++;
groupCount += 1;
sortableAttributeGroups[groupCount - 1] = [];

@@ -139,0 +139,0 @@ }

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

beforeSelfCloseNeedSpace: 'A space is required before closing bracket',
beforeSelfCloseNeedNewline: 'A newline is required before closing bracket',
afterOpenNoSpace: 'A space is forbidden after opening bracket',

@@ -24,2 +25,3 @@ afterOpenNeedSpace: 'A space is required after opening bracket',

beforeCloseNeedSpace: 'Whitespace is required before closing bracket',
beforeCloseNeedNewline: 'A newline is required before closing bracket',
};

@@ -104,2 +106,15 @@

if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
if (leftToken.loc.end.line === closingSlash.loc.start.line) {
report(context, messages.beforeSelfCloseNeedNewline, 'beforeSelfCloseNeedNewline', {
node,
loc: leftToken.loc.end,
fix(fixer) {
return fixer.insertTextBefore(closingSlash, '\n');
},
});
return;
}
}
if (leftToken.loc.end.line !== closingSlash.loc.start.line) {

@@ -109,3 +124,5 @@ return;

if (option === 'always' && !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash);
if ((option === 'always' || option === 'proportional-always') && adjacent) {
report(context, messages.beforeSelfCloseNeedSpace, 'beforeSelfCloseNeedSpace', {

@@ -118,3 +135,3 @@ node,

});
} else if (option === 'never' && sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
} else if (option === 'never' && !adjacent) {
report(context, messages.beforeSelfCloseNoSpace, 'beforeSelfCloseNoSpace', {

@@ -174,6 +191,20 @@ node,

const sourceCode = context.getSourceCode();
const lastTokens = sourceCode.getLastTokens(node, 2);
const closingToken = lastTokens[1];
const leftToken = lastTokens[0];
const leftToken = option === 'proportional-always'
? getTokenBeforeClosingBracket(node)
: sourceCode.getLastTokens(node, 2)[0];
const closingToken = sourceCode.getTokenAfter(leftToken);
if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
if (leftToken.loc.end.line === closingToken.loc.start.line) {
report(context, messages.beforeCloseNeedNewline, 'beforeCloseNeedNewline', {
node,
loc: leftToken.loc.end,
fix(fixer) {
return fixer.insertTextBefore(closingToken, '\n');
},
});
return;
}
}
if (leftToken.loc.start.line !== closingToken.loc.start.line) {

@@ -207,2 +238,13 @@ return;

});
} else if (option === 'proportional-always' && node.type === 'JSXOpeningElement' && adjacent !== (node.loc.start.line === node.loc.end.line)) {
report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
node,
loc: {
start: leftToken.loc.end,
end: closingToken.loc.start,
},
fix(fixer) {
return fixer.insertTextBefore(closingToken, ' ');
},
});
}

@@ -243,3 +285,3 @@ }

beforeSelfClosing: {
enum: ['always', 'never', 'allow'],
enum: ['always', 'proportional-always', 'never', 'allow'],
},

@@ -250,3 +292,3 @@ afterOpening: {

beforeClosing: {
enum: ['always', 'never', 'allow'],
enum: ['always', 'proportional-always', 'never', 'allow'],
},

@@ -253,0 +295,0 @@ },

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

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

@@ -93,16 +94,6 @@ // ------------------------------------------------------------------------------

function isParenthesised(node) {
const sourceCode = context.getSourceCode();
const previousToken = sourceCode.getTokenBefore(node);
const nextToken = sourceCode.getTokenAfter(node);
return previousToken && nextToken
&& previousToken.value === '(' && previousToken.range[1] <= node.range[0]
&& nextToken.value === ')' && nextToken.range[0] >= node.range[1];
}
function needsOpeningNewLine(node) {
const previousToken = context.getSourceCode().getTokenBefore(node);
if (!isParenthesised(node)) {
if (!isParenthesized(context, node)) {
return false;

@@ -121,3 +112,3 @@ }

if (!isParenthesised(node)) {
if (!isParenthesized(context, node)) {
return false;

@@ -159,3 +150,3 @@ }

if ((option === true || option === 'parens') && !isParenthesised(node) && isMultilines(node)) {
if ((option === true || option === 'parens') && !isParenthesized(context, node) && isMultilines(node)) {
report(node, 'missingParens', (fixer) => fixer.replaceText(node, `(${sourceCode.getText(node)})`));

@@ -165,3 +156,3 @@ }

if (option === 'parens-new-line' && isMultilines(node)) {
if (!isParenthesised(node)) {
if (!isParenthesized(context, node)) {
const tokenBefore = sourceCode.getTokenBefore(node, { includeComments: true });

@@ -168,0 +159,0 @@ const tokenAfter = sourceCode.getTokenAfter(node, { includeComments: true });

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

const docsUrl = require('../util/docsUrl');
const Components = require('../util/Components');
const componentUtil = require('../util/componentUtil');
const report = require('../util/report');

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

create: Components.detect((context, components, utils) => {
create(context) {
function isSetStateCall(node) {

@@ -53,3 +53,3 @@ return node.type === 'CallExpression'

function isClassComponent() {
return !!(utils.getParentES6Component() || utils.getParentES5Component());
return !!(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context));
}

@@ -189,3 +189,3 @@

};
}),
},
};

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

const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -48,3 +49,3 @@ const lifecycleMethods = require('../util/lifecycleMethods');

create: Components.detect((context, components, utils) => {
create: Components.detect((context, components) => {
/**

@@ -62,3 +63,3 @@ * @param {Array} properties list of component properties

const isLifecycleMethod = (
node.static && !utils.isES5Component(node)
node.static && !componentUtil.isES5Component(node, context)
? lifecycleMethods.static

@@ -65,0 +66,0 @@ : lifecycleMethods.instance

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

const values = require('object.values');
const Components = require('../util/Components');
const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -33,2 +32,59 @@ const pragmaUtil = require('../util/pragma');

function getDeprecated(pragma) {
const deprecated = {};
// 0.12.0
deprecated[`${pragma}.renderComponent`] = ['0.12.0', `${pragma}.render`];
deprecated[`${pragma}.renderComponentToString`] = ['0.12.0', `${pragma}.renderToString`];
deprecated[`${pragma}.renderComponentToStaticMarkup`] = ['0.12.0', `${pragma}.renderToStaticMarkup`];
deprecated[`${pragma}.isValidComponent`] = ['0.12.0', `${pragma}.isValidElement`];
deprecated[`${pragma}.PropTypes.component`] = ['0.12.0', `${pragma}.PropTypes.element`];
deprecated[`${pragma}.PropTypes.renderable`] = ['0.12.0', `${pragma}.PropTypes.node`];
deprecated[`${pragma}.isValidClass`] = ['0.12.0'];
deprecated['this.transferPropsTo'] = ['0.12.0', 'spread operator ({...})'];
// 0.13.0
deprecated[`${pragma}.addons.classSet`] = ['0.13.0', 'the npm module classnames'];
deprecated[`${pragma}.addons.cloneWithProps`] = ['0.13.0', `${pragma}.cloneElement`];
// 0.14.0
deprecated[`${pragma}.render`] = ['0.14.0', 'ReactDOM.render'];
deprecated[`${pragma}.unmountComponentAtNode`] = ['0.14.0', 'ReactDOM.unmountComponentAtNode'];
deprecated[`${pragma}.findDOMNode`] = ['0.14.0', 'ReactDOM.findDOMNode'];
deprecated[`${pragma}.renderToString`] = ['0.14.0', 'ReactDOMServer.renderToString'];
deprecated[`${pragma}.renderToStaticMarkup`] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup'];
// 15.0.0
deprecated[`${pragma}.addons.LinkedStateMixin`] = ['15.0.0'];
deprecated['ReactPerf.printDOM'] = ['15.0.0', 'ReactPerf.printOperations'];
deprecated['Perf.printDOM'] = ['15.0.0', 'Perf.printOperations'];
deprecated['ReactPerf.getMeasurementsSummaryMap'] = ['15.0.0', 'ReactPerf.getWasted'];
deprecated['Perf.getMeasurementsSummaryMap'] = ['15.0.0', 'Perf.getWasted'];
// 15.5.0
deprecated[`${pragma}.createClass`] = ['15.5.0', 'the npm module create-react-class'];
deprecated[`${pragma}.addons.TestUtils`] = ['15.5.0', 'ReactDOM.TestUtils'];
deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types'];
// 15.6.0
deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories'];
// 16.9.0
// For now the following life-cycle methods are just legacy, not deprecated:
// `componentWillMount`, `componentWillReceiveProps`, `componentWillUpdate`
// https://github.com/yannickcr/eslint-plugin-react/pull/1750#issuecomment-425975934
deprecated.componentWillMount = [
'16.9.0',
'UNSAFE_componentWillMount',
'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
deprecated.componentWillReceiveProps = [
'16.9.0',
'UNSAFE_componentWillReceiveProps',
'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
deprecated.componentWillUpdate = [
'16.9.0',
'UNSAFE_componentWillUpdate',
'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
return deprecated;
}
const messages = {

@@ -52,65 +108,7 @@ deprecated: '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}{{refs}}',

create: Components.detect((context, components, utils) => {
create(context) {
const pragma = pragmaUtil.getFromContext(context);
const deprecated = getDeprecated(pragma);
function getDeprecated() {
const deprecated = {};
// 0.12.0
deprecated[`${pragma}.renderComponent`] = ['0.12.0', `${pragma}.render`];
deprecated[`${pragma}.renderComponentToString`] = ['0.12.0', `${pragma}.renderToString`];
deprecated[`${pragma}.renderComponentToStaticMarkup`] = ['0.12.0', `${pragma}.renderToStaticMarkup`];
deprecated[`${pragma}.isValidComponent`] = ['0.12.0', `${pragma}.isValidElement`];
deprecated[`${pragma}.PropTypes.component`] = ['0.12.0', `${pragma}.PropTypes.element`];
deprecated[`${pragma}.PropTypes.renderable`] = ['0.12.0', `${pragma}.PropTypes.node`];
deprecated[`${pragma}.isValidClass`] = ['0.12.0'];
deprecated['this.transferPropsTo'] = ['0.12.0', 'spread operator ({...})'];
// 0.13.0
deprecated[`${pragma}.addons.classSet`] = ['0.13.0', 'the npm module classnames'];
deprecated[`${pragma}.addons.cloneWithProps`] = ['0.13.0', `${pragma}.cloneElement`];
// 0.14.0
deprecated[`${pragma}.render`] = ['0.14.0', 'ReactDOM.render'];
deprecated[`${pragma}.unmountComponentAtNode`] = ['0.14.0', 'ReactDOM.unmountComponentAtNode'];
deprecated[`${pragma}.findDOMNode`] = ['0.14.0', 'ReactDOM.findDOMNode'];
deprecated[`${pragma}.renderToString`] = ['0.14.0', 'ReactDOMServer.renderToString'];
deprecated[`${pragma}.renderToStaticMarkup`] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup'];
// 15.0.0
deprecated[`${pragma}.addons.LinkedStateMixin`] = ['15.0.0'];
deprecated['ReactPerf.printDOM'] = ['15.0.0', 'ReactPerf.printOperations'];
deprecated['Perf.printDOM'] = ['15.0.0', 'Perf.printOperations'];
deprecated['ReactPerf.getMeasurementsSummaryMap'] = ['15.0.0', 'ReactPerf.getWasted'];
deprecated['Perf.getMeasurementsSummaryMap'] = ['15.0.0', 'Perf.getWasted'];
// 15.5.0
deprecated[`${pragma}.createClass`] = ['15.5.0', 'the npm module create-react-class'];
deprecated[`${pragma}.addons.TestUtils`] = ['15.5.0', 'ReactDOM.TestUtils'];
deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types'];
// 15.6.0
deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories'];
// 16.9.0
// For now the following life-cycle methods are just legacy, not deprecated:
// `componentWillMount`, `componentWillReceiveProps`, `componentWillUpdate`
// https://github.com/yannickcr/eslint-plugin-react/pull/1750#issuecomment-425975934
deprecated.componentWillMount = [
'16.9.0',
'UNSAFE_componentWillMount',
'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
deprecated.componentWillReceiveProps = [
'16.9.0',
'UNSAFE_componentWillReceiveProps',
'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
deprecated.componentWillUpdate = [
'16.9.0',
'UNSAFE_componentWillUpdate',
'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate. '
+ 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.',
];
return deprecated;
}
function isDeprecated(method) {
const deprecated = getDeprecated();
return (

@@ -128,3 +126,2 @@ deprecated

}
const deprecated = getDeprecated();
const version = deprecated[methodName][0];

@@ -176,3 +173,6 @@ const newMethod = deprecated[methodName][1];

function checkLifeCycleMethods(node) {
if (utils.isES5Component(node) || utils.isES6Component(node)) {
if (
componentUtil.isES5Component(node, context)
|| componentUtil.isES6Component(node, context)
) {
const methods = getLifeCycleMethods(node);

@@ -231,3 +231,3 @@ methods.forEach((method) => checkDeprecation(node, method.name, method.node));

};
}),
},
};

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

const Components = require('../util/Components');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -103,3 +104,3 @@ const report = require('../util/report');

const item = getOuterMemberExpression(node.left);
if (utils.isStateMemberExpression(item)) {
if (componentUtil.isStateMemberExpression(item)) {
const mutations = (component && component.mutations) || [];

@@ -120,3 +121,3 @@ mutations.push(node.left.object);

const item = getOuterMemberExpression(node.argument);
if (utils.isStateMemberExpression(item)) {
if (componentUtil.isStateMemberExpression(item)) {
const mutations = (component && component.mutations) || [];

@@ -123,0 +124,0 @@ mutations.push(item);

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

const Components = require('../util/Components');
const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -35,3 +35,3 @@ const report = require('../util/report');

create: Components.detect((context, components, utils) => {
create(context) {
/**

@@ -70,3 +70,3 @@ * Checks for shouldComponentUpdate property

function checkForViolation(node) {
if (utils.isPureComponent(node)) {
if (componentUtil.isPureComponent(node, context)) {
const hasScu = hasShouldComponentUpdate(node);

@@ -89,3 +89,3 @@ if (hasScu) {

};
}),
},
};

@@ -8,3 +8,3 @@ /**

const Components = require('../util/Components');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -44,3 +44,3 @@ const report = require('../util/report');

create: Components.detect((context, components, utils) => {
create(context) {
const detectTemplateLiterals = context.options[0] ? context.options[0].noTemplateLiterals : false;

@@ -54,3 +54,3 @@ /**

return !!(
(utils.getParentES6Component() || utils.getParentES5Component())
(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context))
&& node.object.type === 'ThisExpression'

@@ -121,3 +121,3 @@ && node.property.name === 'refs'

};
}),
},
};

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

const docsUrl = require('../util/docsUrl');
const componentUtil = require('../util/componentUtil');
const report = require('../util/report');

@@ -206,3 +207,3 @@ const lifecycleMethods = require('../util/lifecycleMethods');

'ClassProperty, PropertyDefinition'(node) {
if (!node.static || !utils.isES6Component(node.parent.parent)) {
if (!node.static || !componentUtil.isES6Component(node.parent.parent, context)) {
return;

@@ -228,3 +229,3 @@ }

relatedComponent
&& (utils.isES6Component(relatedComponent.node) || (
&& (componentUtil.isES6Component(relatedComponent.node, context) || (
relatedComponent.node.type !== 'ClassDeclaration' && utils.isReturningJSX(relatedComponent.node)))

@@ -238,3 +239,3 @@ && (node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right)

MethodDefinition(node) {
if (!utils.isES6Component(node.parent.parent)) {
if (!componentUtil.isES6Component(node.parent.parent, context)) {
return;

@@ -247,3 +248,3 @@ }

ObjectExpression(node) {
const component = utils.isES5Component(node) && components.get(node);
const component = componentUtil.isES5Component(node, context) && components.get(node);

@@ -250,0 +251,0 @@ if (!component) {

@@ -30,3 +30,4 @@ /**

const ATTRIBUTE_TAGS_MAP = {
crossOrigin: ['script', 'img', 'video', 'audio', 'link'],
// image is required for SVG support, all other tags are HTML.
crossOrigin: ['script', 'img', 'video', 'audio', 'link', 'image'],
};

@@ -33,0 +34,0 @@

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

const Components = require('../util/Components');
const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -48,3 +48,3 @@ const testReactVersion = require('../util/version').testReactVersion;

create: Components.detect((context, components, utils) => {
create(context) {
const config = context.options[0] || {};

@@ -138,3 +138,3 @@ const checkAliases = config.checkAliases || false;

function checkLifeCycleMethods(node) {
if (utils.isES5Component(node) || utils.isES6Component(node)) {
if (componentUtil.isES5Component(node, context) || componentUtil.isES6Component(node, context)) {
const methods = getLifeCycleMethods(node);

@@ -150,3 +150,3 @@ methods.forEach((method) => checkUnsafe(node, method));

};
}),
},
};

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

const ERROR_MESSAGE_WITHOUT_NAME = 'Declare this component outside parent component or memoize it.';
const COMPONENT_AS_PROPS_INFO = ' If you want to allow component creation in props, set allowAsProps option to true.';

@@ -28,7 +27,7 @@ const HOOK_REGEXP = /^use[A-Z0-9].*$/;

* Generate error message with given parent component name
* @param {String} parentName Name of the parent component
* @param {String} parentName Name of the parent component, if known
* @returns {String} Error message with parent component name
*/
function generateErrorMessageWithParentName(parentName) {
return `Declare this component outside parent component "${parentName}" or memoize it.`;
return `Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component${parentName ? ` “${parentName}” ` : ' '}and pass data as props.`;
}

@@ -469,5 +468,3 @@

let message = parentName
? generateErrorMessageWithParentName(parentName)
: ERROR_MESSAGE_WITHOUT_NAME;
let message = generateErrorMessageWithParentName(parentName);

@@ -494,4 +491,5 @@ // Add information about allowAsProps option when component is declared inside prop

ClassDeclaration(node) { validate(node); },
CallExpression(node) { validate(node); },
};
}),
};

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

const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const componentUtil = require('../util/componentUtil');
const report = require('../util/report');

@@ -119,3 +119,3 @@

create: Components.detect((context, components, utils) => {
create: ((context) => {
let classInfo = null;

@@ -176,3 +176,3 @@

ClassDeclaration(node) {
if (utils.isES6Component(node)) {
if (componentUtil.isES6Component(node, context)) {
classInfo = getInitialClassInfo(node, true);

@@ -183,3 +183,3 @@ }

ObjectExpression(node) {
if (utils.isES5Component(node)) {
if (componentUtil.isES5Component(node, context)) {
classInfo = getInitialClassInfo(node, false);

@@ -186,0 +186,0 @@ }

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

// As for exceptions for props.children or props.className (and alike) look at
// https://github.com/yannickcr/eslint-plugin-react/issues/7
// https://github.com/jsx-eslint/eslint-plugin-react/issues/7

@@ -12,0 +12,0 @@ const Components = require('../util/Components');

@@ -12,5 +12,5 @@ /**

const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const ast = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const report = require('../util/report');

@@ -95,3 +95,3 @@

create: Components.detect((context, components, utils) => {
create(context) {
// Non-null when we are inside a React component ClassDeclaration and we have

@@ -166,2 +166,5 @@ // not yet encountered any use of this.state which we have chosen not to

function addUsedStateField(node) {
if (!classInfo) {
return;
}
const name = getName(node);

@@ -236,3 +239,3 @@ if (name) {

function handleES6ComponentEnter(node) {
if (utils.isES6Component(node)) {
if (componentUtil.isES6Component(node, context)) {
classInfo = getInitialClassInfo();

@@ -274,3 +277,3 @@ }

ObjectExpression(node) {
if (utils.isES5Component(node)) {
if (componentUtil.isES5Component(node, context)) {
classInfo = getInitialClassInfo();

@@ -285,3 +288,3 @@ }

if (utils.isES5Component(node)) {
if (componentUtil.isES5Component(node, context)) {
reportUnusedFields();

@@ -380,3 +383,3 @@ classInfo = null;

const stateArg = node.value.params[1]; // probably "state"
if (!scope.variables) {
if (!scope || !scope.variables) {
return;

@@ -431,3 +434,3 @@ }

const parent = node.parent;
if (!utils.isES5Component(parent.parent)) {
if (!componentUtil.isES5Component(parent.parent, context)) {
return;

@@ -524,3 +527,3 @@ }

};
}),
},
};

@@ -8,3 +8,3 @@ /**

const Components = require('../util/Components');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -38,3 +38,3 @@ const report = require('../util/report');

create: Components.detect((context, components, utils) => {
create(context) {
const configuration = context.options[0] || 'always';

@@ -44,3 +44,3 @@

ObjectExpression(node) {
if (utils.isES5Component(node) && configuration === 'always') {
if (componentUtil.isES5Component(node, context) && configuration === 'always') {
report(context, messages.shouldUseES6Class, 'shouldUseES6Class', {

@@ -52,3 +52,3 @@ node,

ClassDeclaration(node) {
if (utils.isES6Component(node) && configuration === 'never') {
if (componentUtil.isES6Component(node, context) && configuration === 'never') {
report(context, messages.shouldUseCreateClass, 'shouldUseCreateClass', {

@@ -60,3 +60,3 @@ node,

};
}),
},
};

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

const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -276,3 +277,3 @@ const report = require('../util/report');

function visitClass(node) {
if (ignorePureComponents && utils.isPureComponent(node)) {
if (ignorePureComponents && componentUtil.isPureComponent(node, context)) {
markSCUAsDeclared(node);

@@ -375,3 +376,6 @@ }

|| list[component].useDecorators
|| (!utils.isES5Component(list[component].node) && !utils.isES6Component(list[component].node))
|| (
!componentUtil.isES5Component(list[component].node, context)
&& !componentUtil.isES6Component(list[component].node, context)
)
) {

@@ -378,0 +382,0 @@ return;

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

// As for exceptions for props.children or props.className (and alike) look at
// https://github.com/yannickcr/eslint-plugin-react/issues/7
// https://github.com/jsx-eslint/eslint-plugin-react/issues/7

@@ -12,0 +12,0 @@ const Components = require('../util/Components');

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

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

@@ -21,2 +23,5 @@ const docsUrl = require('../util/docsUrl');

shouldHaveDefault: 'propType "{{name}}" is not required, but has no corresponding defaultProps declaration.',
noDefaultPropsWithFunction: 'Don’t use defaultProps with function components.',
shouldAssignObjectDefault: 'propType "{{name}}" is not required, but has no corresponding default argument value.',
destructureInSignature: 'Must destructure props in the function signature to initialize an optional prop.',
};

@@ -40,2 +45,15 @@

},
classes: {
allow: {
enum: ['defaultProps', 'ignore'],
},
},
functions: {
allow: {
enum: ['defaultArguments', 'defaultProps', 'ignore'],
},
},
/**
* @deprecated
*/
ignoreFunctionalComponents: {

@@ -52,3 +70,11 @@ type: 'boolean',

const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false;
const ignoreFunctionalComponents = configuration.ignoreFunctionalComponents || false;
const classes = configuration.classes || 'defaultProps';
/**
* @todo
* - Remove ignoreFunctionalComponents
* - Change default to 'defaultArguments'
*/
const functions = configuration.ignoreFunctionalComponents
? 'ignore'
: configuration.functions || 'defaultProps';

@@ -62,10 +88,6 @@ /**

function reportPropTypesWithoutDefault(propTypes, defaultProps) {
// If this defaultProps is "unresolved", then we should ignore this component and not report
// any errors for it, to avoid false-positives with e.g. external defaultProps declarations or spread operators.
if (defaultProps === 'unresolved') {
return;
}
entries(propTypes).forEach((propType) => {
const propName = propType[0];
const prop = propType[1];
Object.keys(propTypes).forEach((propName) => {
const prop = propTypes[propName];
if (!prop.node) {

@@ -95,2 +117,44 @@ return;

/**
* If functions option is 'defaultArguments', reports defaultProps is used and all params that doesn't initialized.
* @param {Object} componentNode Node of component.
* @param {Object[]} declaredPropTypes List of propTypes to check `isRequired`.
* @param {Object} defaultProps Object of defaultProps to check used.
*/
function reportFunctionComponent(componentNode, declaredPropTypes, defaultProps) {
if (defaultProps) {
report(context, messages.noDefaultPropsWithFunction, 'noDefaultPropsWithFunction', {
node: componentNode,
});
}
const props = componentNode.params[0];
const propTypes = declaredPropTypes;
if (props.type === 'Identifier') {
const hasOptionalProp = values(propTypes).some((propType) => !propType.isRequired);
if (hasOptionalProp) {
report(context, messages.destructureInSignature, 'destructureInSignature', {
node: props,
});
}
} else if (props.type === 'ObjectPattern') {
props.properties.filter((prop) => {
if (prop.type === 'RestElement' || prop.type === 'ExperimentalRestProperty') {
return false;
}
const propType = propTypes[prop.key.name];
if (!propType || propType.isRequired) {
return false;
}
return prop.value.type !== 'AssignmentPattern';
}).forEach((prop) => {
report(context, messages.shouldAssignObjectDefault, 'shouldAssignObjectDefault', {
node: prop,
data: { name: prop.key.name },
});
});
}
}
// --------------------------------------------------------------------------

@@ -104,13 +168,29 @@ // Public API

Object.keys(list).filter((component) => {
if (ignoreFunctionalComponents
&& (astUtil.isFunction(list[component].node) || astUtil.isFunctionLikeExpression(list[component].node))) {
values(list).filter((component) => {
if (functions === 'ignore' && astUtil.isFunctionLike(component.node)) {
return false;
}
return list[component].declaredPropTypes;
if (classes === 'ignore' && astUtil.isClass(component.node)) {
return false;
}
// If this defaultProps is "unresolved", then we should ignore this component and not report
// any errors for it, to avoid false-positives with e.g. external defaultProps declarations or spread operators.
if (component.defaultProps === 'unresolved') {
return false;
}
return component.declaredPropTypes !== undefined;
}).forEach((component) => {
reportPropTypesWithoutDefault(
list[component].declaredPropTypes,
list[component].defaultProps || {}
);
if (functions === 'defaultArguments' && astUtil.isFunctionLike(component.node)) {
reportFunctionComponent(
component.node,
component.declaredPropTypes,
component.defaultProps
);
} else {
reportPropTypesWithoutDefault(
component.declaredPropTypes,
component.defaultProps || {}
);
}
});

@@ -117,0 +197,0 @@ },

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

const Components = require('../util/Components');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -42,3 +43,3 @@ const report = require('../util/report');

create: Components.detect((context, components, utils) => {
create: Components.detect((context, components) => {
const configuration = context.options[0] || {};

@@ -182,3 +183,7 @@ const allowDecorators = configuration.allowDecorators || [];

ClassDeclaration(node) {
if (!(hasPureRenderDecorator(node) || hasCustomDecorator(node) || utils.isPureComponent(node))) {
if (!(
hasPureRenderDecorator(node)
|| hasCustomDecorator(node)
|| componentUtil.isPureComponent(node, context)
)) {
return;

@@ -185,0 +190,0 @@ }

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

const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -36,3 +37,3 @@ const report = require('../util/report');

create: Components.detect((context, components, utils) => {
create: Components.detect((context, components) => {
/**

@@ -66,3 +67,3 @@ * Mark a return statement as present

if (/Function(Expression|Declaration)$/.test(ancestor.type)) {
depth++;
depth += 1;
}

@@ -92,3 +93,6 @@ if (

|| list[component].hasReturnStatement
|| (!utils.isES5Component(list[component].node) && !utils.isES6Component(list[component].node))
|| (
!componentUtil.isES5Component(list[component].node, context)
&& !componentUtil.isES6Component(list[component].node, context)
)
) {

@@ -95,0 +99,0 @@ return;

@@ -269,3 +269,3 @@ /**

// Increment the prop score
errors[propA.index].score++;
errors[propA.index].score += 1;
// Stop here if we already have pushed another node at this position

@@ -272,0 +272,0 @@ if (getPropertyName(errors[propA.index].node) !== getPropertyName(propA.node)) {

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

const Components = require('../util/Components');
const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const docsUrl = require('../util/docsUrl');

@@ -38,3 +39,3 @@ const report = require('../util/report');

create: Components.detect((context, components, utils) => {
create(context) {
const option = context.options[0] || 'always';

@@ -47,3 +48,3 @@ return {

&& node.key.name === 'state'
&& utils.getParentES6Component()
&& componentUtil.getParentES6Component(context)
) {

@@ -58,5 +59,5 @@ report(context, messages.stateInitConstructor, 'stateInitConstructor', {

option === 'never'
&& utils.isStateMemberExpression(node.left)
&& utils.inConstructor()
&& utils.getParentES6Component()
&& componentUtil.isStateMemberExpression(node.left)
&& astUtil.inConstructor(context)
&& componentUtil.getParentES6Component(context)
) {

@@ -69,3 +70,3 @@ report(context, messages.stateInitClassProp, 'stateInitClassProp', {

};
}),
},
};

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

const astUtil = require('../util/ast');
const componentUtil = require('../util/componentUtil');
const propsUtil = require('../util/props');

@@ -145,3 +146,3 @@ const report = require('../util/report');

'ClassProperty, PropertyDefinition'(node) {
if (!utils.getParentES6Component()) {
if (!componentUtil.getParentES6Component(context)) {
return;

@@ -165,3 +166,3 @@ }

// If the related component is not an ES6 component then skip this node
if (!relatedComponent || !utils.isES6Component(relatedComponent.node)) {
if (!relatedComponent || !componentUtil.isES6Component(relatedComponent.node, context)) {
return;

@@ -176,3 +177,3 @@ }

// If the function is inside a class and is static getter then check if correctly positioned
if (utils.getParentES6Component() && node.static && node.kind === 'get') {
if (componentUtil.getParentES6Component(context) && node.static && node.kind === 'get') {
// Report error if needed

@@ -179,0 +180,0 @@ reportNodeIncorrectlyPositioned(node, STATIC_GETTER);

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

const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');

@@ -67,3 +67,3 @@

create: Components.detect((context, components, utils) => ({
create: (context) => ({
JSXElement(node) {

@@ -113,3 +113,3 @@ const elementName = node.openingElement.name.name;

if (!utils.isCreateElement(node)) {
if (!isCreateElement(node, context)) {
return;

@@ -167,3 +167,3 @@ }

},
})),
}),
};

@@ -16,5 +16,3 @@ import eslint from 'eslint';

interface Context extends eslint.SourceCode {
getFirstTokens(node: estree.Node | ASTNode, options?: eslint.SourceCode.CursorWithCountOptions): eslint.AST.Token[];
}
type Context = eslint.Rule.RuleContext

@@ -21,0 +19,0 @@ type TypeDeclarationBuilder = (annotation: ASTNode, parentName: string, seen: Set<typeof annotation>) => object;

@@ -31,2 +31,17 @@ /**

function loopNodes(nodes) {
for (let i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].type === 'ReturnStatement') {
return nodes[i];
}
if (nodes[i].type === 'SwitchStatement') {
const j = nodes[i].cases.length - 1;
if (j >= 0) {
return loopNodes(nodes[i].cases[j].consequent);
}
}
}
return false;
}
/**

@@ -46,21 +61,8 @@ * Find a return statment in the current node

const bodyNodes = (node.value ? node.value.body.body : node.body.body);
const bodyNodes = node.value ? node.value.body.body : node.body.body;
return (function loopNodes(nodes) {
let i = nodes.length - 1;
for (; i >= 0; i--) {
if (nodes[i].type === 'ReturnStatement') {
return nodes[i];
}
if (nodes[i].type === 'SwitchStatement') {
let j = nodes[i].cases.length - 1;
for (; j >= 0; j--) {
return loopNodes(nodes[i].cases[j].consequent);
}
}
}
return false;
}(bodyNodes));
return loopNodes(bodyNodes);
}
// eslint-disable-next-line valid-jsdoc -- valid-jsdoc cannot parse function types.
/**

@@ -72,14 +74,17 @@ * Helper function for traversing "returns" (return statements or the

* @param {Context} context The context of `ASTNode`.
* @param {function} enterFunc Function to execute for each returnStatement found
* @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn
* Function to execute for each returnStatement found
* @returns {undefined}
*/
function traverseReturns(ASTNode, context, enterFunc) {
function traverseReturns(ASTNode, context, onReturn) {
const nodeType = ASTNode.type;
if (nodeType === 'ReturnStatement') {
return enterFunc(ASTNode);
onReturn(ASTNode.argument, () => {});
return;
}
if (nodeType === 'ArrowFunctionExpression' && ASTNode.expression) {
return enterFunc(ASTNode.body);
onReturn(ASTNode.body, () => {});
return;
}

@@ -116,11 +121,19 @@

enter(node) {
const breakTraverse = () => {
this.break();
};
switch (node.type) {
case 'ReturnStatement':
this.skip();
return enterFunc(node);
case 'FunctionExpression':
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
return this.skip();
onReturn(node.argument, breakTraverse);
return;
case 'BlockStatement':
case 'IfStatement':
case 'ForStatement':
case 'WhileStatement':
case 'SwitchStatement':
case 'SwitchCase':
return;
default:
this.skip();
}

@@ -227,2 +240,11 @@ },

/**
* Checks if node is a function declaration or expression or arrow function.
* @param {ASTNode} node The node to check
* @return {Boolean} true if it's a function-like
*/
function isFunctionLike(node) {
return node.type === 'FunctionDeclaration' || isFunctionLikeExpression(node);
}
/**
* Checks if the node is a class.

@@ -237,2 +259,19 @@ * @param {ASTNode} node The node to check

/**
* Check if we are in a class constructor
* @param {Context} context
* @return {boolean}
*/
function inConstructor(context) {
let scope = context.getScope();
while (scope) {
// @ts-ignore
if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') {
return true;
}
scope = scope.upper;
}
return false;
}
/**
* Removes quotes from around an identifier.

@@ -249,3 +288,3 @@ * @param {string} string the identifier to strip

* @param {Context} context The AST node with the key.
* @param {ASTNode} node The AST node with the key.
* @param {any} node The AST node with the key.
* @return {string | undefined} the name of the key

@@ -255,3 +294,3 @@ */

if (node.type === 'ObjectTypeProperty') {
const tokens = context.getFirstTokens(node, 2);
const tokens = context.getSourceCode().getFirstTokens(node, 2);
return (tokens[0].value === '+' || tokens[0].value === '-'

@@ -276,2 +315,19 @@ ? tokens[1].value

/**
* Checks if a node is surrounded by parenthesis.
*
* @param {object} context - Context from the rule
* @param {ASTNode} node - Node to be checked
* @returns {boolean}
*/
function isParenthesized(context, node) {
const sourceCode = context.getSourceCode();
const previousToken = sourceCode.getTokenBefore(node);
const nextToken = sourceCode.getTokenAfter(node);
return !!previousToken && !!nextToken
&& previousToken.value === '(' && previousToken.range[1] <= node.range[0]
&& nextToken.value === ')' && nextToken.range[0] >= node.range[1];
}
/**
* Checks if a node is being assigned a value: props.bar = 'bar'

@@ -392,2 +448,3 @@ * @param {ASTNode} node The AST node being checked.

getKeyValue,
isParenthesized,
isAssignmentLHS,

@@ -397,2 +454,4 @@ isClass,

isFunctionLikeExpression,
isFunctionLike,
inConstructor,
isNodeFirstInLine,

@@ -399,0 +458,0 @@ unwrapTSAsExpression,

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

const doctrine = require('doctrine');
const arrayIncludes = require('array-includes');

@@ -17,2 +16,3 @@ const fromEntries = require('object.fromentries');

const astUtil = require('./ast');
const componentUtil = require('./componentUtil');
const propTypesUtil = require('./propTypes');

@@ -23,3 +23,2 @@ const jsxUtil = require('./jsx');

const isFirstLetterCapitalized = require('./isFirstLetterCapitalized');
const isCreateElement = require('./isCreateElement');
const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport');

@@ -254,4 +253,36 @@

// eslint-disable-next-line valid-jsdoc
/**
* Merge many eslint rules into one
* @param {{[_: string]: Function}[]} rules the returned values for eslint rule.create(context)
* @returns {{[_: string]: Function}} merged rule
*/
function mergeRules(rules) {
/** @type {Map<string, Function[]>} */
const handlersByKey = new Map();
rules.forEach((rule) => {
Object.keys(rule).forEach((key) => {
const fns = handlersByKey.get(key);
if (!fns) {
handlersByKey.set(key, [rule[key]]);
} else {
fns.push(rule[key]);
}
});
});
/** @type {{[key: string]: Function}} */
const rule = {};
handlersByKey.forEach((fns, key) => {
rule[key] = function mergedHandler(node) {
fns.forEach((fn) => {
fn(node);
});
};
});
return rule;
}
function componentRule(rule, context) {
const createClass = pragmaUtil.getCreateClassFromContext(context);
const pragma = pragmaUtil.getFromContext(context);

@@ -264,85 +295,3 @@ const sourceCode = context.getSourceCode();

const utils = {
/**
* Check if the node is a React ES5 component
*
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if the node is a React ES5 component, false if not
*/
isES5Component(node) {
if (!node.parent) {
return false;
}
return new RegExp(`^(${pragma}\\.)?${createClass}$`).test(sourceCode.getText(node.parent.callee));
},
/**
* Check if the node is a React ES6 component
*
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if the node is a React ES6 component, false if not
*/
isES6Component(node) {
if (utils.isExplicitComponent(node)) {
return true;
}
if (!node.superClass) {
return false;
}
return new RegExp(`^(${pragma}\\.)?(Pure)?Component$`).test(sourceCode.getText(node.superClass));
},
/**
* Check if the node is explicitly declared as a descendant of a React Component
*
* @param {ASTNode} node The AST node being checked (can be a ReturnStatement or an ArrowFunctionExpression).
* @returns {Boolean} True if the node is explicitly declared as a descendant of a React Component, false if not
*/
isExplicitComponent(node) {
let comment;
// Sometimes the passed node may not have been parsed yet by eslint, and this function call crashes.
// Can be removed when eslint sets "parent" property for all nodes on initial AST traversal: https://github.com/eslint/eslint-scope/issues/27
// eslint-disable-next-line no-warning-comments
// FIXME: Remove try/catch when https://github.com/eslint/eslint-scope/issues/27 is implemented.
try {
comment = sourceCode.getJSDocComment(node);
} catch (e) {
comment = null;
}
if (comment === null) {
return false;
}
let commentAst;
try {
commentAst = doctrine.parse(comment.value, {
unwrap: true,
tags: ['extends', 'augments'],
});
} catch (e) {
// handle a bug in the archived `doctrine`, see #2596
return false;
}
const relevantTags = commentAst.tags.filter((tag) => tag.name === 'React.Component' || tag.name === 'React.PureComponent');
return relevantTags.length > 0;
},
/**
* Checks to see if our component extends React.PureComponent
*
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if node extends React.PureComponent, false if not
*/
isPureComponent(node) {
if (node.superClass) {
return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(sourceCode.getText(node.superClass));
}
return false;
},
/**
* Check if variable is destructured from pragma import

@@ -357,46 +306,12 @@ *

/**
* Checks to see if node is called within createElement from pragma
*
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if createElement called from pragma
*/
isCreateElement(node) {
return isCreateElement(node, context);
},
/**
* Check if we are in a class constructor
* @return {boolean} true if we are in a class constructor, false if not
*/
inConstructor() {
let scope = context.getScope();
while (scope) {
if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') {
return true;
}
scope = scope.upper;
}
return false;
},
/**
* Determine if the node is MemberExpression of `this.state`
* @param {Object} node The node to process
* @returns {Boolean}
*/
isStateMemberExpression(node) {
return node.type === 'MemberExpression' && node.object.type === 'ThisExpression' && node.property.name === 'state';
},
isReturningJSX(ASTNode, strict) {
return jsxUtil.isReturningJSX(this.isCreateElement.bind(this), ASTNode, context, strict, true);
return jsxUtil.isReturningJSX(ASTNode, context, strict, true);
},
isReturningJSXOrNull(ASTNode, strict) {
return jsxUtil.isReturningJSX(this.isCreateElement.bind(this), ASTNode, context, strict);
return jsxUtil.isReturningJSX(ASTNode, context, strict);
},
isReturningOnlyNull(ASTNode) {
return jsxUtil.isReturningOnlyNull(this.isCreateElement.bind(this), ASTNode, context);
return jsxUtil.isReturningOnlyNull(ASTNode, context);
},

@@ -525,4 +440,4 @@

return (
utils.getParentES6Component()
|| utils.getParentES5Component()
componentUtil.getParentES6Component(context)
|| componentUtil.getParentES5Component(context)
|| utils.getParentStatelessComponent()

@@ -533,36 +448,2 @@ );

/**
* Get the parent ES5 component node from the current scope
*
* @returns {ASTNode} component node, null if we are not in a component
*/
getParentES5Component() {
let scope = context.getScope();
while (scope) {
const node = scope.block && scope.block.parent && scope.block.parent.parent;
if (node && utils.isES5Component(node)) {
return node;
}
scope = scope.upper;
}
return null;
},
/**
* Get the parent ES6 component node from the current scope
*
* @returns {ASTNode} component node, null if we are not in a component
*/
getParentES6Component() {
let scope = context.getScope();
while (scope && scope.type !== 'class') {
scope = scope.upper;
}
const node = scope && scope.block;
if (!node || !utils.isES6Component(node)) {
return null;
}
return node;
},
/**
* @param {ASTNode} node

@@ -899,3 +780,3 @@ * @returns {boolean}

ClassExpression(node) {
if (!utils.isES6Component(node)) {
if (!componentUtil.isES6Component(node, context)) {
return;

@@ -907,3 +788,3 @@ }

ClassDeclaration(node) {
if (!utils.isES6Component(node)) {
if (!componentUtil.isES6Component(node, context)) {
return;

@@ -914,12 +795,4 @@ }

'ClassProperty, PropertyDefinition'(node) {
node = utils.getParentComponent();
if (!node) {
return;
}
components.add(node, 2);
},
ObjectExpression(node) {
if (!utils.isES5Component(node)) {
if (!componentUtil.isES5Component(node, context)) {
return;

@@ -945,3 +818,3 @@ }

}
components.add(component, 1);
components.add(component, 2);
},

@@ -959,3 +832,3 @@

}
components.add(node, 1);
components.add(node, 2);
},

@@ -978,11 +851,7 @@

}
if (component.expression && utils.isReturningJSX(component)) {
components.add(component, 2);
} else {
components.add(component, 1);
}
components.add(component, 2);
},
ThisExpression(node) {
const component = utils.getParentComponent();
const component = utils.getParentStatelessComponent();
if (!component || !/Function/.test(component.type) || !node.parent.property) {

@@ -994,15 +863,2 @@ return;

},
ReturnStatement(node) {
if (!utils.isReturningJSX(node)) {
return;
}
node = utils.getParentComponent();
if (!node) {
const scope = context.getScope();
components.add(scope.block, 1);
return;
}
components.add(node, 2);
},
};

@@ -1029,40 +885,17 @@

// Update the provided rule instructions to add the component detection
const ruleInstructions = rule(context, components, utils);
const updatedRuleInstructions = Object.assign({}, ruleInstructions);
const propTypesInstructions = propTypesUtil(context, components, utils);
const usedPropTypesInstructions = usedPropTypesUtil(context, components, utils);
const defaultPropsInstructions = defaultPropsUtil(context, components, utils);
const allKeys = new Set(Object.keys(detectionInstructions).concat(
Object.keys(propTypesInstructions),
Object.keys(usedPropTypesInstructions),
Object.keys(defaultPropsInstructions),
Object.keys(reactImportInstructions)
));
allKeys.forEach((instruction) => {
updatedRuleInstructions[instruction] = (node) => {
if (instruction in detectionInstructions) {
detectionInstructions[instruction](node);
}
if (instruction in propTypesInstructions) {
propTypesInstructions[instruction](node);
}
if (instruction in usedPropTypesInstructions) {
usedPropTypesInstructions[instruction](node);
}
if (instruction in defaultPropsInstructions) {
defaultPropsInstructions[instruction](node);
}
if (instruction in reactImportInstructions) {
reactImportInstructions[instruction](node);
}
if (ruleInstructions[instruction]) {
return ruleInstructions[instruction](node);
}
};
});
const mergedRule = mergeRules([
detectionInstructions,
propTypesInstructions,
usedPropTypesInstructions,
defaultPropsInstructions,
reactImportInstructions,
ruleInstructions,
]);
// Return the updated rule instructions
return updatedRuleInstructions;
return mergedRule;
}

@@ -1069,0 +902,0 @@

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

const astUtil = require('./ast');
const componentUtil = require('./componentUtil');
const propsUtil = require('./props');

@@ -175,3 +176,3 @@ const variableUtil = require('./variable');

// find component this propTypes/defaultProps belongs to
const component = components.get(utils.getParentES6Component());
const component = components.get(componentUtil.getParentES6Component(context));
if (!component) {

@@ -219,3 +220,3 @@ return;

// find component this propTypes/defaultProps belongs to
const component = components.get(utils.getParentES6Component());
const component = components.get(componentUtil.getParentES6Component(context));
if (!component) {

@@ -246,3 +247,3 @@ return;

// find component this propTypes/defaultProps belongs to
const component = utils.isES5Component(node) && components.get(node);
const component = componentUtil.isES5Component(node, context) && components.get(node);
if (!component) {

@@ -249,0 +250,0 @@ return;

'use strict';
function docsUrl(ruleName) {
return `https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules/${ruleName}.md`;
return `https://github.com/jsx-eslint/eslint-plugin-react/tree/master/docs/rules/${ruleName}.md`;
}
module.exports = docsUrl;

@@ -10,3 +10,3 @@ 'use strict';

const attributes = node.attributes;
if (attributes.length === 0) {
if (!attributes || attributes.length === 0) {
return node.name;

@@ -13,0 +13,0 @@ }

@@ -7,6 +7,6 @@ /**

const estraverse = require('estraverse');
const elementType = require('jsx-ast-utils/elementType');
const astUtil = require('./ast');
const isCreateElement = require('./isCreateElement');
const variableUtil = require('./variable');

@@ -90,4 +90,2 @@

*
* @param {Function} isCreateElement Function to determine if a CallExpresion is
* a createElement one
* @param {ASTNode} ASTnode The AST node being checked

@@ -99,60 +97,45 @@ * @param {Context} context The context of `ASTNode`.

*/
function isReturningJSX(isCreateElement, ASTnode, context, strict, ignoreNull) {
let found = false;
astUtil.traverseReturns(ASTnode, context, (node) => {
// Traverse return statement
astUtil.traverse(node, {
enter(childNode) {
const setFound = () => {
found = true;
this.skip();
};
switch (childNode.type) {
case 'FunctionExpression':
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
// Do not traverse into inner function definitions
return this.skip();
case 'ConditionalExpression':
if (!strict) break;
if (isJSX(childNode.consequent) && isJSX(childNode.alternate)) {
setFound();
}
this.skip();
break;
case 'LogicalExpression':
if (!strict) break;
if (isJSX(childNode.left) && isJSX(childNode.right)) {
setFound();
}
this.skip();
break;
case 'JSXElement':
case 'JSXFragment':
setFound();
break;
case 'CallExpression':
if (isCreateElement(childNode)) {
setFound();
}
this.skip();
break;
case 'Literal':
if (!ignoreNull && childNode.value === null) {
setFound();
}
break;
case 'Identifier': {
const variable = variableUtil.findVariableByName(context, childNode.name);
if (isJSX(variable)) {
setFound();
}
break;
}
default:
function isReturningJSX(ASTnode, context, strict, ignoreNull) {
const isJSXValue = (node) => {
if (!node) {
return false;
}
switch (node.type) {
case 'ConditionalExpression':
if (strict) {
return isJSXValue(node.consequent) && isJSXValue(node.alternate);
}
},
});
return isJSXValue(node.consequent) || isJSXValue(node.alternate);
case 'LogicalExpression':
if (strict) {
return isJSXValue(node.left) && isJSXValue(node.right);
}
return isJSXValue(node.left) || isJSXValue(node.right);
case 'SequenceExpression':
return isJSXValue(node.expressions[node.expressions.length - 1]);
case 'JSXElement':
case 'JSXFragment':
return true;
case 'CallExpression':
return isCreateElement(node, context);
case 'Literal':
if (!ignoreNull && node.value === null) {
return true;
}
return false;
case 'Identifier': {
const variable = variableUtil.findVariableByName(context, node.name);
return isJSX(variable);
}
default:
return false;
}
};
return found && estraverse.VisitorOption.Break;
let found = false;
astUtil.traverseReturns(ASTnode, context, (node, breakTraverse) => {
if (isJSXValue(node)) {
found = true;
breakTraverse();
}
});

@@ -166,4 +149,2 @@

*
* @param {Function} isCreateElement Function to determine if a CallExpresion is
* a createElement one
* @param {ASTNode} ASTnode The AST node being checked

@@ -173,3 +154,3 @@ * @param {Context} context The context of `ASTNode`.

*/
function isReturningOnlyNull(isCreateElement, ASTnode, context) {
function isReturningOnlyNull(ASTnode, context) {
let found = false;

@@ -176,0 +157,0 @@ let foundSomethingElse = false;

@@ -76,2 +76,6 @@ /**

if (shouldBeNoop(context, methodName)) {
return {};
}
// --------------------------------------------------------------------------

@@ -83,6 +87,2 @@ // Public

CallExpression(node) {
if (shouldBeNoop(context, methodName)) {
return;
}
const callee = node.callee;

@@ -100,3 +100,3 @@ if (

if (/Function(Expression|Declaration)$/.test(ancestor.type)) {
depth++;
depth += 1;
}

@@ -103,0 +103,0 @@ if (

@@ -12,2 +12,6 @@ /**

/**
* @param {Context} context
* @returns {string}
*/
function getCreateClassFromContext(context) {

@@ -25,2 +29,6 @@ let pragma = 'createReactClass';

/**
* @param {Context} context
* @returns {string}
*/
function getFragmentFromContext(context) {

@@ -38,2 +46,6 @@ let pragma = 'Fragment';

/**
* @param {Context} context
* @returns {string}
*/
function getFromContext(context) {

@@ -40,0 +52,0 @@ let pragma = 'React';

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

VoidFunctionComponent: 0,
VFC: 0,
PropsWithChildren: 0,

@@ -1019,3 +1020,3 @@ SFC: 0,

declaredPropTypes: obj.declaredPropTypes,
ignorePropsValidation: false,
ignorePropsValidation: obj.shouldIgnorePropTypes,
});

@@ -1033,3 +1034,3 @@ return;

// https://github.com/yannickcr/eslint-plugin-react/issues/2784
// https://github.com/jsx-eslint/eslint-plugin-react/issues/2784
if (isInsideClassBody(node) && !astUtil.isFunction(node)) {

@@ -1058,3 +1059,3 @@ return;

} else {
// implements what's discussed here: https://github.com/yannickcr/eslint-plugin-react/issues/2777#issuecomment-683944481
// implements what's discussed here: https://github.com/jsx-eslint/eslint-plugin-react/issues/2777#issuecomment-683944481
const annotation = siblingIdentifier.typeAnnotation.typeAnnotation;

@@ -1061,0 +1062,0 @@

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

const astUtil = require('./ast');
const componentUtil = require('./componentUtil');
const testReactVersion = require('./version').testReactVersion;

@@ -185,4 +186,8 @@ const ast = require('./ast');

function isInClassComponent(utils) {
return utils.getParentES6Component() || utils.getParentES5Component();
/**
* @param {Context} context
* @returns {boolean}
*/
function isInClassComponent(context) {
return !!(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context));
}

@@ -224,3 +229,3 @@

if (isInClassComponent(utils)) {
if (isInClassComponent(context)) {
// this.props.*

@@ -233,3 +238,3 @@ if (isThisDotProps(unwrappedObjectNode)) {

isCommonVariableNameForProps(unwrappedObjectNode.name)
&& (inLifeCycleMethod(context, checkAsyncSafeLifeCycles) || utils.inConstructor())
&& (inLifeCycleMethod(context, checkAsyncSafeLifeCycles) || astUtil.inConstructor(context))
) {

@@ -472,3 +477,3 @@ return true;

// let props = this.props
if (isThisDotProps(unwrappedInitNode) && isInClassComponent(utils) && node.id.type === 'Identifier') {
if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context) && node.id.type === 'Identifier') {
propVariables.set(node.id.name, []);

@@ -509,3 +514,3 @@ }

// let {firstname} = this.props
if (isThisDotProps(unwrappedInitNode) && isInClassComponent(utils)) {
if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context)) {
markPropTypesAsUsed(node.id);

@@ -512,0 +517,0 @@ return;

@@ -90,3 +90,3 @@ /**

error('Warning: React version not specified in eslint-plugin-react settings. '
+ 'See https://github.com/yannickcr/eslint-plugin-react#configuration .');
+ 'See https://github.com/jsx-eslint/eslint-plugin-react#configuration .');
warnedForMissingVersion = true;

@@ -93,0 +93,0 @@ }

{
"name": "eslint-plugin-react",
"version": "7.29.4",
"version": "7.30.0",
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>",

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

"scripts": {
"prepack": "npmignore --auto --commentLines=autogenerated",
"lint": "eslint .",

@@ -19,17 +20,11 @@ "postlint": "npm run type-check",

},
"files": [
"LICENSE",
"README.md",
"index.js",
"lib"
],
"repository": {
"type": "git",
"url": "https://github.com/yannickcr/eslint-plugin-react"
"url": "https://github.com/jsx-eslint/eslint-plugin-react"
},
"homepage": "https://github.com/yannickcr/eslint-plugin-react",
"bugs": "https://github.com/yannickcr/eslint-plugin-react/issues",
"homepage": "https://github.com/jsx-eslint/eslint-plugin-react",
"bugs": "https://github.com/jsx-eslint/eslint-plugin-react/issues",
"dependencies": {
"array-includes": "^3.1.4",
"array.prototype.flatmap": "^1.2.5",
"array-includes": "^3.1.5",
"array.prototype.flatmap": "^1.3.0",
"doctrine": "^2.1.0",

@@ -41,3 +36,3 @@ "estraverse": "^5.3.0",

"object.fromentries": "^2.0.5",
"object.hasown": "^1.1.0",
"object.hasown": "^1.1.1",
"object.values": "^1.1.5",

@@ -47,14 +42,14 @@ "prop-types": "^15.8.1",

"semver": "^6.3.0",
"string.prototype.matchall": "^4.0.6"
"string.prototype.matchall": "^4.0.7"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/core": "^7.17.12",
"@babel/eslint-parser": "^7.17.0",
"@babel/plugin-syntax-decorators": "^7.17.0",
"@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.16.7",
"@babel/preset-react": "^7.17.12",
"@types/eslint": "=7.2.10",
"@types/estree": "^0.0.50",
"@types/node": "^16.11.26",
"@types/estree": "0.0.51",
"@types/node": "^16.11.35",
"@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4.0.0 || ^5.0.0",

@@ -66,11 +61,12 @@ "aud": "^2.0.0",

"eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1",
"eslint-plugin-import": "^2.25.4",
"eslint-remote-tester": "^2.1.1",
"eslint-remote-tester-repositories": "^0.0.4",
"eslint-plugin-import": "^2.26.0",
"eslint-remote-tester": "^2.1.4",
"eslint-remote-tester-repositories": "^0.0.5",
"eslint-scope": "^3.7.3",
"espree": "^3.5.4",
"istanbul": "^0.4.5",
"ls-engines": "^0.6.5",
"ls-engines": "^0.6.6",
"markdown-magic": "^2.6.0",
"mocha": "^5.2.0",
"npmignore": "^0.3.0",
"sinon": "^7.5.0",

@@ -93,7 +89,16 @@ "typescript": "^3.9.9",

"license": "MIT",
"greenkeeper": {
"publishConfig": {
"ignore": [
"semver"
".github/",
"!lib",
"docs/",
"test/",
"tests/",
"*.md",
"*.config.js",
".eslintrc",
".editorconfig",
"tsconfig.json"
]
}
}
`eslint-plugin-react`
===================
[![Maintenance Status][status-image]][status-url] [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][deps-image]][deps-url] [![Code Climate][climate-image]][climate-url] [![Tidelift][tidelift-image]][tidelift-url]
[![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]

@@ -10,14 +10,8 @@ React specific linting rules for `eslint`

Install [`eslint`](https://www.github.com/eslint/eslint) either locally or globally. (Note that locally, per project, is strongly preferred)
```sh
$ npm install eslint@7 --save-dev
$ npm install eslint eslint-plugin-react --save-dev
```
If you installed `eslint` globally, you have to install the React plugin globally too. Otherwise, install it locally (strongly preferred)
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.
```sh
$ npm install eslint-plugin-react --save-dev
```
# Configuration

@@ -35,3 +29,3 @@

If you are using the [new JSX transform from React 17](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#removing-unused-react-imports), extend [`react/jsx-runtime`](https://github.com/yannickcr/eslint-plugin-react/blob/c8917b0885094b5e4cc2a6f613f7fb6f16fe932e/index.js#L163-L176) in your eslint config (add `"plugin:react/jsx-runtime"` to `"extends"`) to disable the relevant rules.
If you are using the [new JSX transform from React 17](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#removing-unused-react-imports), extend [`react/jsx-runtime`](https://github.com/jsx-eslint/eslint-plugin-react/blob/c8917b0885094b5e4cc2a6f613f7fb6f16fe932e/index.js#L163-L176) in your eslint config (add `"plugin:react/jsx-runtime"` to `"extends"`) to disable the relevant rules.

@@ -128,3 +122,3 @@ You should also specify settings that will be shared across all the plugin rules. ([More about eslint shared settings](https://eslint.org/docs/user-guide/configuring/configuration-files#adding-shared-settings))

| | | [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/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 |

@@ -214,2 +208,3 @@ | | | [react/forbid-component-props](docs/rules/forbid-component-props.md) | Forbid certain props on components |

| ✔ | | [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 |

@@ -279,15 +274,12 @@ | | | [react/jsx-no-script-url](docs/rules/jsx-no-script-url.md) | Forbid `javascript:` URLs |

[travis-url]: https://travis-ci.org/yannickcr/eslint-plugin-react
[travis-image]: https://img.shields.io/travis/yannickcr/eslint-plugin-react/master.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
[deps-url]: https://david-dm.org/yannickcr/eslint-plugin-react
[deps-image]: https://img.shields.io/david/dev/yannickcr/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
[climate-url]: https://codeclimate.com/github/yannickcr/eslint-plugin-react
[climate-image]: https://img.shields.io/codeclimate/maintainability/yannickcr/eslint-plugin-react.svg
[status-url]: https://github.com/jsx-eslint/eslint-plugin-react/pulse
[status-image]: https://img.shields.io/github/last-commit/jsx-eslint/eslint-plugin-react.svg
[status-url]: https://github.com/yannickcr/eslint-plugin-react/pulse
[status-image]: https://img.shields.io/github/last-commit/yannickcr/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/yannickcr/eslint-plugin-react?style=flat
[tidelift-image]: https://tidelift.com/badges/github/jsx-eslint/eslint-plugin-react?style=flat
SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc