prefer-await-to-callbacks.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. 'use strict'
  2. const getDocsUrl = require('./lib/get-docs-url')
  3. module.exports = {
  4. meta: {
  5. type: 'suggestion',
  6. docs: {
  7. url: getDocsUrl('prefer-await-to-callbacks'),
  8. },
  9. messages: {
  10. error: 'Avoid callbacks. Prefer Async/Await.',
  11. },
  12. },
  13. create(context) {
  14. function checkLastParamsForCallback(node) {
  15. const lastParam = node.params[node.params.length - 1] || {}
  16. if (lastParam.name === 'callback' || lastParam.name === 'cb') {
  17. context.report({ node: lastParam, messageId: 'error' })
  18. }
  19. }
  20. function isInsideYieldOrAwait() {
  21. return context.getAncestors().some((parent) => {
  22. return (
  23. parent.type === 'AwaitExpression' || parent.type === 'YieldExpression'
  24. )
  25. })
  26. }
  27. return {
  28. CallExpression(node) {
  29. // Callbacks aren't allowed.
  30. if (node.callee.name === 'cb' || node.callee.name === 'callback') {
  31. context.report({ node, messageId: 'error' })
  32. return
  33. }
  34. // Then-ables aren't allowed either.
  35. const args = node.arguments
  36. const lastArgIndex = args.length - 1
  37. const arg = lastArgIndex > -1 && node.arguments[lastArgIndex]
  38. if (
  39. (arg && arg.type === 'FunctionExpression') ||
  40. arg.type === 'ArrowFunctionExpression'
  41. ) {
  42. // Ignore event listener callbacks.
  43. if (
  44. node.callee.property &&
  45. (node.callee.property.name === 'on' ||
  46. node.callee.property.name === 'once')
  47. ) {
  48. return
  49. }
  50. // carve out exemption for map/filter/etc
  51. const arrayMethods = [
  52. 'map',
  53. 'every',
  54. 'forEach',
  55. 'some',
  56. 'find',
  57. 'filter',
  58. ]
  59. const isLodash =
  60. node.callee.object &&
  61. ['lodash', 'underscore', '_'].includes(node.callee.object.name)
  62. const callsArrayMethod =
  63. node.callee.property &&
  64. arrayMethods.includes(node.callee.property.name) &&
  65. (node.arguments.length === 1 ||
  66. (node.arguments.length === 2 && isLodash))
  67. const isArrayMethod =
  68. node.callee.name &&
  69. arrayMethods.includes(node.callee.name) &&
  70. node.arguments.length === 2
  71. if (callsArrayMethod || isArrayMethod) return
  72. // actually check for callbacks (I know this is the worst)
  73. if (
  74. arg.params &&
  75. arg.params[0] &&
  76. (arg.params[0].name === 'err' || arg.params[0].name === 'error')
  77. ) {
  78. if (!isInsideYieldOrAwait()) {
  79. context.report({ node: arg, messageId: 'error' })
  80. }
  81. }
  82. }
  83. },
  84. FunctionDeclaration: checkLastParamsForCallback,
  85. FunctionExpression: checkLastParamsForCallback,
  86. ArrowFunctionExpression: checkLastParamsForCallback,
  87. }
  88. },
  89. }