Skip to content

Commit

Permalink
feat(rule): add no-identity-handlers rule
Browse files Browse the repository at this point in the history
Resolves #12
  • Loading branch information
macklinu committed Feb 14, 2018
1 parent c0c662a commit af77254
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Enforce best practices for JavaScript promises.
* [`always-return`](#always-return)
* [`no-native`](#no-native)
* [`no-nesting`](#no-nesting)
* [`no-identity-handlers`](#no-identity-handlers)
* [`no-promise-in-callback`](#no-promise-in-callback)
* [`no-callback-in-promise`](#no-callback-in-promise)
* [`avoid-new`](#avoid-new)
Expand Down Expand Up @@ -70,6 +71,7 @@ Then configure the rules you want to use under the rules section.
"promise/catch-or-return": "error",
"promise/no-native": "off",
"promise/no-nesting": "warn",
"promise/no-identity-handlers": "warn",
"promise/no-promise-in-callback": "warn",
"promise/no-callback-in-promise": "warn",
"promise/avoid-new": "warn",
Expand All @@ -96,6 +98,7 @@ or start with the recommended rule set
| `always-return` | Return inside each `then()` to create readable and reusable Promise chains. | :bangbang: | |
| `no-native` | In an ES5 environment, make sure to create a `Promise` constructor before using. | | |
| `no-nesting` | Avoid nested `then()` or `catch()` statements | :warning: | |
| `no-identity-handlers` | Avoid unnecessary identity functions in `then()` or `catch()` | :warning: | |
| `no-promise-in-callback` | Avoid using promises inside of callbacks | :warning: | |
| `no-callback-in-promise` | Avoid calling `cb()` inside of a `then()` (use [nodeify][] instead) | :warning: | |
| `avoid-new` | Avoid creating `new` promises outside of utility libs (use [pify][] instead) | :warning: | |
Expand Down Expand Up @@ -297,6 +300,10 @@ var x = Promise.resolve('bad')

Avoid nested `then()` or `catch()` statements

### `no-identity-handlers`

Avoid unnecessary identity functions in `then()` or `catch()`

### `no-promise-in-callback`

Avoid using promises inside of callbacks
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'no-callback-in-promise': require('./rules/no-callback-in-promise'),
'no-promise-in-callback': require('./rules/no-promise-in-callback'),
'no-nesting': require('./rules/no-nesting'),
'no-identity-handlers': require('./rules/no-identity-handlers'),
'avoid-new': require('./rules/avoid-new'),
'no-return-in-finally': require('./rules/no-return-in-finally')
},
Expand All @@ -31,6 +32,7 @@ module.exports = {
'promise/catch-or-return': 'error',
'promise/no-native': 'off',
'promise/no-nesting': 'warn',
'promise/no-identity-handlers': 'warn',
'promise/no-promise-in-callback': 'warn',
'promise/no-callback-in-promise': 'warn',
'promise/avoid-new': 'warn',
Expand Down
76 changes: 76 additions & 0 deletions rules/no-identity-handlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict'

const isPromise = require('./lib/is-promise')

function isFunctionExpression(node) {
return (
node.type === 'FunctionExpression' ||
node.type === 'ArrowFunctionExpression'
)
}

function getFirstParamName(node) {
const firstParam = node.params[0]
return firstParam && firstParam.type === 'Identifier' ? firstParam.name : null
}

function getBodyValueName(node) {
const body = node.body || {}
if (body.type === 'Identifier') {
return body.name
}
if (body.type === 'BlockStatement') {
const firstBodyStatement = body.body[0] || { type: '', argument: {} }
return (firstBodyStatement.type === 'ReturnStatement' ||
firstBodyStatement.type === 'ThrowStatement') &&
firstBodyStatement.argument.type === 'Identifier'
? firstBodyStatement.argument.name
: null
}
return null
}

function isIdentityFunction(node) {
return node.params.length === 1
? getFirstParamName(node) === getBodyValueName(node)
: false
}

module.exports = {
meta: {
docs: {
url:
'https://github.com/xjamundx/eslint-plugin-promise#no-identity-handlers'
}
},
create(context) {
function checkIdentity(node) {
if (node && isFunctionExpression(node) && isIdentityFunction(node)) {
context.report({
node,
message: 'No identity handlers'
})
}
}

return {
CallExpression(node) {
if (!isPromise(node)) {
return
}

switch (node.callee.property.name) {
case 'then':
checkIdentity(node.arguments[0])
checkIdentity(node.arguments[1])
break
case 'catch':
checkIdentity(node.arguments[0])
break
default:
break
}
}
}
}
}
60 changes: 60 additions & 0 deletions test/no-identity-handlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use strict'

const rule = require('../rules/no-identity-handlers')
const RuleTester = require('eslint').RuleTester
const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 6
}
})

ruleTester.run('no-identity-handlers', rule, {
valid: [
'Promise.resolve(2).then(value => value * 2)',
'Promise.resolve(2).then(value => { return value + 1 })',
'Promise.resolve(2).then(() => null)',
'Promise.resolve(2).then(() => doSomethingTotallyDifferent())',
'somePromise().then(value => doSomethingWith(value))',
'somePromise().then(handler)',
'Promise.reject(Error()).catch(err => { console.error(err); throw err; })',
'Promise.reject(Error()).catch(err => { throw doSomethingTo(err) })',
'somePromise().catch(handler)',
'somePromise().catch(createHandler())',
'somePromise().then(value => value * 2, err => { console.error(err); throw err; })',
'somePromise().then(func, func)',

// edge cases that aren't really valid but shouldn't throw or report
'Promise.resolve(2).then()',
'Promise.reject(Error()).catch()'
],
invalid: [
{
code: 'Promise.resolve(2).then(_ => _)',
errors: [{ message: 'No identity handlers' }]
},
{
code: 'Promise.resolve(2).then(val => { return val })',
errors: [{ message: 'No identity handlers' }]
},
{
code: 'Promise.resolve(2).then(function (value) { return value })',
errors: [{ message: 'No identity handlers' }]
},
{
code: 'Promise.reject(Error()).catch(err => { throw err })',
errors: [{ message: 'No identity handlers' }]
},
{
code: 'Promise.reject(Error()).catch(function (e) { throw e })',
errors: [{ message: 'No identity handlers' }]
},
{
code: 'Promise.reject(Error()).then(null, error => { throw error })',
errors: [{ message: 'No identity handlers' }]
},
{
code: 'Promise.reject(Error()).then(null, function (e) { throw e })',
errors: [{ message: 'No identity handlers' }]
}
]
})

0 comments on commit af77254

Please sign in to comment.