Skip to content

Commit

Permalink
[Fix] no-this-in-sfc, component detection: Improve stateless compon…
Browse files Browse the repository at this point in the history
…ent detection

Fixes #3054.
  • Loading branch information
Wesitos authored and ljharb committed Aug 29, 2021
1 parent 261d93a commit 179550b
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 20 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,11 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel

## Unreleased

### Fixed
* [`no-this-in-sfc`], component detection: Improve stateless component detection ([#3056][] @Wesitos)

[#3056]: https://github.com/yannickcr/eslint-plugin-react/pull/3056

## [7.25.0] - 2021.08.27

### Added
Expand Down
51 changes: 33 additions & 18 deletions lib/util/Components.js
Expand Up @@ -608,6 +608,7 @@ function componentRule(rule, context) {
* @returns {ASTNode | undefined}
*/
getStatelessComponent(node) {
const parent = node.parent;
if (
node.type === 'FunctionDeclaration'
&& (!node.id || isFirstLetterCapitalized(node.id.name))
Expand All @@ -617,6 +618,13 @@ function componentRule(rule, context) {
}

if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
const isMethod = parent.type === 'Property' && parent.method;
const isPropertyAssignment = parent.type === 'AssignmentExpression'
&& parent.left.type === 'MemberExpression';
const isModuleExportsAssignment = isPropertyAssignment
&& parent.left.object.name === 'module'
&& parent.left.property.name === 'exports';

if (node.parent.type === 'ExportDefaultDeclaration') {
if (utils.isReturningJSX(node)) {
return node;
Expand All @@ -630,31 +638,38 @@ function componentRule(rule, context) {
}
return undefined;
}
if (utils.isInAllowedPositionForComponent(node) && utils.isReturningJSXOrNull(node)) {
if (utils.isParentComponentNotStatelessComponent(node)) return undefined;

const isMethod = node.parent.type === 'Property' && node.parent.method;
// Case like `React.memo(() => <></>)` or `React.forwardRef(...)`
const pragmaComponentWrapper = utils.getPragmaComponentWrapper(node);
if (pragmaComponentWrapper) {
return pragmaComponentWrapper;
}

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

if (node.id && isFirstLetterCapitalized(node.id.name)) {
return node;
}
if (utils.isParentComponentNotStatelessComponent(node)) {
return undefined;
}

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

return undefined;
if (node.id) {
return isFirstLetterCapitalized(node.id.name) ? node : undefined;
}

// Case like `React.memo(() => <></>)` or `React.forwardRef(...)`
const pragmaComponentWrapper = utils.getPragmaComponentWrapper(node);
if (pragmaComponentWrapper) {
return pragmaComponentWrapper;
if (
isPropertyAssignment
&& !isModuleExportsAssignment
&& !isFirstLetterCapitalized(parent.left.property.name)
) {
return undefined;
}

return node;
}

return undefined;
Expand Down Expand Up @@ -783,7 +798,7 @@ function componentRule(rule, context) {
},

isParentComponentNotStatelessComponent(node) {
return (
return !!(
node.parent
&& node.parent.key
&& node.parent.key.type === 'Identifier'
Expand Down
18 changes: 16 additions & 2 deletions tests/lib/rules/no-this-in-sfc.js
Expand Up @@ -23,7 +23,7 @@ const parserOptions = {

const ruleTester = new RuleTester({parserOptions});
ruleTester.run('no-this-in-sfc', rule, {
valid: [{
valid: [].concat({
code: `
function Foo(props) {
const { foo } = props;
Expand Down Expand Up @@ -141,7 +141,21 @@ ruleTester.run('no-this-in-sfc', rule, {
},
});
`
}],
}, {
code: `
obj.notAComponent = function () {
return this.a || null;
};`
}, parsers.TS([{
code: `
$.fn.getValueAsStringWeak = function (): string | null {
const val = this.length === 1 ? this.val() : null;
return typeof val === 'string' ? val : null;
};
`,
parser: parsers['@TYPESCRIPT_ESLINT']
}])),
invalid: [{
code: `
function Foo(props) {
Expand Down

0 comments on commit 179550b

Please sign in to comment.