@ttsc/lint 0.13.1 → 0.14.0-dev.20260528

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/README.md +948 -69
  2. package/lib/defaultFormat.d.ts +9 -6
  3. package/lib/defaultFormat.js +9 -6
  4. package/lib/defaultFormat.js.map +1 -1
  5. package/lib/index.d.ts +46 -0
  6. package/lib/index.js +35 -10
  7. package/lib/index.js.map +1 -1
  8. package/lib/structures/ITtscLintConfig.d.ts +12 -6
  9. package/lib/structures/{ITtscLintFormatConfig.d.ts → ITtscLintFormat.d.ts} +2 -2
  10. package/lib/structures/{TtscLintRuleMap.js → ITtscLintFormat.js} +1 -1
  11. package/lib/structures/ITtscLintFormat.js.map +1 -0
  12. package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
  13. package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
  14. package/lib/structures/TtscLintRuleSetting.js.map +1 -0
  15. package/lib/structures/index.d.ts +3 -4
  16. package/lib/structures/index.js +3 -4
  17. package/lib/structures/index.js.map +1 -1
  18. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +104 -0
  19. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +14 -0
  20. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
  21. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +79 -0
  22. package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
  23. package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
  24. package/lib/structures/rules/ITtscLintContributorRules.d.ts +17 -0
  25. package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
  26. package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
  27. package/lib/structures/rules/ITtscLintCoreRules.d.ts +1257 -0
  28. package/lib/structures/{ITtscLintFormatConfig.js → rules/ITtscLintCoreRules.js} +1 -1
  29. package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
  30. package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
  31. package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
  32. package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
  33. package/lib/structures/rules/ITtscLintCypressRules.d.ts +129 -0
  34. package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
  35. package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
  36. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +157 -0
  37. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
  38. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
  39. package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +167 -0
  40. package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
  41. package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
  42. package/lib/structures/rules/ITtscLintJestRules.d.ts +201 -0
  43. package/lib/structures/rules/ITtscLintJestRules.js +3 -0
  44. package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
  45. package/lib/structures/rules/ITtscLintJsdocRules.d.ts +140 -0
  46. package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
  47. package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
  48. package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +302 -0
  49. package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
  50. package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
  51. package/lib/structures/rules/ITtscLintNextjsRules.d.ts +185 -0
  52. package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
  53. package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
  54. package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +330 -0
  55. package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
  56. package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
  57. package/lib/structures/rules/ITtscLintPromiseRules.d.ts +188 -0
  58. package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
  59. package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
  60. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
  61. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
  62. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
  63. package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +51 -0
  64. package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
  65. package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
  66. package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +39 -0
  67. package/lib/structures/rules/ITtscLintReactRuleOptions.js +12 -0
  68. package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
  69. package/lib/structures/rules/ITtscLintReactRules.d.ts +289 -0
  70. package/lib/structures/rules/ITtscLintReactRules.js +3 -0
  71. package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
  72. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +190 -0
  73. package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
  74. package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
  75. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +66 -0
  76. package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
  77. package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
  78. package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
  79. package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
  80. package/lib/structures/rules/ITtscLintRules.js.map +1 -0
  81. package/lib/structures/rules/ITtscLintSecurityRules.d.ts +148 -0
  82. package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
  83. package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
  84. package/lib/structures/rules/ITtscLintSolidRules.d.ts +181 -0
  85. package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
  86. package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
  87. package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +21 -0
  88. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
  89. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
  90. package/lib/structures/rules/ITtscLintStorybookRules.d.ts +174 -0
  91. package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
  92. package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
  93. package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +94 -0
  94. package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
  95. package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
  96. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +22 -0
  97. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
  98. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
  99. package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +310 -0
  100. package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
  101. package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
  102. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1132 -0
  103. package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
  104. package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
  105. package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
  106. package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
  107. package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
  108. package/lib/structures/rules/ITtscLintVitestRules.d.ts +141 -0
  109. package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
  110. package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
  111. package/lib/structures/rules/index.d.ts +31 -0
  112. package/lib/structures/rules/index.js +48 -0
  113. package/lib/structures/rules/index.js.map +1 -0
  114. package/linthost/ast_helpers.go +30 -5
  115. package/linthost/config.go +48 -17
  116. package/linthost/config_format.go +9 -9
  117. package/linthost/directives.go +67 -25
  118. package/linthost/dispatch.go +14 -3
  119. package/linthost/engine.go +152 -31
  120. package/linthost/fix.go +2 -2
  121. package/linthost/lsp.go +747 -0
  122. package/linthost/print_dispatch.go +2 -2
  123. package/linthost/print_nodes_function.go +2 -2
  124. package/linthost/print_nodes_object.go +1 -1
  125. package/linthost/rule_names.go +14 -0
  126. package/linthost/rules_arrays.go +2 -2
  127. package/linthost/rules_boundaries.go +532 -0
  128. package/linthost/rules_camelcase.go +133 -0
  129. package/linthost/rules_complexity.go +94 -0
  130. package/linthost/rules_consistent_return.go +130 -0
  131. package/linthost/rules_console.go +1 -1
  132. package/linthost/rules_core_extra.go +739 -0
  133. package/linthost/rules_curly.go +70 -0
  134. package/linthost/rules_cypress.go +608 -0
  135. package/linthost/rules_debugger.go +2 -2
  136. package/linthost/rules_default_case.go +39 -0
  137. package/linthost/rules_default_case_last.go +46 -0
  138. package/linthost/rules_dupes.go +3 -3
  139. package/linthost/rules_empty.go +3 -3
  140. package/linthost/rules_escape.go +44 -24
  141. package/linthost/rules_eval.go +2 -2
  142. package/linthost/rules_finally.go +2 -2
  143. package/linthost/rules_format_jsdoc.go +1 -1
  144. package/linthost/rules_format_print_width.go +9 -9
  145. package/linthost/rules_format_quotes.go +1 -1
  146. package/linthost/rules_format_semi.go +1 -1
  147. package/linthost/rules_format_sort_imports.go +1 -1
  148. package/linthost/rules_format_trailing_comma.go +1 -1
  149. package/linthost/rules_functional.go +967 -0
  150. package/linthost/rules_gap.go +24 -45
  151. package/linthost/rules_grouped_accessor_pairs.go +87 -0
  152. package/linthost/rules_guard_for_in.go +198 -0
  153. package/linthost/rules_id_length.go +77 -0
  154. package/linthost/rules_imports.go +70 -2
  155. package/linthost/rules_init_declarations.go +53 -0
  156. package/linthost/rules_jest.go +618 -0
  157. package/linthost/rules_jsdoc.go +502 -0
  158. package/linthost/rules_jsx_a11y.go +1264 -0
  159. package/linthost/rules_logic.go +12 -12
  160. package/linthost/rules_loops.go +1 -1
  161. package/linthost/rules_max_classes_per_file.go +58 -0
  162. package/linthost/rules_max_depth.go +83 -0
  163. package/linthost/rules_max_lines.go +101 -0
  164. package/linthost/rules_max_lines_per_function.go +57 -0
  165. package/linthost/rules_max_nested_callbacks.go +61 -0
  166. package/linthost/rules_max_params.go +49 -0
  167. package/linthost/rules_max_statements.go +59 -0
  168. package/linthost/rules_misc.go +1 -1
  169. package/linthost/rules_nextjs.go +697 -0
  170. package/linthost/rules_no_else_return.go +75 -0
  171. package/linthost/rules_no_empty_named_blocks.go +91 -0
  172. package/linthost/rules_no_extend_native.go +51 -0
  173. package/linthost/rules_no_import_assign.go +140 -0
  174. package/linthost/rules_no_invalid_this.go +52 -0
  175. package/linthost/rules_no_loop_func.go +85 -0
  176. package/linthost/rules_no_magic_numbers.go +123 -0
  177. package/linthost/rules_no_mixed_operators.go +80 -0
  178. package/linthost/rules_no_param_reassign.go +98 -0
  179. package/linthost/rules_no_redeclare.go +270 -0
  180. package/linthost/rules_no_restricted_imports.go +105 -0
  181. package/linthost/rules_no_restricted_syntax.go +49 -0
  182. package/linthost/rules_no_shadow.go +147 -0
  183. package/linthost/rules_no_unreachable.go +77 -0
  184. package/linthost/rules_no_useless_assignment.go +120 -0
  185. package/linthost/rules_no_useless_constructor.go +245 -0
  186. package/linthost/rules_no_useless_return.go +51 -0
  187. package/linthost/rules_params.go +1 -1
  188. package/linthost/rules_playwright.go +788 -0
  189. package/linthost/rules_prefer_arrow_callback.go +124 -0
  190. package/linthost/rules_prefer_destructuring.go +98 -0
  191. package/linthost/rules_prefer_named_capture_group.go +129 -0
  192. package/linthost/rules_prefer_rest_params.go +94 -0
  193. package/linthost/rules_problems.go +41 -30
  194. package/linthost/rules_promise.go +1128 -73
  195. package/linthost/rules_protos.go +2 -2
  196. package/linthost/rules_react.go +773 -0
  197. package/linthost/rules_react_extras.go +227 -0
  198. package/linthost/rules_react_hooks.go +848 -0
  199. package/linthost/rules_react_perf.go +293 -0
  200. package/linthost/rules_react_refresh.go +583 -0
  201. package/linthost/rules_regexp.go +410 -0
  202. package/linthost/rules_security.go +830 -0
  203. package/linthost/rules_self.go +2 -2
  204. package/linthost/rules_solid.go +1073 -0
  205. package/linthost/rules_sort_imports.go +69 -0
  206. package/linthost/rules_sort_keys.go +134 -0
  207. package/linthost/rules_storybook.go +1176 -0
  208. package/linthost/rules_strings.go +4 -4
  209. package/linthost/rules_suggestions.go +38 -38
  210. package/linthost/rules_tanstack_query.go +844 -0
  211. package/linthost/rules_testing_library.go +1204 -0
  212. package/linthost/rules_throw.go +1 -1
  213. package/linthost/rules_ts.go +19 -19
  214. package/linthost/rules_ts_async.go +399 -0
  215. package/linthost/rules_ts_class_literal_property_style.go +115 -0
  216. package/linthost/rules_ts_consistent_type_exports.go +256 -0
  217. package/linthost/rules_ts_explicit_function_return_type.go +67 -0
  218. package/linthost/rules_ts_extra.go +155 -36
  219. package/linthost/rules_ts_no_base_to_string.go +218 -0
  220. package/linthost/rules_ts_no_confusing_void.go +72 -0
  221. package/linthost/rules_ts_no_deprecated.go +195 -0
  222. package/linthost/rules_ts_no_for_in_array.go +84 -0
  223. package/linthost/rules_ts_no_magic_numbers.go +124 -0
  224. package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
  225. package/linthost/rules_ts_no_misused_spread.go +104 -0
  226. package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
  227. package/linthost/rules_ts_no_restricted_types.go +62 -0
  228. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
  229. package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
  230. package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
  231. package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
  232. package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
  233. package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
  234. package/linthost/rules_ts_no_unsafe_argument.go +82 -0
  235. package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
  236. package/linthost/rules_ts_no_unsafe_call.go +82 -0
  237. package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
  238. package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
  239. package/linthost/rules_ts_no_unsafe_return.go +126 -0
  240. package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
  241. package/linthost/rules_ts_no_useless_constructor.go +52 -0
  242. package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
  243. package/linthost/rules_ts_parameter_properties.go +38 -0
  244. package/linthost/rules_ts_prefer_find.go +178 -0
  245. package/linthost/rules_ts_prefer_includes.go +255 -0
  246. package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
  247. package/linthost/rules_ts_prefer_optional_chain.go +228 -0
  248. package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
  249. package/linthost/rules_ts_prefer_readonly.go +73 -0
  250. package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
  251. package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
  252. package/linthost/rules_ts_prefer_return_this_type.go +135 -0
  253. package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
  254. package/linthost/rules_ts_promise_function_async.go +77 -0
  255. package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
  256. package/linthost/rules_ts_require_array_sort_compare.go +96 -0
  257. package/linthost/rules_ts_restrict_plus_operands.go +127 -0
  258. package/linthost/rules_ts_restrict_template_expressions.go +95 -0
  259. package/linthost/rules_ts_simple.go +387 -0
  260. package/linthost/rules_ts_sort_type_constituents.go +144 -0
  261. package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
  262. package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
  263. package/linthost/rules_ts_unbound_method.go +126 -0
  264. package/linthost/rules_tsdoc.go +136 -0
  265. package/linthost/rules_unicorn_better_regex.go +27 -0
  266. package/linthost/rules_unicorn_catch_error_name.go +46 -0
  267. package/linthost/rules_unicorn_consistent_assert.go +47 -0
  268. package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
  269. package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
  270. package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
  271. package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
  272. package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
  273. package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
  274. package/linthost/rules_unicorn_custom_error_definition.go +138 -0
  275. package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
  276. package/linthost/rules_unicorn_error_message.go +64 -0
  277. package/linthost/rules_unicorn_escape_case.go +51 -0
  278. package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
  279. package/linthost/rules_unicorn_explicit_length_check.go +88 -0
  280. package/linthost/rules_unicorn_filename_case.go +29 -0
  281. package/linthost/rules_unicorn_import_style.go +28 -0
  282. package/linthost/rules_unicorn_isolated_functions.go +29 -0
  283. package/linthost/rules_unicorn_new_for_builtins.go +87 -0
  284. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
  285. package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
  286. package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
  287. package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
  288. package/linthost/rules_unicorn_no_array_for_each.go +40 -0
  289. package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
  290. package/linthost/rules_unicorn_no_array_reduce.go +41 -0
  291. package/linthost/rules_unicorn_no_array_reverse.go +44 -0
  292. package/linthost/rules_unicorn_no_array_sort.go +43 -0
  293. package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
  294. package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
  295. package/linthost/rules_unicorn_no_console_spaces.go +61 -0
  296. package/linthost/rules_unicorn_no_document_cookie.go +41 -0
  297. package/linthost/rules_unicorn_no_empty_file.go +44 -0
  298. package/linthost/rules_unicorn_no_for_loop.go +102 -0
  299. package/linthost/rules_unicorn_no_hex_escape.go +42 -0
  300. package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
  301. package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
  302. package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
  303. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
  304. package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
  305. package/linthost/rules_unicorn_no_lonely_if.go +44 -0
  306. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
  307. package/linthost/rules_unicorn_no_named_default.go +54 -0
  308. package/linthost/rules_unicorn_no_negated_condition.go +85 -0
  309. package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
  310. package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
  311. package/linthost/rules_unicorn_no_new_array.go +30 -0
  312. package/linthost/rules_unicorn_no_new_buffer.go +31 -0
  313. package/linthost/rules_unicorn_no_null.go +24 -0
  314. package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
  315. package/linthost/rules_unicorn_no_process_exit.go +43 -0
  316. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
  317. package/linthost/rules_unicorn_no_static_only_class.go +56 -0
  318. package/linthost/rules_unicorn_no_thenable.go +74 -0
  319. package/linthost/rules_unicorn_no_this_assignment.go +35 -0
  320. package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
  321. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
  322. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
  323. package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
  324. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
  325. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
  326. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
  327. package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
  328. package/linthost/rules_unicorn_no_unused_properties.go +30 -0
  329. package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
  330. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
  331. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
  332. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
  333. package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
  334. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
  335. package/linthost/rules_unicorn_no_useless_spread.go +67 -0
  336. package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
  337. package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
  338. package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
  339. package/linthost/rules_unicorn_number_literal_case.go +69 -0
  340. package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
  341. package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
  342. package/linthost/rules_unicorn_prefer_array_find.go +51 -0
  343. package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
  344. package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
  345. package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
  346. package/linthost/rules_unicorn_prefer_array_some.go +67 -0
  347. package/linthost/rules_unicorn_prefer_at.go +77 -0
  348. package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
  349. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
  350. package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
  351. package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
  352. package/linthost/rules_unicorn_prefer_code_point.go +49 -0
  353. package/linthost/rules_unicorn_prefer_date_now.go +78 -0
  354. package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
  355. package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
  356. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
  357. package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
  358. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
  359. package/linthost/rules_unicorn_prefer_event_target.go +36 -0
  360. package/linthost/rules_unicorn_prefer_export_from.go +129 -0
  361. package/linthost/rules_unicorn_prefer_global_this.go +127 -0
  362. package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
  363. package/linthost/rules_unicorn_prefer_includes.go +149 -0
  364. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
  365. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
  366. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
  367. package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
  368. package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
  369. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
  370. package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
  371. package/linthost/rules_unicorn_prefer_module.go +50 -0
  372. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
  373. package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
  374. package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
  375. package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
  376. package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
  377. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
  378. package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
  379. package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
  380. package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
  381. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
  382. package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
  383. package/linthost/rules_unicorn_prefer_set_has.go +42 -0
  384. package/linthost/rules_unicorn_prefer_set_size.go +48 -0
  385. package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
  386. package/linthost/rules_unicorn_prefer_single_call.go +96 -0
  387. package/linthost/rules_unicorn_prefer_spread.go +51 -0
  388. package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
  389. package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
  390. package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
  391. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
  392. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
  393. package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
  394. package/linthost/rules_unicorn_prefer_switch.go +103 -0
  395. package/linthost/rules_unicorn_prefer_ternary.go +71 -0
  396. package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
  397. package/linthost/rules_unicorn_prefer_type_error.go +127 -0
  398. package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
  399. package/linthost/rules_unicorn_relative_url_style.go +49 -0
  400. package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
  401. package/linthost/rules_unicorn_require_module_attributes.go +54 -0
  402. package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
  403. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
  404. package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
  405. package/linthost/rules_unicorn_string_content.go +27 -0
  406. package/linthost/rules_unicorn_switch_case_braces.go +42 -0
  407. package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
  408. package/linthost/rules_unicorn_template_indent.go +28 -0
  409. package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
  410. package/linthost/rules_unicorn_throw_new_error.go +61 -0
  411. package/linthost/rules_var.go +4 -4
  412. package/linthost/rules_vitest.go +568 -0
  413. package/package.json +3 -3
  414. package/plugin/main.go +49 -3
  415. package/rule/astutil/astutil.go +10 -3
  416. package/rule/rule.go +12 -10
  417. package/src/defaultFormat.ts +11 -8
  418. package/src/index.ts +40 -16
  419. package/src/structures/ITtscLintConfig.ts +12 -6
  420. package/src/structures/{ITtscLintFormatConfig.ts → ITtscLintFormat.ts} +2 -2
  421. package/src/structures/TtscLintRuleSetting.ts +55 -0
  422. package/src/structures/index.ts +3 -4
  423. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +128 -0
  424. package/src/structures/rules/ITtscLintBoundariesRules.ts +95 -0
  425. package/src/structures/rules/ITtscLintContributorRules.ts +21 -0
  426. package/src/structures/rules/ITtscLintCoreRules.ts +1406 -0
  427. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
  428. package/src/structures/rules/ITtscLintCypressRules.ts +145 -0
  429. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +213 -0
  430. package/src/structures/rules/ITtscLintFunctionalRules.ts +202 -0
  431. package/src/structures/rules/ITtscLintJestRules.ts +220 -0
  432. package/src/structures/rules/ITtscLintJsdocRules.ts +153 -0
  433. package/src/structures/rules/ITtscLintJsxA11yRules.ts +339 -0
  434. package/src/structures/rules/ITtscLintNextjsRules.ts +206 -0
  435. package/src/structures/rules/ITtscLintPlaywrightRules.ts +361 -0
  436. package/src/structures/rules/ITtscLintPromiseRules.ts +205 -0
  437. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
  438. package/src/structures/rules/ITtscLintReactPerfRules.ts +55 -0
  439. package/src/structures/rules/ITtscLintReactRuleOptions.ts +43 -0
  440. package/src/structures/rules/ITtscLintReactRules.ts +321 -0
  441. package/src/structures/rules/ITtscLintRegexpRules.ts +212 -0
  442. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +89 -0
  443. package/src/structures/rules/ITtscLintRules.ts +77 -0
  444. package/src/structures/rules/ITtscLintSecurityRules.ts +162 -0
  445. package/src/structures/rules/ITtscLintSolidRules.ts +202 -0
  446. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +23 -0
  447. package/src/structures/rules/ITtscLintStorybookRules.ts +193 -0
  448. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +102 -0
  449. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +24 -0
  450. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +342 -0
  451. package/src/structures/rules/ITtscLintTypeScriptRules.ts +1230 -0
  452. package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
  453. package/src/structures/rules/ITtscLintVitestRules.ts +154 -0
  454. package/src/structures/rules/index.ts +31 -0
  455. package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
  456. package/lib/structures/TtscLintRule.d.ts +0 -17
  457. package/lib/structures/TtscLintRule.js.map +0 -1
  458. package/lib/structures/TtscLintRuleMap.d.ts +0 -46
  459. package/lib/structures/TtscLintRuleMap.js.map +0 -1
  460. package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
  461. package/lib/structures/TtscLintRuleOptions.js.map +0 -1
  462. package/src/structures/TtscLintRule.ts +0 -160
  463. package/src/structures/TtscLintRuleMap.ts +0 -55
  464. package/src/structures/TtscLintRuleOptions.ts +0 -151
