prefer-await-to-callbacks.js 2.9 KB

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