mirror of
https://github.com/desktop/desktop
synced 2024-10-02 14:23:59 +00:00
port react-no-unbound-dispatcher-props rule to eslint, add new tests
This commit is contained in:
parent
329323ef44
commit
3c805f52c8
63
eslint-rules/react-no-unbound-dispatcher-props.js
vendored
Normal file
63
eslint-rules/react-no-unbound-dispatcher-props.js
vendored
Normal 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,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
36
eslint-rules/tests/react-no-unbound-dispatcher-props.test.js
Normal file
36
eslint-rules/tests/react-no-unbound-dispatcher-props.test.js
Normal 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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
Loading…
Reference in a new issue