Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix false positives for display-name #2399

Merged
merged 1 commit into from
Sep 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions lib/rules/display-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ module.exports = {
const namedClass = (
(node.type === 'ClassDeclaration' || node.type === 'ClassExpression') &&
node.id &&
node.id.name
!!node.id.name
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the below addition of !! aren't strictly needed but I spotted every other variable in this block returns a boolean so I figured these should too

);

const namedFunctionDeclaration = (
(node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') &&
node.id &&
node.id.name
!!node.id.name
);

const namedFunctionExpression = (
Expand Down Expand Up @@ -202,6 +202,31 @@ module.exports = {
markDisplayNameAsDeclared(node);
},

CallExpression(node) {
if (!utils.isPragmaComponentWrapper(node)) {
return;
}

if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) {
// Skip over React.forwardRef declarations that are embeded within
// a React.memo i.e. React.memo(React.forwardRef(/* ... */))
// This means that we raise a single error for the call to React.memo
// instead of one for React.memo and one for React.forwardRef
const isWrappedInAnotherPragma = utils.getPragmaComponentWrapper(node);

if (
!isWrappedInAnotherPragma &&
(ignoreTranspilerName || !hasTranspilerName(node.arguments[0]))
) {
return;
}

if (components.get(node)) {
markDisplayNameAsDeclared(node);
}
}
},

'Program:exit': function () {
const list = components.list();
// Report missing display name for all components
Expand Down
104 changes: 104 additions & 0 deletions tests/lib/rules/display-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,22 @@ ruleTester.run('display-name', rule, {

export default React.memo(Component)
`
}, {
code: `
import React from 'react'

const ComponentWithMemo = React.memo(function Component({ world }) {
return <div>Hello {world}</div>
})
`
}, {
code: `
import React from 'react'

const ForwardRefComponentLike = React.forwardRef(function ComponentLike({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
`
}, {
code: `
function F() {
Expand Down Expand Up @@ -684,6 +700,94 @@ ruleTester.run('display-name', rule, {
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'

const ComponentWithMemo = React.memo(({ world }) => {
return <div>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'

const ComponentWithMemo = React.memo(function() {
return <div>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'

const ForwardRefComponentLike = React.forwardRef(({ world }, ref) => {
return <div ref={ref}>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from 'react'

const ForwardRefComponentLike = React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
// Only trigger an error for the outer React.memo
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(({ world }, ref) => {
return <div ref={ref}>Hello {world}</div>
})
)
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
// Only trigger an error for the outer React.memo
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
// React does not handle the result of forwardRef being passed into memo
// ComponentWithMemoAndForwardRef gets shown as Memo(Anonymous)
// See https://github.com/facebook/react/issues/16722
code: `
import React from 'react'

const MemoizedForwardRefComponentLike = React.memo(
React.forwardRef(function ComponentLike({ world }, ref) {
return <div ref={ref}>Hello {world}</div>
})
)
`,
errors: [{
message: 'Component definition is missing display name'
}]
}, {
code: `
import React from "react";
Expand Down