needs-semicolon.js 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. 'use strict';
  2. // https://github.com/eslint/espree/blob/6b7d0b8100537dcd5c84a7fb17bbe28edcabe05d/lib/token-translator.js#L20
  3. const tokenTypesNeedsSemicolon = new Set([
  4. 'String',
  5. 'Null',
  6. 'Boolean',
  7. 'Numeric',
  8. 'RegularExpression',
  9. ]);
  10. const charactersMightNeedsSemicolon = new Set([
  11. '[',
  12. '(',
  13. '/',
  14. '`',
  15. '+',
  16. '-',
  17. '*',
  18. ',',
  19. '.',
  20. ]);
  21. /**
  22. Determines if a semicolon needs to be inserted before `code`, in order to avoid a SyntaxError.
  23. @param {Token} tokenBefore Token before `code`.
  24. @param {SourceCode} sourceCode
  25. @param {String} [code] Code text to determine.
  26. @returns {boolean} `true` if a semicolon needs to be inserted before `code`.
  27. */
  28. function needsSemicolon(tokenBefore, sourceCode, code) {
  29. if (
  30. code === ''
  31. || (code && !charactersMightNeedsSemicolon.has(code.charAt(0)))
  32. ) {
  33. return false;
  34. }
  35. if (!tokenBefore) {
  36. return false;
  37. }
  38. const {type, value, range} = tokenBefore;
  39. const lastBlockNode = sourceCode.getNodeByRangeIndex(range[0]);
  40. if (type === 'Punctuator') {
  41. if (value === ';') {
  42. return false;
  43. }
  44. if (value === ']') {
  45. return true;
  46. }
  47. if (value === ')') {
  48. switch (lastBlockNode.type) {
  49. case 'IfStatement': {
  50. if (sourceCode.getTokenBefore(lastBlockNode.consequent) === tokenBefore) {
  51. return false;
  52. }
  53. break;
  54. }
  55. case 'ForStatement':
  56. case 'ForInStatement':
  57. case 'ForOfStatement':
  58. case 'WhileStatement':
  59. case 'DoWhileStatement':
  60. case 'WithStatement': {
  61. if (lastBlockNode.body && sourceCode.getTokenBefore(lastBlockNode.body) === tokenBefore) {
  62. return false;
  63. }
  64. break;
  65. }
  66. // No default
  67. }
  68. return true;
  69. }
  70. }
  71. if (tokenTypesNeedsSemicolon.has(type)) {
  72. return true;
  73. }
  74. if (type === 'Template') {
  75. return value.endsWith('`');
  76. }
  77. if (lastBlockNode.type === 'ObjectExpression') {
  78. return true;
  79. }
  80. if (type === 'Identifier') {
  81. // `for...of`
  82. if (value === 'of' && lastBlockNode.type === 'ForOfStatement') {
  83. return false;
  84. }
  85. // `await`
  86. if (value === 'await' && lastBlockNode.type === 'AwaitExpression') {
  87. return false;
  88. }
  89. return true;
  90. }
  91. return false;
  92. }
  93. module.exports = needsSemicolon;