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]: Functional components first letter capitalization check #2699

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 20 additions & 4 deletions lib/util/Components.js
Expand Up @@ -70,6 +70,14 @@ function isReturnsLogicalJSX(node, property, strict) {
: (returnsLogicalJSXLeft || returnsLogicalJSXRight);
}

function isFirstLetterCapitalized(word) {
if (!word) {
return false;
}
const firstLetter = word.charAt(0);
return firstLetter.toUpperCase() === firstLetter;
}

const Lists = new WeakMap();

/**
Expand Down Expand Up @@ -624,13 +632,21 @@ function componentRule(rule, context) {
* @returns {ASTNode | undefined}
*/
getStatelessComponent(node) {
if (node.type === 'FunctionDeclaration') {
if (utils.isReturningJSXOrNull(node)) {
return node;
}
if (
node.type === 'FunctionDeclaration'
&& isFirstLetterCapitalized(node.id.name)
&& utils.isReturningJSXOrNull(node)
) {
return node;
}

if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
if (node.parent.type === 'VariableDeclarator' && utils.isReturningJSXOrNull(node)) {
if (isFirstLetterCapitalized(node.parent.id.name)) {
return node;
}
return undefined;
}
if (utils.isInAllowedPositionForComponent(node) && utils.isReturningJSXOrNull(node)) {
return node;
}
Expand Down
26 changes: 26 additions & 0 deletions tests/lib/rules/function-component-definition.js
Expand Up @@ -68,6 +68,32 @@ ruleTester.run('function-component-definition', rule, {
}, {
code: 'var Foo = React.memo(function Foo() { return <p/> })',
options: [{namedComponents: 'function-declaration'}]
}, {
// shouldn't trigger this rule since functions stating with a lowercase
// letter are not considered components
code: `
const selectAvatarByUserId = (state, id) => {
const user = selectUserById(state, id)
return null
}
`,
options: [{namedComponents: 'function-declaration'}]
}, {
// shouldn't trigger this rule since functions stating with a lowercase
// letter are not considered components
code: `
function ensureValidSourceType(sourceType: string) {
switch (sourceType) {
case 'ALBUM':
case 'PLAYLIST':
return sourceType;
default:
return null;
}
}
`,
options: [{namedComponents: 'arrow-function'}],
parser: parsers.TYPESCRIPT_ESLINT
}, {
code: 'function Hello(props: Test) { return <p/> }',
options: [{namedComponents: 'function-declaration'}],
Expand Down
22 changes: 21 additions & 1 deletion tests/lib/rules/prop-types.js
Expand Up @@ -2504,7 +2504,27 @@ ruleTester.run('prop-types', rule, {
}
`,
parser: parsers.TYPESCRIPT_ESLINT
}
},
// shouldn't trigger this rule since functions stating with a lowercase
// letter are not considered components
`
function noAComponent(props) {
return <div>{props.text}</div>
}
`,
// shouldn't trigger this rule for 'render' since functions stating with a lowercase
// letter are not considered components
`
const MyComponent = (props) => {
const render = () => {
return <test>{props.hello}</test>;
}
return render();
};
MyComponent.propTypes = {
hello: PropTypes.string.isRequired,
};
`
],

invalid: [
Expand Down