@@ -1,11 +1,13 @@
1
1
  package linthost
2
2
 
3
3
  import (
4
- shimast "github.com/microsoft/typescript-go/shim/ast"
5
- shimchecker "github.com/microsoft/typescript-go/shim/checker"
4
+ shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
6
6
  )
7
7
 
8
- // await-thenable: `await x` where `x` is not a Promise and not a
8
+ const promiseRulePrefix = "promise/"
9
+
10
+ // awaitThenable: `await x` where `x` is not a Promise and not a
9
11
  // thenable is always a no-op. The runtime resolves `await 42` to `42`
10
12
  // after one microtask hop — almost never the intent. typescript-eslint
11
13
  // recommended-type-checked:
@@ -18,44 +20,44 @@ import (
18
20
  // no shim addition.
19
21
  type awaitThenable struct{}
20
22
 
21
- func (awaitThenable) Name() string { return "await-thenable" }
23
+ func (awaitThenable) Name() string { return "typescript/await-thenable" }
22
24
  func (awaitThenable) NeedsTypeChecker() bool {
23
- return true
25
+ return true
24
26
  }
25
27
  func (awaitThenable) Visits() []shimast.Kind {
26
- return []shimast.Kind{shimast.KindAwaitExpression}
28
+ return []shimast.Kind{shimast.KindAwaitExpression}
27
29
  }
28
30
  func (awaitThenable) Check(ctx *Context, node *shimast.Node) {
29
- if ctx.Checker == nil {
30
- return
31
- }
32
- expr := node.AsAwaitExpression()
33
- if expr == nil || expr.Expression == nil {
34
- return
35
- }
36
- operandType := ctx.Checker.GetTypeAtLocation(expr.Expression)
37
- if operandType == nil {
38
- return
39
- }
40
- if isAwaitable(ctx.Checker, operandType) {
41
- return
42
- }
43
- message := "Unexpected `await` of a non-Promise (non-thenable) value."
44
- // Fix: drop the `await ` keyword and the following whitespace by
45
- // replacing [node.Pos(), expr.Expression.Pos()) with empty text.
46
- // `node.Pos()` may include leading trivia; use tokenRange to anchor
47
- // the start at the actual `await` token.
48
- startPos, _ := tokenRange(ctx.File, node)
49
- operandStart := expr.Expression.Pos()
50
- if startPos < 0 || operandStart <= startPos {
51
- ctx.Report(node, message)
52
- return
53
- }
54
- ctx.ReportFix(
55
- node,
56
- message,
57
- TextEdit{Pos: startPos, End: operandStart, Text: ""},
58
- )
31
+ if ctx.Checker == nil {
32
+ return
33
+ }
34
+ expr := node.AsAwaitExpression()
35
+ if expr == nil || expr.Expression == nil {
36
+ return
37
+ }
38
+ operandType := ctx.Checker.GetTypeAtLocation(expr.Expression)
39
+ if operandType == nil {
40
+ return
41
+ }
42
+ if isAwaitable(ctx.Checker, operandType) {
43
+ return
44
+ }
45
+ message := "Unexpected `await` of a non-Promise (non-thenable) value."
46
+ // Fix: drop the `await ` keyword and the following whitespace by
47
+ // replacing [node.Pos(), expr.Expression.Pos()) with empty text.
48
+ // `node.Pos()` may include leading trivia; use tokenRange to anchor
49
+ // the start at the actual `await` token.
50
+ startPos, _ := tokenRange(ctx.File, node)
51
+ operandStart := expr.Expression.Pos()
52
+ if startPos < 0 || operandStart <= startPos {
53
+ ctx.Report(node, message)
54
+ return
55
+ }
56
+ ctx.ReportFix(
57
+ node,
58
+ message,
59
+ TextEdit{Pos: startPos, End: operandStart, Text: ""},
60
+ )
59
61
  }
60
62
 
61
63
  // isAwaitable reports whether `t` is safe to `await`. A type is awaitable
@@ -77,50 +79,1103 @@ func (awaitThenable) Check(ctx *Context, node *shimast.Node) {
77
79
  // necessary because GetPromisedTypeOfPromise returns nil on composite types
78
80
  // like `Promise<X> | number` even though the expression can legally be awaited.
79
81
  func isAwaitable(checker *shimchecker.Checker, t *shimchecker.Type) bool {
80
- if checker == nil || t == nil {
81
- return false
82
- }
83
- flags := t.Flags()
84
- if flags&shimchecker.TypeFlagsAny != 0 ||
85
- flags&shimchecker.TypeFlagsUnknown != 0 ||
86
- flags&shimchecker.TypeFlagsNever != 0 {
87
- return true
88
- }
89
- if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
90
- for _, part := range t.Types() {
91
- if part == nil {
92
- continue
93
- }
94
- if isAwaitable(checker, part) {
95
- return true
96
- }
97
- }
98
- return false
99
- }
100
- if checker.GetPromisedTypeOfPromise(t) != nil {
101
- return true
102
- }
103
- return isThenableType(checker, t)
82
+ if checker == nil || t == nil {
83
+ return false
84
+ }
85
+ flags := t.Flags()
86
+ if flags&shimchecker.TypeFlagsAny != 0 ||
87
+ flags&shimchecker.TypeFlagsUnknown != 0 ||
88
+ flags&shimchecker.TypeFlagsNever != 0 {
89
+ return true
90
+ }
91
+ if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
92
+ for _, part := range t.Types() {
93
+ if part == nil {
94
+ continue
95
+ }
96
+ if isAwaitable(checker, part) {
97
+ return true
98
+ }
99
+ }
100
+ return false
101
+ }
102
+ if checker.GetPromisedTypeOfPromise(t) != nil {
103
+ return true
104
+ }
105
+ return isThenableType(checker, t)
104
106
  }
