Skip to content

Commit

Permalink
[New] jsx-handler-names: add checkInlineFunction option
Browse files Browse the repository at this point in the history
  • Loading branch information
YONGJAE LEE authored and ljharb committed Aug 25, 2020
1 parent 20103c5 commit 9eb81bc
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,11 +7,13 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel

### Added
* [`button-has-type`]: support trivial ternary expressions ([#2748][] @Hypnosphi)
* [`jsx-handler-names`]: add `checkInlineFunction` option ([#2761][] @dididy)

### Fixed
* [`function-component-definition`]: ignore object properties ([#2771][] @stefan-wullems)

[#2771]: https://github.com/yannickcr/eslint-plugin-react/pull/2771
[#2761]: https://github.com/yannickcr/eslint-plugin-react/pull/2761
[#2748]: https://github.com/yannickcr/eslint-plugin-react/pull/2748

## [7.20.6] - 2020.08.12
Expand Down
4 changes: 3 additions & 1 deletion docs/rules/jsx-handler-names.md
Expand Up @@ -31,14 +31,16 @@ The following patterns are **not** considered warnings:
"react/jsx-handler-names": [<enabled>, {
"eventHandlerPrefix": <eventHandlerPrefix>,
"eventHandlerPropPrefix": <eventHandlerPropPrefix>,
"checkLocalVariables": <boolean>
"checkLocalVariables": <boolean>,
"checkInlineFunction": <boolean>
}]
...
```

* `eventHandlerPrefix`: Prefix for component methods used as event handlers. Defaults to `handle`
* `eventHandlerPropPrefix`: Prefix for props that are used as event handlers. Defaults to `on`
* `checkLocalVariables`: Determines whether event handlers stored as local variables are checked. Defaults to `false`
* `checkInlineFunction`: Determines whether event handlers set as inline functions are checked. Defaults to `false`

## When Not To Use It

Expand Down
38 changes: 33 additions & 5 deletions lib/rules/jsx-handler-names.js
Expand Up @@ -27,7 +27,8 @@ module.exports = {
properties: {
eventHandlerPrefix: {type: 'string'},
eventHandlerPropPrefix: {type: 'string'},
checkLocalVariables: {type: 'boolean'}
checkLocalVariables: {type: 'boolean'},
checkInlineFunction: {type: 'boolean'}
},
additionalProperties: false
}, {
Expand All @@ -38,7 +39,8 @@ module.exports = {
type: 'boolean',
enum: [false]
},
checkLocalVariables: {type: 'boolean'}
checkLocalVariables: {type: 'boolean'},
checkInlineFunction: {type: 'boolean'}
},
additionalProperties: false
}, {
Expand All @@ -49,7 +51,8 @@ module.exports = {
enum: [false]
},
eventHandlerPropPrefix: {type: 'string'},
checkLocalVariables: {type: 'boolean'}
checkLocalVariables: {type: 'boolean'},
checkInlineFunction: {type: 'boolean'}
},
additionalProperties: false
}, {
Expand All @@ -58,6 +61,12 @@ module.exports = {
checkLocalVariables: {type: 'boolean'}
},
additionalProperties: false
}, {
type: 'object',
properties: {
checkInlineFunction: {type: 'boolean'}
},
additionalProperties: false
}
]
}]
Expand All @@ -68,6 +77,10 @@ module.exports = {
return prefix === false;
}

function isInlineHandler(node) {
return node.value.expression.type === 'ArrowFunctionExpression';
}

const configuration = context.options[0] || {};

const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix)
Expand All @@ -86,14 +99,29 @@ module.exports = {

const checkLocal = !!configuration.checkLocalVariables;

const checkInlineFunction = !!configuration.checkInlineFunction;

return {
JSXAttribute(node) {
if (!node.value || !node.value.expression || (!checkLocal && !node.value.expression.object)) {
if (
!node.value
|| !node.value.expression
|| (
!checkLocal
&& (isInlineHandler(node)
? !node.value.expression.body.callee.object
: !node.value.expression.object
)
)
) {
return;
}

const propKey = typeof node.name === 'object' ? node.name.name : node.name;
const propValue = context.getSourceCode().getText(node.value.expression).replace(/^this\.|.*::/, '');
const expression = node.value.expression;
const propValue = context.getSourceCode()
.getText(checkInlineFunction && isInlineHandler(node) ? expression.body.callee : expression)
.replace(/^this\.|.*::/, '');

if (propKey === 'ref') {
return;
Expand Down
51 changes: 51 additions & 0 deletions tests/lib/rules/jsx-handler-names.js
Expand Up @@ -42,6 +42,17 @@ ruleTester.run('jsx-handler-names', rule, {
options: [{
checkLocalVariables: false
}]
}, {
code: '<TestComponent onChange={() => handleChange()} />',
options: [{
checkInlineFunction: true,
checkLocalVariables: true
}]
}, {
code: '<TestComponent onChange={() => this.handleChange()} />',
options: [{
checkInlineFunction: true
}]
}, {
code: '<TestComponent onChange={this.props.onFoo} />'
}, {
Expand Down Expand Up @@ -71,6 +82,24 @@ ruleTester.run('jsx-handler-names', rule, {
}, {
code: '<TestComponent onChange={props.foo::handleChange} />',
parser: parsers.BABEL_ESLINT
}, {
code: '<TestComponent onChange={() => props::handleChange()} />',
parser: parsers.BABEL_ESLINT,
options: [{
checkInlineFunction: true
}]
}, {
code: '<TestComponent onChange={() => ::props.onChange()} />',
parser: parsers.BABEL_ESLINT,
options: [{
checkInlineFunction: true
}]
}, {
code: '<TestComponent onChange={() => props.foo::handleChange()} />',
parser: parsers.BABEL_ESLINT,
options: [{
checkInlineFunction: true
}]
}, {
code: '<TestComponent only={this.only} />'
}, {
Expand Down Expand Up @@ -115,6 +144,12 @@ ruleTester.run('jsx-handler-names', rule, {
options: [{
checkLocalVariables: true
}]
}, {
code: '<TestComponent onChange={() => this.takeCareOfChange()} />',
errors: [{message: 'Handler function for onChange prop key must begin with \'handle\''}],
options: [{
checkInlineFunction: true
}]
}, {
code: '<TestComponent only={this.handleChange} />',
errors: [{message: 'Prop key for handleChange must begin with \'on\''}]
Expand All @@ -127,6 +162,13 @@ ruleTester.run('jsx-handler-names', rule, {
options: [{
checkLocalVariables: true
}]
}, {
code: '<TestComponent whenChange={() => handleChange()} />',
errors: [{message: 'Prop key for handleChange must begin with \'on\''}],
options: [{
checkInlineFunction: true,
checkLocalVariables: true
}]
}, {
code: '<TestComponent onChange={handleChange} />',
errors: [{message: 'Prop key for handleChange must begin with \'when\''}],
Expand All @@ -135,6 +177,15 @@ ruleTester.run('jsx-handler-names', rule, {
eventHandlerPrefix: 'handle',
eventHandlerPropPrefix: 'when'
}]
}, {
code: '<TestComponent onChange={() => handleChange()} />',
errors: [{message: 'Prop key for handleChange must begin with \'when\''}],
options: [{
checkInlineFunction: true,
checkLocalVariables: true,
eventHandlerPrefix: 'handle',
eventHandlerPropPrefix: 'when'
}]
}, {
code: '<TestComponent onChange={this.onChange} />',
errors: [{message: 'Handler function for onChange prop key must begin with \'handle\''}]
Expand Down

0 comments on commit 9eb81bc

Please sign in to comment.