port react-no-unbound-dispatcher-props rule to eslint, add new tests

This commit is contained in:
Brendan Forster 2021-07-04 13:38:21 -03:00
parent 329323ef44
commit 3c805f52c8
2 changed files with 99 additions and 0 deletions

View file

@ -0,0 +1,63 @@
// @ts-check
/**
* react-no-unbound-dispatcher-props
*
* This custom eslint rule is highly specific to GitHub Desktop and attempts
* to prevent errors caused by passing unbound dispatcher methods as callbacks
* to components.
*
* Example
*
* <Resizable onReset={this.props.dispatcher.resetSidebarWidth} />
*
* The example above will fail at runtime because the resetSidebarWidth method
* is invoked with the `this` context not set to the dispatcher instance. The
* solution is to wrap the dispatcher callback in a bound instance method and
* passing that to the component in question.
*
* private handleReset = () => { this.props.dispatcher.resetSidebarWidth }
* ...
* <Resizable onReset={this.handleReset} />
*/
/**
* @typedef {import('eslint').Rule.RuleModule} RuleModule
*/
/** @type {RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'Identify any potential misuse of the dispatcher inside components',
},
messages: {
unboundMethod:
'Use of unbound dispatcher method: {{ text }}. Consider extracting the method call to a bound instance method.',
},
fixable: 'code',
schema: [], // no options
},
create: function (context) {
const sourceCode = context.getSourceCode()
return {
JSXExpressionContainer(node) {
const text = sourceCode.getText(node)
if (/^\{this\.props\.dispatcher\./.test(text)) {
const textWithoutContainer = text.slice(1, text.length - 1)
context.report({
node: node,
messageId: 'unboundMethod',
data: {
text: textWithoutContainer,
},
})
}
},
}
},
}

View file

@ -0,0 +1,36 @@
// @ts-check
const RuleTester = require('eslint').RuleTester
const rule = require('../react-no-unbound-dispatcher-props')
const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
}
// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
const ruleTester = new RuleTester({ parserOptions })
ruleTester.run('react-no-unbound-dispatcher-props', rule, {
valid: [
'<Resizable onReset={() => { this.props.dispatcher.resetSidebarWidth }} />',
],
invalid: [
{
code: '<Resizable onReset={this.props.dispatcher.resetSidebarWidth} />',
errors: [
{
messageId: 'unboundMethod',
data: {
text: 'this.props.dispatcher.resetSidebarWidth',
},
},
],
},
],
})