105
107
 
106
108
  // isThenableType reports whether t has a callable `then` property, which is
107
109
  // the runtime-observable contract for "thenable" in the ES spec. The check
108
110
  // intentionally mirrors what the JS engine uses at await-time.
109
111
  func isThenableType(checker *shimchecker.Checker, t *shimchecker.Type) bool {
110
- if checker == nil || t == nil {
111
- return false
112
- }
113
- prop := checker.GetPropertyOfType(t, "then")
114
- if prop == nil {
115
- return false
116
- }
117
- propType := checker.GetTypeOfSymbol(prop)
118
- if propType == nil {
119
- return false
120
- }
121
- return len(checker.GetSignaturesOfType(propType, shimchecker.SignatureKindCall)) > 0
112
+ if checker == nil || t == nil {
113
+ return false
114
+ }
115
+ prop := checker.GetPropertyOfType(t, "then")
116
+ if prop == nil {
117
+ return false
118
+ }
119
+ propType := checker.GetTypeOfSymbol(prop)
120
+ if propType == nil {
121
+ return false
122
+ }
123
+ return len(checker.GetSignaturesOfType(propType, shimchecker.SignatureKindCall)) > 0
124
+ }
125
+
126
+ // noFloatingPromises: an `ExpressionStatement` whose value is a Promise but
127
+ // is neither awaited, wrapped in `void`, nor terminated by a rejection-aware
128
+ // handler (`.catch`, `.finally`, or `.then(_, onRejected)`) silently swallows
129
+ // any rejection produced by the Promise. The Node runtime emits
130
+ // `unhandledRejection` only for the outermost discarded Promise, so a single
131
+ // missing handler cascades into hard-to-trace failures. typescript-eslint
132
+ // recommended-type-checked:
133
+ // https://typescript-eslint.io/rules/no-floating-promises/
134
+ //
135
+ // Only `ExpressionStatement` is visited: assignments, return values, and
136
+ // argument positions are never "floating" because the surrounding context
137
+ // captures the Promise. The handler set mirrors `await-thenable`'s shape so
138
+ // the Checker calls stay confined to one file.
139
+ type noFloatingPromises struct{}
140
+
141
+ func (noFloatingPromises) Name() string { return "typescript/no-floating-promises" }
142
+ func (noFloatingPromises) NeedsTypeChecker() bool {
143
+ return true
144
+ }
145
+ func (noFloatingPromises) Visits() []shimast.Kind {
146
+ return []shimast.Kind{shimast.KindExpressionStatement}
147
+ }
148
+ func (noFloatingPromises) Check(ctx *Context, node *shimast.Node) {
149
+ if ctx.Checker == nil {
150
+ return
151
+ }
152
+ stmt := node.AsExpressionStatement()
153
+ if stmt == nil || stmt.Expression == nil {
154
+ return
155
+ }
156
+ expr := stripParens(stmt.Expression)
157
+ if expr == nil {
158
+ return
159
+ }
160
+ // Syntactic discharges short-circuit the Checker call: any of these
161
+ // expression shapes already signals that the author opted out of the
162
+ // "discarded" reading, so the rule never needs to walk the type system.
163
+ if isPromiseHandledExpression(expr) {
164
+ return
165
+ }
166
+ t := ctx.Checker.GetTypeAtLocation(expr)
167
+ if t == nil {
168
+ return
169
+ }
170
+ if !isPromiseTypedExpression(ctx.Checker, t) {
171
+ return
172
+ }
173
+ ctx.Report(node, "Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator.")
174
+ }
175
+
176
+ // isPromiseHandledExpression reports whether the expression is syntactically
177
+ // "handled" — already awaited, marked `void`, or terminated by a rejection-aware
178
+ // promise method. The list of accepted methods matches typescript-eslint's
179
+ // no-floating-promises: `catch` and `finally` always defuse the chain, while
180
+ // `then` only defuses it when an explicit rejection handler is supplied as the
181
+ // second argument.
182
+ func isPromiseHandledExpression(expr *shimast.Node) bool {
183
+ if expr == nil {
184
+ return false
185
+ }
186
+ switch expr.Kind {
187
+ case shimast.KindAwaitExpression, shimast.KindVoidExpression:
188
+ return true
189
+ case shimast.KindCallExpression:
190
+ call := expr.AsCallExpression()
191
+ if call == nil {
192
+ return false
193
+ }
194
+ method, ok := promiseMethodCallName(call)
195
+ if !ok {
196
+ return false
197
+ }
198
+ switch method {
199
+ case "catch", "finally":
200
+ return true
201
+ case "then":
202
+ return call.Arguments != nil && len(call.Arguments.Nodes) >= 2
203
+ }
204
+ }
205
+ return false
206
+ }
207
+
208
+ // promiseMethodCallName returns the trailing property name when the call's
209
+ // callee is a property access (e.g. `getPromise().catch` -> "catch"). Unlike
210
+ // `promiseCallMethod`, it does NOT gate the result on whether the receiver
211
+ // looks like a Promise — the Checker handles that downstream when deciding
212
+ // whether the whole expression is still Promise-typed. The named-receiver
213
+ // constraint would otherwise reject legitimate handlers on user-typed
214
+ // objects such as `repo.users.findById(id).catch(reportError)`.
215
+ func promiseMethodCallName(call *shimast.CallExpression) (string, bool) {
216
+ if call == nil || call.Expression == nil {
217
+ return "", false
218
+ }
219
+ _, method, ok := promisePropertyAccessParts(call.Expression)
220
+ if !ok {
221
+ return "", false
222
+ }
223
+ return method, true
224
+ }
225
+
226
+ // isPromiseTypedExpression reports whether `t` represents a Promise (or thenable)
227
+ // at runtime. Mirrors `isAwaitable` but is intentionally stricter: `any` and
228
+ // `unknown` are NOT treated as floating, because that would explode on
229
+ // generic-helper boundaries; `never` is also skipped because a function whose
230
+ // return type is `never` cannot float a real Promise.
231
+ func isPromiseTypedExpression(checker *shimchecker.Checker, t *shimchecker.Type) bool {
232
+ if checker == nil || t == nil {
233
+ return false
234
+ }
235
+ flags := t.Flags()
236
+ if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
237
+ return false
238
+ }
239
+ if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
240
+ for _, part := range t.Types() {
241
+ if part == nil {
242
+ continue
243
+ }
244
+ if isPromiseTypedExpression(checker, part) {
245
+ return true
246
+ }
247
+ }
248
+ return false
249
+ }
250
+ if checker.GetPromisedTypeOfPromise(t) != nil {
251
+ return true
252
+ }
253
+ return isThenableType(checker, t)
254
+ }
255
+
256
+ // returnAwait: `return promise;` inside try/catch/finally lets the rejection
257
+ // escape the surrounding handler instead of being observed by it. Awaiting the
258
+ // promise first (`return await promise;`) keeps the rejection inside the
259
+ // async function's microtask queue, so the enclosing `catch` or `finally` can
260
+ // see it. typescript-eslint recommended-type-checked:
261
+ // https://typescript-eslint.io/rules/return-await/
262
+ //
263
+ // Trigger condition (walks up from the return statement and stops at the
264
+ // nearest function boundary):
265
+ //
266
+ // - the return is lexically inside a `try` block — fire;
267
+ // - the return is lexically inside a `finally` block — fire;
268
+ // - the return is lexically inside a `catch` block AND another
269
+ // try/catch/finally context wraps that catch — fire.
270
+ //
271
+ // Returning a Promise from outside any handler is fine: the caller observes
272
+ // the rejection through its own await. The rule is type-aware: it consults
273
+ // `ctx.Checker.GetTypeAtLocation` and `GetPromisedTypeOfPromise` to skip
274
+ // non-Promise returns.
275
+ type returnAwait struct{}
276
+
277
+ func (returnAwait) Name() string { return "typescript/return-await" }
278
+ func (returnAwait) NeedsTypeChecker() bool {
279
+ return true
280
+ }
281
+ func (returnAwait) Visits() []shimast.Kind {
282
+ return []shimast.Kind{shimast.KindReturnStatement}
283
+ }
284
+ func (returnAwait) Check(ctx *Context, node *shimast.Node) {
285
+ if ctx.Checker == nil {
286
+ return
287
+ }
288
+ ret := node.AsReturnStatement()
289
+ if ret == nil || ret.Expression == nil {
290
+ return
291
+ }
292
+ expr := stripParens(ret.Expression)
293
+ if expr == nil {
294
+ return
295
+ }
296
+ // Already `return await promise;` — author opted in.
297
+ if expr.Kind == shimast.KindAwaitExpression {
298
+ return
299
+ }
300
+ if !returnAwaitInsideHandler(node) {
301
+ return
302
+ }
303
+ t := ctx.Checker.GetTypeAtLocation(expr)
304
+ if t == nil {
305
+ return
306
+ }
307
+ if ctx.Checker.GetPromisedTypeOfPromise(t) == nil {
308
+ return
309
+ }
310
+ ctx.Report(node, "Returning a Promise from a try/catch/finally block requires `await` so the surrounding handler observes the rejection.")
311
+ }
312
+
313
+ // returnAwaitInsideHandler walks the parent chain from `node` upward, stops at
314
+ // the nearest function boundary, and reports whether the return statement sits
315
+ // inside a try block, a finally block, or a catch block that is itself wrapped
316
+ // by another try/catch/finally context.
317
+ //
318
+ // The function boundary stop matches `walkToFinally` in rules_finally: a return
319
+ // inside a nested function targets that inner function, so it cannot escape
320
+ // the outer try/catch/finally and must not trip the rule.
321
+ func returnAwaitInsideHandler(node *shimast.Node) bool {
322
+ cur := node.Parent
323
+ for cur != nil {
324
+ if isFunctionLikeKind(cur) || cur.Kind == shimast.KindSourceFile {
325
+ return false
326
+ }
327
+ if cur.Kind == shimast.KindBlock {
328
+ grand := cur.Parent
329
+ if grand != nil && grand.Kind == shimast.KindTryStatement {
330
+ try := grand.AsTryStatement()
331
+ if try != nil {
332
+ if try.TryBlock == cur {
333
+ return true
334
+ }
335
+ if try.FinallyBlock == cur {
336
+ return true
337
+ }
338
+ }
339
+ }
340
+ }
341
+ if cur.Kind == shimast.KindCatchClause {
342
+ // Inside a catch: only fires when another try/catch/finally
343
+ // wraps this one so the rejection can be re-observed.
344
+ if hasEnclosingTryContext(cur.Parent) {
345
+ return true
346
+ }
347
+ }
348
+ cur = cur.Parent
349
+ }
350
+ return false
351
+ }
352
+
353
+ // hasEnclosingTryContext walks upward from `node` and reports whether the path
354
+ // to the nearest function boundary crosses any try block, catch clause, or
355
+ // finally block. Used by the catch-clause arm of returnAwaitInsideHandler to
356
+ // decide whether the catch is itself wrapped by another handler.
357
+ func hasEnclosingTryContext(node *shimast.Node) bool {
358
+ for cur := node; cur != nil; cur = cur.Parent {
359
+ if isFunctionLikeKind(cur) || cur.Kind == shimast.KindSourceFile {
360
+ return false
361
+ }
362
+ if cur.Kind == shimast.KindBlock {
363
+ grand := cur.Parent
364
+ if grand != nil && grand.Kind == shimast.KindTryStatement {
365
+ try := grand.AsTryStatement()
366
+ if try != nil && (try.TryBlock == cur || try.FinallyBlock == cur) {
367
+ return true
368
+ }
369
+ }
370
+ }
371
+ if cur.Kind == shimast.KindCatchClause {
372
+ return true
373
+ }
374
+ }
375
+ return false
376
+ }
377
+
378
+ // promise/param-names mirrors eslint-plugin-promise's constructor convention:
379
+ // inline executors should name their parameters resolve/reject (allowing a
380
+ // leading underscore for intentionally unused parameters).
381
+ type promiseParamNames struct{}
382
+
383
+ func (promiseParamNames) Name() string { return promiseRulePrefix + "param-names" }
384
+ func (promiseParamNames) Visits() []shimast.Kind {
385
+ return []shimast.Kind{shimast.KindNewExpression}
386
+ }
387
+ func (promiseParamNames) Check(ctx *Context, node *shimast.Node) {
388
+ executor := promiseExecutor(node)
389
+ if executor == nil {
390
+ return
391
+ }
392
+ params := executor.Parameters()
393
+ if len(params) > 0 {
394
+ name := parameterIdentifierName(params[0])
395
+ if name != "" && name != "resolve" && name != "_resolve" {
396
+ ctx.Report(params[0], "Promise constructor first parameter should be named resolve.")
397
+ }
398
+ }
399
+ if len(params) > 1 {
400
+ name := parameterIdentifierName(params[1])
401
+ if name != "" && name != "reject" && name != "_reject" {
402
+ ctx.Report(params[1], "Promise constructor second parameter should be named reject.")
403
+ }
404
+ }
405
+ }
406
+
407
+ type promiseAvoidNew struct{}
408
+
409
+ func (promiseAvoidNew) Name() string { return promiseRulePrefix + "avoid-new" }
410
+ func (promiseAvoidNew) Visits() []shimast.Kind {
411
+ return []shimast.Kind{shimast.KindNewExpression}
412
+ }
413
+ func (promiseAvoidNew) Check(ctx *Context, node *shimast.Node) {
414
+ ne := node.AsNewExpression()
415
+ if ne != nil && identifierText(ne.Expression) == "Promise" {
416
+ ctx.Report(node, "Avoid creating new promises directly.")
417
+ }
418
+ }
419
+
420
+ type promiseNoNewStatics struct{}
421
+
422
+ func (promiseNoNewStatics) Name() string { return promiseRulePrefix + "no-new-statics" }
423
+ func (promiseNoNewStatics) Visits() []shimast.Kind {
424
+ return []shimast.Kind{shimast.KindNewExpression}
425
+ }
426
+ func (promiseNoNewStatics) Check(ctx *Context, node *shimast.Node) {
427
+ ne := node.AsNewExpression()
428
+ if ne == nil {
429
+ return
430
+ }
431
+ method, ok := promiseStaticMethod(ne.Expression)
432
+ if ok {
433
+ ctx.Report(node, "Avoid calling new on Promise."+method+"().")
434
+ }
435
+ }
436
+
437
+ type promiseValidParams struct{}
438
+
439
+ func (promiseValidParams) Name() string { return promiseRulePrefix + "valid-params" }
440
+ func (promiseValidParams) Visits() []shimast.Kind {
441
+ return []shimast.Kind{shimast.KindCallExpression}
442
+ }
443
+ func (promiseValidParams) Check(ctx *Context, node *shimast.Node) {
444
+ call := node.AsCallExpression()
445
+ if call == nil || call.Arguments == nil {
446
+ return
447
+ }
448
+ _, method, ok := promiseCallMethod(call)
449
+ if !ok {
450
+ return
451
+ }
452
+ count := len(call.Arguments.Nodes)
453
+ switch method {
454
+ case "resolve", "reject":
455
+ if count > 1 {
456
+ ctx.Report(node, "Promise."+method+"() accepts at most one argument.")
457
+ }
458
+ case "then":
459
+ if count < 1 || count > 2 {
460
+ ctx.Report(node, "Promise then() expects one or two arguments.")
461
+ }
462
+ case "race", "all", "allSettled", "any", "catch", "finally":
463
+ if count != 1 {
464
+ ctx.Report(node, "Promise "+method+"() expects exactly one argument.")
465
+ }
466
+ }
467
+ }
468
+
469
+ type promiseSpecOnly struct{}
470
+
471
+ func (promiseSpecOnly) Name() string { return promiseRulePrefix + "spec-only" }
472
+ func (promiseSpecOnly) Visits() []shimast.Kind {
473
+ return []shimast.Kind{shimast.KindPropertyAccessExpression}
474
+ }
475
+ func (promiseSpecOnly) Check(ctx *Context, node *shimast.Node) {
476
+ obj, prop, ok := promisePropertyAccessParts(node)
477
+ if !ok {
478
+ return
479
+ }
480
+ if identifierText(obj) == "Promise" {
481
+ if prop == "prototype" {
482
+ return
483
+ }
484
+ if !isPromiseStaticMethod(prop) {
485
+ ctx.Report(node, "Avoid using non-standard Promise."+prop+".")
486
+ }
487
+ return
488
+ }
489
+ base, baseProp, baseOK := promisePropertyAccessParts(obj)
490
+ if baseOK && identifierText(base) == "Promise" && baseProp == "prototype" && !isPromiseInstanceMethod(prop) {
491
+ ctx.Report(node, "Avoid using non-standard Promise.prototype."+prop+".")
492
+ }
493
+ }
494
+
495
+ type promiseNoNative struct{}
496
+
497
+ func (promiseNoNative) Name() string { return promiseRulePrefix + "no-native" }
498
+ func (promiseNoNative) Visits() []shimast.Kind {
499
+ return []shimast.Kind{shimast.KindSourceFile}
500
+ }
501
+ func (promiseNoNative) Check(ctx *Context, node *shimast.Node) {
502
+ if ctx.File == nil || fileDeclaresPromise(node) {
503
+ return
504
+ }
505
+ reported := false
506
+ walkDescendants(node, func(child *shimast.Node) {
507
+ if reported || child == nil {
508
+ return
509
+ }
510
+ switch child.Kind {
511
+ case shimast.KindNewExpression:
512
+ ne := child.AsNewExpression()
513
+ if ne != nil && identifierText(ne.Expression) == "Promise" {
514
+ reported = true
515
+ ctx.Report(ne.Expression, "\"Promise\" is not defined in ES5 environments.")
516
+ }
517
+ case shimast.KindPropertyAccessExpression:
518
+ obj, _, ok := promisePropertyAccessParts(child)
519
+ if ok && identifierText(obj) == "Promise" {
520
+ reported = true
521
+ ctx.Report(obj, "\"Promise\" is not defined in ES5 environments.")
522
+ }
523
+ }
524
+ })
525
+ }
526
+
527
+ type promisePreferAwaitToThen struct{}
528
+
529
+ func (promisePreferAwaitToThen) Name() string { return promiseRulePrefix + "prefer-await-to-then" }
530
+ func (promisePreferAwaitToThen) Visits() []shimast.Kind {
531
+ return []shimast.Kind{shimast.KindCallExpression}
532
+ }
533
+ func (promisePreferAwaitToThen) Check(ctx *Context, node *shimast.Node) {
534
+ call := node.AsCallExpression()
535
+ _, method, ok := promiseCallMethod(call)
536
+ if ok && isPromiseInstanceMethod(method) {
537
+ ctx.Report(node, "Prefer async/await to promise "+method+"() chains.")
538
+ }
539
+ }
540
+
541
+ type promisePreferCatch struct{}
542
+
543
+ func (promisePreferCatch) Name() string { return promiseRulePrefix + "prefer-catch" }
544
+ func (promisePreferCatch) Visits() []shimast.Kind {
545
+ return []shimast.Kind{shimast.KindCallExpression}
546
+ }
547
+ func (promisePreferCatch) Check(ctx *Context, node *shimast.Node) {
548
+ call := node.AsCallExpression()
549
+ _, method, ok := promiseCallMethod(call)
550
+ if !ok || method != "then" || call.Arguments == nil || len(call.Arguments.Nodes) < 2 {
551
+ return
552
+ }
553
+ ctx.Report(call.Arguments.Nodes[1], "Prefer catch() to passing a rejection handler to then().")
554
+ }
555
+
556
+ type promiseCatchOrReturn struct{}
557
+
558
+ func (promiseCatchOrReturn) Name() string { return promiseRulePrefix + "catch-or-return" }
559
+ func (promiseCatchOrReturn) Visits() []shimast.Kind {
560
+ return []shimast.Kind{shimast.KindExpressionStatement}
561
+ }
562
+ func (promiseCatchOrReturn) Check(ctx *Context, node *shimast.Node) {
563
+ stmt := node.AsExpressionStatement()
564
+ if stmt == nil || stmt.Expression == nil {
565
+ return
566
+ }
567
+ if promiseChainHasMethod(stmt.Expression, "then") && !promiseChainHasMethod(stmt.Expression, "catch") {
568
+ ctx.Report(node, "Promise chains should be returned or terminated with catch().")
569
+ }
570
+ }
571
+
572
+ type promiseAlwaysReturn struct{}
573
+
574
+ func (promiseAlwaysReturn) Name() string { return promiseRulePrefix + "always-return" }
575
+ func (promiseAlwaysReturn) Visits() []shimast.Kind {
576
+ return []shimast.Kind{shimast.KindCallExpression}
577
+ }
578
+ func (promiseAlwaysReturn) Check(ctx *Context, node *shimast.Node) {
579
+ call := node.AsCallExpression()
580
+ _, method, ok := promiseCallMethod(call)
581
+ if !ok || method != "then" || call.Arguments == nil || len(call.Arguments.Nodes) == 0 {
582
+ return
583
+ }
584
+ callback := stripParens(call.Arguments.Nodes[0])
585
+ if callback == nil || !isFunctionLikeKind(callback) {
586
+ return
587
+ }
588
+ body := callback.Body()
589
+ if body == nil || body.Kind != shimast.KindBlock {
590
+ return
591
+ }
592
+ if !blockReturnsOrThrows(body) {
593
+ ctx.Report(callback, "Each then() callback should return a value or throw.")
594
+ }
595
+ }
596
+
597
+ type promiseNoReturnWrap struct{}
598
+
599
+ func (promiseNoReturnWrap) Name() string { return promiseRulePrefix + "no-return-wrap" }
600
+ func (promiseNoReturnWrap) Visits() []shimast.Kind {
601
+ return []shimast.Kind{shimast.KindReturnStatement, shimast.KindArrowFunction}
602
+ }
603
+ func (promiseNoReturnWrap) Check(ctx *Context, node *shimast.Node) {
604
+ switch node.Kind {
605
+ case shimast.KindReturnStatement:
606
+ ret := node.AsReturnStatement()
607
+ if ret == nil || ret.Expression == nil || !isInsidePromiseCallbackFunction(node) {
608
+ return
609
+ }
610
+ if method, ok := promiseResolveRejectCall(ret.Expression); ok {
611
+ ctx.Report(ret.Expression, "Avoid wrapping return values in Promise."+method+"().")
612
+ }
613
+ case shimast.KindArrowFunction:
614
+ arrow := node.AsArrowFunction()
615
+ if arrow == nil || arrow.Body == nil || arrow.Body.Kind == shimast.KindBlock || !isPromiseCallbackFunction(node) {
616
+ return
617
+ }
618
+ if method, ok := promiseResolveRejectCall(arrow.Body); ok {
619
+ ctx.Report(arrow.Body, "Avoid wrapping return values in Promise."+method+"().")
620
+ }
621
+ }
622
+ }
623
+
624
+ type promiseNoReturnInFinally struct{}
625
+
626
+ func (promiseNoReturnInFinally) Name() string { return promiseRulePrefix + "no-return-in-finally" }
627
+ func (promiseNoReturnInFinally) Visits() []shimast.Kind {
628
+ return []shimast.Kind{shimast.KindReturnStatement}
629
+ }
630
+ func (promiseNoReturnInFinally) Check(ctx *Context, node *shimast.Node) {
631
+ if fn := nearestFunctionLike(node); fn != nil && isPromiseCallbackFunctionFor(fn, "finally") {
632
+ ctx.Report(node, "Do not return from a Promise finally() callback.")
633
+ }
634
+ }
635
+
636
+ type promiseNoNesting struct{}
637
+
638
+ func (promiseNoNesting) Name() string { return promiseRulePrefix + "no-nesting" }
639
+ func (promiseNoNesting) Visits() []shimast.Kind {
640
+ return []shimast.Kind{shimast.KindCallExpression}
641
+ }
642
+ func (promiseNoNesting) Check(ctx *Context, node *shimast.Node) {
643
+ call := node.AsCallExpression()
644
+ _, method, ok := promiseCallMethod(call)
645
+ if !ok || (method != "then" && method != "catch") {
646
+ return
647
+ }
648
+ if fn := nearestFunctionLike(node); fn != nil && isPromiseCallbackFunction(fn) {
649
+ ctx.Report(node, "Avoid nesting promise callbacks.")
650
+ }
651
+ }
652
+
653
+ type promiseNoCallbackInPromise struct{}
654
+
655
+ func (promiseNoCallbackInPromise) Name() string { return promiseRulePrefix + "no-callback-in-promise" }
656
+ func (promiseNoCallbackInPromise) Visits() []shimast.Kind {
657
+ return []shimast.Kind{shimast.KindCallExpression}
658
+ }
659
+ func (promiseNoCallbackInPromise) Check(ctx *Context, node *shimast.Node) {
660
+ call := node.AsCallExpression()
661
+ if call == nil {
662
+ return
663
+ }
664
+ if isCallbackCall(call) {
665
+ if fn := nearestFunctionLike(node); fn != nil && isPromiseCallbackFunction(fn) {
666
+ ctx.Report(node, "Avoid calling callbacks inside promise callbacks.")
667
+ }
668
+ return
669
+ }
670
+ _, method, ok := promiseCallMethod(call)
671
+ if !ok || (method != "then" && method != "catch") || call.Arguments == nil || len(call.Arguments.Nodes) == 0 {
672
+ return
673
+ }
674
+ first := stripParens(call.Arguments.Nodes[0])
675
+ if first != nil && first.Kind == shimast.KindIdentifier && isCallbackName(identifierText(first)) {
676
+ ctx.Report(first, "Avoid passing callbacks into promise chains.")
677
+ }
678
+ }
679
+
680
+ type promiseNoPromiseInCallback struct{}
681
+
682
+ func (promiseNoPromiseInCallback) Name() string { return promiseRulePrefix + "no-promise-in-callback" }
683
+ func (promiseNoPromiseInCallback) Visits() []shimast.Kind {
684
+ return []shimast.Kind{shimast.KindCallExpression}
685
+ }
686
+ func (promiseNoPromiseInCallback) Check(ctx *Context, node *shimast.Node) {
687
+ call := node.AsCallExpression()
688
+ if call == nil || !isPromiseLikeCall(call) || isDirectReturnValue(node) {
689
+ return
690
+ }
691
+ if fn := nearestFunctionLike(node); fn != nil && isErrorFirstCallback(fn) {
692
+ ctx.Report(call.Expression, "Avoid using promises inside callbacks.")
693
+ }
694
+ }
695
+
696
+ type promisePreferAwaitToCallbacks struct{}
697
+
698
+ func (promisePreferAwaitToCallbacks) Name() string {
699
+ return promiseRulePrefix + "prefer-await-to-callbacks"
700
+ }
701
+ func (promisePreferAwaitToCallbacks) Visits() []shimast.Kind {
702
+ return []shimast.Kind{
703
+ shimast.KindCallExpression,
704
+ shimast.KindFunctionDeclaration,
705
+ shimast.KindFunctionExpression,
706
+ shimast.KindArrowFunction,
707
+ }
708
+ }
709
+ func (promisePreferAwaitToCallbacks) Check(ctx *Context, node *shimast.Node) {
710
+ switch node.Kind {
711
+ case shimast.KindCallExpression:
712
+ call := node.AsCallExpression()
713
+ if call == nil {
714
+ return
715
+ }
716
+ if isCallbackCall(call) {
717
+ ctx.Report(node, "Avoid callbacks. Prefer async/await.")
718
+ return
719
+ }
720
+ if hasErrorFirstCallbackArgument(call) && !isInsideAwaitOrYield(node) {
721
+ ctx.Report(node, "Avoid callbacks. Prefer async/await.")
722
+ }
723
+ default:
724
+ params := node.Parameters()
725
+ if len(params) == 0 {
726
+ return
727
+ }
728
+ lastName := parameterIdentifierName(params[len(params)-1])
729
+ if lastName == "callback" || lastName == "cb" {
730
+ ctx.Report(params[len(params)-1], "Avoid callbacks. Prefer async/await.")
731
+ }
732
+ }
733
+ }
734
+
735
+ type promiseNoMultipleResolved struct{}
736
+
737
+ func (promiseNoMultipleResolved) Name() string { return promiseRulePrefix + "no-multiple-resolved" }
738
+ func (promiseNoMultipleResolved) Visits() []shimast.Kind {
739
+ return []shimast.Kind{shimast.KindNewExpression}
740
+ }
741
+ func (promiseNoMultipleResolved) Check(ctx *Context, node *shimast.Node) {
742
+ executor := promiseExecutor(node)
743
+ if executor == nil {
744
+ return
745
+ }
746
+ params := executor.Parameters()
747
+ resolverNames := map[string]bool{}
748
+ for i := 0; i < len(params) && i < 2; i++ {
749
+ if name := parameterIdentifierName(params[i]); name != "" {
750
+ resolverNames[name] = true
751
+ }
752
+ }
753
+ if len(resolverNames) == 0 {
754
+ return
755
+ }
756
+ count := 0
757
+ walkFunctionBody(executor, func(child *shimast.Node) {
758
+ if child == nil || child.Kind != shimast.KindCallExpression {
759
+ return
760
+ }
761
+ call := child.AsCallExpression()
762
+ if call == nil || !resolverNames[callCalleeName(call)] {
763
+ return
764
+ }
765
+ count++
766
+ if count > 1 {
767
+ ctx.Report(child, "Promise should not be resolved multiple times.")
768
+ }
769
+ })
770
+ }
771
+
772
+ func promiseExecutor(node *shimast.Node) *shimast.Node {
773
+ ne := node.AsNewExpression()
774
+ if ne == nil || identifierText(ne.Expression) != "Promise" || ne.Arguments == nil || len(ne.Arguments.Nodes) == 0 {
775
+ return nil
776
+ }
777
+ executor := stripParens(ne.Arguments.Nodes[0])
778
+ if executor == nil || !isFunctionLikeKind(executor) {
779
+ return nil
780
+ }
781
+ return executor
782
+ }
783
+
784
+ func parameterIdentifierName(node *shimast.Node) string {
785
+ if node == nil {
786
+ return ""
787
+ }
788
+ param := node.AsParameterDeclaration()
789
+ if param == nil {
790
+ return ""
791
+ }
792
+ return identifierText(param.Name())
793
+ }
794
+
795
+ func promisePropertyAccessParts(node *shimast.Node) (*shimast.Node, string, bool) {
796
+ node = stripParens(node)
797
+ if node == nil || node.Kind != shimast.KindPropertyAccessExpression {
798
+ return nil, "", false
799
+ }
800
+ access := node.AsPropertyAccessExpression()
801
+ if access == nil {
802
+ return nil, "", false
803
+ }
804
+ prop := identifierText(access.Name())
805
+ return access.Expression, prop, prop != ""
806
+ }
807
+
808
+ func promiseCallMethod(call *shimast.CallExpression) (*shimast.Node, string, bool) {
809
+ if call == nil || call.Expression == nil {
810
+ return nil, "", false
811
+ }
812
+ object, method, ok := promisePropertyAccessParts(call.Expression)
813
+ if !ok {
814
+ return nil, "", false
815
+ }
816
+ if identifierText(object) == "Promise" && !isPromiseStaticMethod(method) {
817
+ return nil, "", false
818
+ }
819
+ return object, method, true
820
+ }
821
+
822
+ func promiseStaticMethod(node *shimast.Node) (string, bool) {
823
+ object, method, ok := promisePropertyAccessParts(node)
824
+ if !ok || identifierText(object) != "Promise" || !isPromiseStaticMethod(method) {
825
+ return "", false
826
+ }
827
+ return method, true
828
+ }
829
+
830
+ func isPromiseStaticMethod(method string) bool {
831
+ switch method {
832
+ case "all", "allSettled", "any", "race", "reject", "resolve", "withResolvers":
833
+ return true
834
+ }
835
+ return false
836
+ }
837
+
838
+ func isPromiseInstanceMethod(method string) bool {
839
+ switch method {
840
+ case "then", "catch", "finally":
841
+ return true
842
+ }
843
+ return false
844
+ }
845
+
846
+ func isPromiseLikeCall(call *shimast.CallExpression) bool {
847
+ object, method, ok := promiseCallMethod(call)
848
+ if !ok {
849
+ return false
850
+ }
851
+ if isPromiseInstanceMethod(method) {
852
+ return true
853
+ }
854
+ return identifierText(object) == "Promise" && isPromiseStaticMethod(method)
855
+ }
856
+
857
+ func promiseChainHasMethod(node *shimast.Node, want string) bool {
858
+ node = stripParens(node)
859
+ for node != nil && node.Kind == shimast.KindCallExpression {
860
+ call := node.AsCallExpression()
861
+ object, method, ok := promiseCallMethod(call)
862
+ if !ok {
863
+ return false
864
+ }
865
+ if method == want {
866
+ return true
867
+ }
868
+ node = object
869
+ }
870
+ return false
871
+ }
872
+
873
+ func promiseResolveRejectCall(node *shimast.Node) (string, bool) {
874
+ node = stripParens(node)
875
+ if node == nil || node.Kind != shimast.KindCallExpression {
876
+ return "", false
877
+ }
878
+ call := node.AsCallExpression()
879
+ object, method, ok := promiseCallMethod(call)
880
+ if !ok || identifierText(object) != "Promise" || (method != "resolve" && method != "reject") {
881
+ return "", false
882
+ }
883
+ return method, true
884
+ }
885
+
886
+ func nearestFunctionLike(node *shimast.Node) *shimast.Node {
887
+ for cur := node.Parent; cur != nil; cur = cur.Parent {
888
+ if isFunctionLikeKind(cur) {
889
+ return cur
890
+ }
891
+ }
892
+ return nil
893
+ }
894
+
895
+ func isInsidePromiseCallbackFunction(node *shimast.Node) bool {
896
+ if fn := nearestFunctionLike(node); fn != nil {
897
+ return isPromiseCallbackFunction(fn)
898
+ }
899
+ return false
900
+ }
901
+
902
+ func isPromiseCallbackFunction(fn *shimast.Node) bool {
903
+ return isPromiseCallbackFunctionFor(fn, "then") ||
904
+ isPromiseCallbackFunctionFor(fn, "catch") ||
905
+ isPromiseCallbackFunctionFor(fn, "finally")
906
+ }
907
+
908
+ func isPromiseCallbackFunctionFor(fn *shimast.Node, method string) bool {
909
+ if fn == nil || !isFunctionLikeKind(fn) {
910
+ return false
911
+ }
912
+ for parent := fn.Parent; parent != nil && parent.Kind == shimast.KindParenthesizedExpression; parent = parent.Parent {
913
+ fn = parent
914
+ }
915
+ callNode := fn.Parent
916
+ if callNode == nil || callNode.Kind != shimast.KindCallExpression {
917
+ return false
918
+ }
919
+ call := callNode.AsCallExpression()
920
+ if call == nil || call.Arguments == nil {
921
+ return false
922
+ }
923
+ found := false
924
+ for _, arg := range call.Arguments.Nodes {
925
+ if stripParens(arg) == fn {
926
+ found = true
927
+ break
928
+ }
929
+ }
930
+ if !found {
931
+ return false
932
+ }
933
+ _, actual, ok := promiseCallMethod(call)
934
+ return ok && actual == method
935
+ }
936
+
937
+ func blockReturnsOrThrows(block *shimast.Node) bool {
938
+ if block == nil || block.Kind != shimast.KindBlock {
939
+ return false
940
+ }
941
+ return statementsReturnOrThrow(block.Statements())
942
+ }
943
+
944
+ func statementsReturnOrThrow(statements []*shimast.Node) bool {
945
+ for _, stmt := range statements {
946
+ if statementReturnsOrThrows(stmt) {
947
+ return true
948
+ }
949
+ }
950
+ return false
951
+ }
952
+
953
+ func statementReturnsOrThrows(stmt *shimast.Node) bool {
954
+ if stmt == nil {
955
+ return false
956
+ }
957
+ switch stmt.Kind {
958
+ case shimast.KindReturnStatement, shimast.KindThrowStatement:
959
+ return true
960
+ case shimast.KindBlock:
961
+ return statementsReturnOrThrow(stmt.Statements())
962
+ case shimast.KindIfStatement:
963
+ ifStmt := stmt.AsIfStatement()
964
+ if ifStmt == nil || ifStmt.ThenStatement == nil || ifStmt.ElseStatement == nil {
965
+ return false
966
+ }
967
+ return statementReturnsOrThrows(ifStmt.ThenStatement) &&
968
+ statementReturnsOrThrows(ifStmt.ElseStatement)
969
+ case shimast.KindTryStatement:
970
+ return tryStatementReturnsOrThrows(stmt)
971
+ case shimast.KindSwitchStatement:
972
+ return switchStatementReturnsOrThrows(stmt)
973
+ case shimast.KindLabeledStatement:
974
+ labeled := stmt.AsLabeledStatement()
975
+ return labeled != nil && statementReturnsOrThrows(labeled.Statement)
976
+ }
977
+ return false
978
+ }
979
+
980
+ func tryStatementReturnsOrThrows(stmt *shimast.Node) bool {
981
+ tryStmt := stmt.AsTryStatement()
982
+ if tryStmt == nil {
983
+ return false
984
+ }
985
+ if tryStmt.FinallyBlock != nil && statementsReturnOrThrow(tryStmt.FinallyBlock.Statements()) {
986
+ return true
987
+ }
988
+ if tryStmt.TryBlock == nil || !statementsReturnOrThrow(tryStmt.TryBlock.Statements()) {
989
+ return false
990
+ }
991
+ if tryStmt.CatchClause == nil {
992
+ return true
993
+ }
994
+ catchClause := tryStmt.CatchClause.AsCatchClause()
995
+ return catchClause != nil &&
996
+ catchClause.Block != nil &&
997
+ statementsReturnOrThrow(catchClause.Block.Statements())
998
+ }
999
+
1000
+ func switchStatementReturnsOrThrows(stmt *shimast.Node) bool {
1001
+ switchStmt := stmt.AsSwitchStatement()
1002
+ if switchStmt == nil || switchStmt.CaseBlock == nil {
1003
+ return false
1004
+ }
1005
+ caseBlock := switchStmt.CaseBlock.AsCaseBlock()
1006
+ if caseBlock == nil || caseBlock.Clauses == nil {
1007
+ return false
1008
+ }
1009
+ clauses := caseBlock.Clauses.Nodes
1010
+ hasDefault := false
1011
+ for index, clauseNode := range clauses {
1012
+ if clauseNode == nil {
1013
+ return false
1014
+ }
1015
+ if clauseNode.Kind == shimast.KindDefaultClause {
1016
+ hasDefault = true
1017
+ }
1018
+ if !switchClauseEntryReturnsOrThrows(clauses[index:]) {
1019
+ return false
1020
+ }
1021
+ }
1022
+ return hasDefault
1023
+ }
1024
+
1025
+ func switchClauseEntryReturnsOrThrows(clauses []*shimast.Node) bool {
1026
+ for _, clauseNode := range clauses {
1027
+ clause := clauseNode.AsCaseOrDefaultClause()
1028
+ if clause == nil || clause.Statements == nil {
1029
+ return false
1030
+ }
1031
+ if statementsReturnOrThrow(clause.Statements.Nodes) {
1032
+ return true
1033
+ }
1034
+ }
1035
+ return false
1036
+ }
1037
+
1038
+ func walkFunctionBody(root *shimast.Node, visit func(*shimast.Node)) {
1039
+ if root == nil {
1040
+ return
1041
+ }
1042
+ var walk func(*shimast.Node)
1043
+ walk = func(node *shimast.Node) {
1044
+ if node == nil {
1045
+ return
1046
+ }
1047
+ if node != root && isFunctionLikeKind(node) {
1048
+ return
1049
+ }
1050
+ visit(node)
1051
+ node.ForEachChild(func(child *shimast.Node) bool {
1052
+ walk(child)
1053
+ return false
1054
+ })
1055
+ }
1056
+ walk(root)
1057
+ }
1058
+
1059
+ func isCallbackName(name string) bool {
1060
+ switch name {
1061
+ case "callback", "cb", "next", "done":
1062
+ return true
1063
+ }
1064
+ return false
1065
+ }
1066
+
1067
+ func isCallbackCall(call *shimast.CallExpression) bool {
1068
+ if call == nil {
1069
+ return false
1070
+ }
1071
+ if isCallbackName(callCalleeName(call)) {
1072
+ return true
1073
+ }
1074
+ return false
1075
+ }
1076
+
1077
+ func isErrorFirstCallback(fn *shimast.Node) bool {
1078
+ if fn == nil || !isFunctionLikeKind(fn) || isPromiseCallbackFunction(fn) {
1079
+ return false
1080
+ }
1081
+ params := fn.Parameters()
1082
+ if len(params) == 0 {
1083
+ return false
1084
+ }
1085
+ switch parameterIdentifierName(params[0]) {
1086
+ case "err", "error":
1087
+ return true
1088
+ }
1089
+ return false
1090
+ }
1091
+
1092
+ func hasErrorFirstCallbackArgument(call *shimast.CallExpression) bool {
1093
+ if call == nil || call.Arguments == nil || len(call.Arguments.Nodes) == 0 {
1094
+ return false
1095
+ }
1096
+ last := stripParens(call.Arguments.Nodes[len(call.Arguments.Nodes)-1])
1097
+ if last == nil || !isFunctionLikeKind(last) || isArrayIterationCallback(call) {
1098
+ return false
1099
+ }
1100
+ params := last.Parameters()
1101
+ return len(params) > 0 && (parameterIdentifierName(params[0]) == "err" || parameterIdentifierName(params[0]) == "error")
1102
+ }
1103
+
1104
+ func isArrayIterationCallback(call *shimast.CallExpression) bool {
1105
+ _, method, ok := promiseCallMethod(call)
1106
+ if !ok {
1107
+ return false
1108
+ }
1109
+ switch method {
1110
+ case "map", "every", "forEach", "some", "find", "filter", "on", "once":
1111
+ return true
1112
+ }
1113
+ return false
1114
+ }
1115
+
1116
+ func isInsideAwaitOrYield(node *shimast.Node) bool {
1117
+ for cur := node.Parent; cur != nil; cur = cur.Parent {
1118
+ if cur.Kind == shimast.KindAwaitExpression || cur.Kind == shimast.KindYieldExpression {
1119
+ return true
1120
+ }
1121
+ }
1122
+ return false
1123
+ }
1124
+
1125
+ func isDirectReturnValue(node *shimast.Node) bool {
1126
+ parent := node.Parent
1127
+ if parent == nil {
1128
+ return false
1129
+ }
1130
+ if parent.Kind == shimast.KindReturnStatement {
1131
+ return true
1132
+ }
1133
+ if parent.Kind == shimast.KindArrowFunction {
1134
+ arrow := parent.AsArrowFunction()
1135
+ return arrow != nil && arrow.Body == node
1136
+ }
1137
+ return false
1138
+ }
1139
+
1140
+ func fileDeclaresPromise(file *shimast.Node) bool {
1141
+ declared := false
1142
+ walkDescendants(file, func(node *shimast.Node) {
1143
+ if declared || node == nil {
1144
+ return
1145
+ }
1146
+ switch node.Kind {
1147
+ case shimast.KindImportClause, shimast.KindImportSpecifier, shimast.KindNamespaceImport:
1148
+ if node.Name() != nil && identifierText(node.Name()) == "Promise" {
1149
+ declared = true
1150
+ }
1151
+ case shimast.KindVariableDeclaration, shimast.KindFunctionDeclaration, shimast.KindClassDeclaration, shimast.KindInterfaceDeclaration, shimast.KindTypeAliasDeclaration:
1152
+ if node.Name() != nil && identifierText(node.Name()) == "Promise" {
1153
+ declared = true
1154
+ }
1155
+ }
1156
+ })
1157
+ return declared
122
1158
  }
123
1159
 
124
1160
  func init() {
125
- Register(awaitThenable{})
1161
+ Register(awaitThenable{})
1162
+ Register(noFloatingPromises{})
1163
+ Register(returnAwait{})
1164
+ Register(promiseAlwaysReturn{})
1165
+ Register(promiseAvoidNew{})
1166
+ Register(promiseCatchOrReturn{})
1167
+ Register(promiseNoCallbackInPromise{})
1168
+ Register(promiseNoMultipleResolved{})
1169
+ Register(promiseNoNative{})
1170
+ Register(promiseNoNesting{})
1171
+ Register(promiseNoNewStatics{})
1172
+ Register(promiseNoPromiseInCallback{})
1173
+ Register(promiseNoReturnInFinally{})
1174
+ Register(promiseNoReturnWrap{})
1175
+ Register(promiseParamNames{})
1176
+ Register(promisePreferAwaitToCallbacks{})
1177
+ Register(promisePreferAwaitToThen{})
1178
+ Register(promisePreferCatch{})
1179
+ Register(promiseSpecOnly{})
1180
+ Register(promiseValidParams{})
126
1181
  }