no-unnecessary-await.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. 'use strict';
  2. const {
  3. addParenthesizesToReturnOrThrowExpression,
  4. removeSpacesAfter,
  5. } = require('./fix/index.js');
  6. const {isParenthesized} = require('./utils/parentheses.js');
  7. const needsSemicolon = require('./utils/needs-semicolon.js');
  8. const isOnSameLine = require('./utils/is-on-same-line.js');
  9. const MESSAGE_ID = 'no-unnecessary-await';
  10. const messages = {
  11. [MESSAGE_ID]: 'Do not `await` non-promise value.',
  12. };
  13. function notPromise(node) {
  14. switch (node.type) {
  15. case 'ArrayExpression':
  16. case 'ArrowFunctionExpression':
  17. case 'AwaitExpression':
  18. case 'BinaryExpression':
  19. case 'ClassExpression':
  20. case 'FunctionExpression':
  21. case 'JSXElement':
  22. case 'JSXFragment':
  23. case 'Literal':
  24. case 'TemplateLiteral':
  25. case 'UnaryExpression':
  26. case 'UpdateExpression': {
  27. return true;
  28. }
  29. case 'SequenceExpression': {
  30. return notPromise(node.expressions[node.expressions.length - 1]);
  31. }
  32. // No default
  33. }
  34. return false;
  35. }
  36. /** @param {import('eslint').Rule.RuleContext} context */
  37. const create = context => ({
  38. AwaitExpression(node) {
  39. if (!notPromise(node.argument)) {
  40. return;
  41. }
  42. const sourceCode = context.getSourceCode();
  43. const awaitToken = sourceCode.getFirstToken(node);
  44. const problem = {
  45. node,
  46. loc: awaitToken.loc,
  47. messageId: MESSAGE_ID,
  48. };
  49. const valueNode = node.argument;
  50. if (
  51. // Removing `await` may change them to a declaration, if there is no `id` will cause SyntaxError
  52. valueNode.type === 'FunctionExpression'
  53. || valueNode.type === 'ClassExpression'
  54. // `+await +1` -> `++1`
  55. || (
  56. node.parent.type === 'UnaryExpression'
  57. && valueNode.type === 'UnaryExpression'
  58. && node.parent.operator === valueNode.operator
  59. )
  60. ) {
  61. return problem;
  62. }
  63. return Object.assign(problem, {
  64. /** @param {import('eslint').Rule.RuleFixer} fixer */
  65. * fix(fixer) {
  66. if (
  67. !isOnSameLine(awaitToken, valueNode)
  68. && !isParenthesized(node, sourceCode)
  69. ) {
  70. yield * addParenthesizesToReturnOrThrowExpression(fixer, node.parent, sourceCode);
  71. }
  72. yield fixer.remove(awaitToken);
  73. yield removeSpacesAfter(awaitToken, sourceCode, fixer);
  74. const nextToken = sourceCode.getTokenAfter(awaitToken);
  75. const tokenBefore = sourceCode.getTokenBefore(awaitToken);
  76. if (needsSemicolon(tokenBefore, sourceCode, nextToken.value)) {
  77. yield fixer.insertTextBefore(nextToken, ';');
  78. }
  79. },
  80. });
  81. },
  82. });
  83. /** @type {import('eslint').Rule.RuleModule} */
  84. module.exports = {
  85. create,
  86. meta: {
  87. type: 'suggestion',
  88. docs: {
  89. description: 'Disallow awaiting non-promise values.',
  90. },
  91. fixable: 'code',
  92. messages,
  93. },
  94. };