@ttsc/lint 0.13.0 → 0.14.0-dev.20250530.1

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 (498) hide show
  1. package/README.md +955 -85
  2. package/lib/defaultFormat.d.ts +15 -8
  3. package/lib/defaultFormat.js +15 -8
  4. package/lib/defaultFormat.js.map +1 -1
  5. package/lib/index.d.ts +46 -0
  6. package/lib/index.js +60 -37
  7. package/lib/index.js.map +1 -1
  8. package/lib/structures/ITtscLintConfig.d.ts +12 -6
  9. package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
  10. package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
  11. package/lib/structures/TtscLintRuleSetting.js.map +1 -0
  12. package/lib/structures/format/ITtscLintFormat.d.ts +128 -0
  13. package/lib/structures/{TtscLintRuleMap.js → format/ITtscLintFormat.js} +1 -1
  14. package/lib/structures/format/ITtscLintFormat.js.map +1 -0
  15. package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +17 -0
  16. package/lib/structures/{ITtscLintFormatConfig.js → format/ITtscLintFormatJsDoc.js} +1 -1
  17. package/lib/structures/format/ITtscLintFormatJsDoc.js.map +1 -0
  18. package/lib/structures/format/ITtscLintFormatSortImports.d.ts +54 -0
  19. package/lib/structures/format/ITtscLintFormatSortImports.js +3 -0
  20. package/lib/structures/format/ITtscLintFormatSortImports.js.map +1 -0
  21. package/lib/structures/format/index.d.ts +3 -0
  22. package/lib/structures/format/index.js +20 -0
  23. package/lib/structures/format/index.js.map +1 -0
  24. package/lib/structures/index.d.ts +3 -4
  25. package/lib/structures/index.js +3 -4
  26. package/lib/structures/index.js.map +1 -1
  27. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +95 -0
  28. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +13 -0
  29. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
  30. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +75 -0
  31. package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
  32. package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
  33. package/lib/structures/rules/ITtscLintContributorRules.d.ts +16 -0
  34. package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
  35. package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
  36. package/lib/structures/rules/ITtscLintCoreRules.d.ts +1229 -0
  37. package/lib/structures/rules/ITtscLintCoreRules.js +3 -0
  38. package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
  39. package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
  40. package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
  41. package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
  42. package/lib/structures/rules/ITtscLintCypressRules.d.ts +125 -0
  43. package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
  44. package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
  45. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +154 -0
  46. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
  47. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
  48. package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +162 -0
  49. package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
  50. package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
  51. package/lib/structures/rules/ITtscLintJestRules.d.ts +190 -0
  52. package/lib/structures/rules/ITtscLintJestRules.js +3 -0
  53. package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
  54. package/lib/structures/rules/ITtscLintJsdocRules.d.ts +133 -0
  55. package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
  56. package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
  57. package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +294 -0
  58. package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
  59. package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
  60. package/lib/structures/rules/ITtscLintNextjsRules.d.ts +180 -0
  61. package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
  62. package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
  63. package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +312 -0
  64. package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
  65. package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
  66. package/lib/structures/rules/ITtscLintPromiseRules.d.ts +179 -0
  67. package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
  68. package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
  69. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
  70. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
  71. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
  72. package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +49 -0
  73. package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
  74. package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
  75. package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +37 -0
  76. package/lib/structures/rules/ITtscLintReactRuleOptions.js +11 -0
  77. package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
  78. package/lib/structures/rules/ITtscLintReactRules.d.ts +276 -0
  79. package/lib/structures/rules/ITtscLintReactRules.js +3 -0
  80. package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
  81. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +182 -0
  82. package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
  83. package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
  84. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +65 -0
  85. package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
  86. package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
  87. package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
  88. package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
  89. package/lib/structures/rules/ITtscLintRules.js.map +1 -0
  90. package/lib/structures/rules/ITtscLintSecurityRules.d.ts +141 -0
  91. package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
  92. package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
  93. package/lib/structures/rules/ITtscLintSolidRules.d.ts +178 -0
  94. package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
  95. package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
  96. package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +20 -0
  97. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
  98. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
  99. package/lib/structures/rules/ITtscLintStorybookRules.d.ts +165 -0
  100. package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
  101. package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
  102. package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +88 -0
  103. package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
  104. package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
  105. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +21 -0
  106. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
  107. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
  108. package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +293 -0
  109. package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
  110. package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
  111. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1074 -0
  112. package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
  113. package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
  114. package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
  115. package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
  116. package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
  117. package/lib/structures/rules/ITtscLintVitestRules.d.ts +133 -0
  118. package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
  119. package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
  120. package/lib/structures/rules/index.d.ts +31 -0
  121. package/lib/structures/rules/index.js +48 -0
  122. package/lib/structures/rules/index.js.map +1 -0
  123. package/linthost/ast_helpers.go +30 -5
  124. package/linthost/compile.go +33 -2
  125. package/linthost/config.go +118 -71
  126. package/linthost/config_format.go +270 -104
  127. package/linthost/directives.go +67 -25
  128. package/linthost/dispatch.go +14 -3
  129. package/linthost/engine.go +160 -40
  130. package/linthost/fix.go +10 -3
  131. package/linthost/flags_gen.go +17 -15
  132. package/linthost/format.go +222 -25
  133. package/linthost/format_editor_settings.go +262 -0
  134. package/linthost/host.go +40 -3
  135. package/linthost/lsp.go +898 -0
  136. package/linthost/print_dispatch.go +5 -2
  137. package/linthost/print_engine.go +5 -5
  138. package/linthost/print_nodes_call.go +149 -67
  139. package/linthost/print_nodes_function.go +27 -2
  140. package/linthost/print_nodes_list.go +40 -5
  141. package/linthost/print_nodes_object.go +1 -1
  142. package/linthost/print_nodes_ternary.go +86 -0
  143. package/linthost/rule_names.go +14 -0
  144. package/linthost/rules_arrays.go +2 -2
  145. package/linthost/rules_boundaries.go +532 -0
  146. package/linthost/rules_camelcase.go +133 -0
  147. package/linthost/rules_complexity.go +95 -0
  148. package/linthost/rules_consistent_return.go +130 -0
  149. package/linthost/rules_console.go +1 -1
  150. package/linthost/rules_core_extra.go +739 -0
  151. package/linthost/rules_curly.go +70 -0
  152. package/linthost/rules_cypress.go +608 -0
  153. package/linthost/rules_debugger.go +2 -2
  154. package/linthost/rules_default_case.go +39 -0
  155. package/linthost/rules_default_case_last.go +46 -0
  156. package/linthost/rules_dupes.go +3 -3
  157. package/linthost/rules_empty.go +3 -3
  158. package/linthost/rules_escape.go +44 -24
  159. package/linthost/rules_eval.go +2 -2
  160. package/linthost/rules_finally.go +2 -2
  161. package/linthost/rules_format_arrow_parens.go +167 -0
  162. package/linthost/rules_format_bracket_spacing.go +117 -0
  163. package/linthost/rules_format_clause_join.go +172 -0
  164. package/linthost/rules_format_declaration_header.go +595 -0
  165. package/linthost/rules_format_indent.go +372 -0
  166. package/linthost/rules_format_jsdoc.go +5 -5
  167. package/linthost/rules_format_orphan_semi.go +106 -0
  168. package/linthost/rules_format_parameter_properties.go +175 -0
  169. package/linthost/rules_format_print_width.go +66 -55
  170. package/linthost/rules_format_quote_props.go +169 -0
  171. package/linthost/rules_format_quotes.go +76 -30
  172. package/linthost/rules_format_semi.go +186 -11
  173. package/linthost/rules_format_sort_imports.go +437 -118
  174. package/linthost/rules_format_statement_split.go +343 -0
  175. package/linthost/rules_format_ternary_nullish_parens.go +77 -0
  176. package/linthost/rules_format_trailing_comma.go +22 -3
  177. package/linthost/rules_format_whitespace.go +336 -0
  178. package/linthost/rules_functional.go +967 -0
  179. package/linthost/rules_gap.go +24 -45
  180. package/linthost/rules_grouped_accessor_pairs.go +87 -0
  181. package/linthost/rules_guard_for_in.go +198 -0
  182. package/linthost/rules_id_length.go +77 -0
  183. package/linthost/rules_imports.go +70 -2
  184. package/linthost/rules_init_declarations.go +53 -0
  185. package/linthost/rules_jest.go +618 -0
  186. package/linthost/rules_jsdoc.go +502 -0
  187. package/linthost/rules_jsx_a11y.go +1264 -0
  188. package/linthost/rules_logic.go +12 -12
  189. package/linthost/rules_loops.go +1 -1
  190. package/linthost/rules_max_classes_per_file.go +58 -0
  191. package/linthost/rules_max_depth.go +83 -0
  192. package/linthost/rules_max_lines.go +101 -0
  193. package/linthost/rules_max_lines_per_function.go +57 -0
  194. package/linthost/rules_max_nested_callbacks.go +61 -0
  195. package/linthost/rules_max_params.go +49 -0
  196. package/linthost/rules_max_statements.go +59 -0
  197. package/linthost/rules_misc.go +1 -1
  198. package/linthost/rules_nextjs.go +697 -0
  199. package/linthost/rules_no_else_return.go +75 -0
  200. package/linthost/rules_no_empty_named_blocks.go +91 -0
  201. package/linthost/rules_no_extend_native.go +51 -0
  202. package/linthost/rules_no_import_assign.go +140 -0
  203. package/linthost/rules_no_invalid_this.go +52 -0
  204. package/linthost/rules_no_loop_func.go +85 -0
  205. package/linthost/rules_no_magic_numbers.go +123 -0
  206. package/linthost/rules_no_mixed_operators.go +80 -0
  207. package/linthost/rules_no_param_reassign.go +98 -0
  208. package/linthost/rules_no_redeclare.go +270 -0
  209. package/linthost/rules_no_restricted_imports.go +105 -0
  210. package/linthost/rules_no_restricted_syntax.go +49 -0
  211. package/linthost/rules_no_shadow.go +147 -0
  212. package/linthost/rules_no_unreachable.go +77 -0
  213. package/linthost/rules_no_useless_assignment.go +120 -0
  214. package/linthost/rules_no_useless_constructor.go +245 -0
  215. package/linthost/rules_no_useless_return.go +51 -0
  216. package/linthost/rules_params.go +1 -1
  217. package/linthost/rules_playwright.go +788 -0
  218. package/linthost/rules_prefer_arrow_callback.go +124 -0
  219. package/linthost/rules_prefer_destructuring.go +98 -0
  220. package/linthost/rules_prefer_named_capture_group.go +129 -0
  221. package/linthost/rules_prefer_rest_params.go +94 -0
  222. package/linthost/rules_problems.go +41 -30
  223. package/linthost/rules_promise.go +1057 -2
  224. package/linthost/rules_protos.go +2 -2
  225. package/linthost/rules_react.go +773 -0
  226. package/linthost/rules_react_extras.go +227 -0
  227. package/linthost/rules_react_hooks.go +848 -0
  228. package/linthost/rules_react_perf.go +293 -0
  229. package/linthost/rules_react_refresh.go +583 -0
  230. package/linthost/rules_regexp.go +410 -0
  231. package/linthost/rules_security.go +830 -0
  232. package/linthost/rules_self.go +2 -2
  233. package/linthost/rules_solid.go +1073 -0
  234. package/linthost/rules_sort_imports.go +69 -0
  235. package/linthost/rules_sort_keys.go +134 -0
  236. package/linthost/rules_storybook.go +1176 -0
  237. package/linthost/rules_strings.go +4 -4
  238. package/linthost/rules_suggestions.go +38 -38
  239. package/linthost/rules_tanstack_query.go +844 -0
  240. package/linthost/rules_testing_library.go +1204 -0
  241. package/linthost/rules_throw.go +1 -1
  242. package/linthost/rules_ts.go +19 -19
  243. package/linthost/rules_ts_async.go +399 -0
  244. package/linthost/rules_ts_class_literal_property_style.go +115 -0
  245. package/linthost/rules_ts_consistent_type_exports.go +256 -0
  246. package/linthost/rules_ts_explicit_function_return_type.go +67 -0
  247. package/linthost/rules_ts_extra.go +159 -36
  248. package/linthost/rules_ts_no_base_to_string.go +218 -0
  249. package/linthost/rules_ts_no_confusing_void.go +72 -0
  250. package/linthost/rules_ts_no_deprecated.go +195 -0
  251. package/linthost/rules_ts_no_for_in_array.go +84 -0
  252. package/linthost/rules_ts_no_magic_numbers.go +124 -0
  253. package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
  254. package/linthost/rules_ts_no_misused_spread.go +104 -0
  255. package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
  256. package/linthost/rules_ts_no_restricted_types.go +62 -0
  257. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
  258. package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
  259. package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
  260. package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
  261. package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
  262. package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
  263. package/linthost/rules_ts_no_unsafe_argument.go +82 -0
  264. package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
  265. package/linthost/rules_ts_no_unsafe_call.go +82 -0
  266. package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
  267. package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
  268. package/linthost/rules_ts_no_unsafe_return.go +126 -0
  269. package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
  270. package/linthost/rules_ts_no_useless_constructor.go +52 -0
  271. package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
  272. package/linthost/rules_ts_parameter_properties.go +38 -0
  273. package/linthost/rules_ts_prefer_find.go +178 -0
  274. package/linthost/rules_ts_prefer_includes.go +255 -0
  275. package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
  276. package/linthost/rules_ts_prefer_optional_chain.go +228 -0
  277. package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
  278. package/linthost/rules_ts_prefer_readonly.go +73 -0
  279. package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
  280. package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
  281. package/linthost/rules_ts_prefer_return_this_type.go +135 -0
  282. package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
  283. package/linthost/rules_ts_promise_function_async.go +77 -0
  284. package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
  285. package/linthost/rules_ts_require_array_sort_compare.go +96 -0
  286. package/linthost/rules_ts_restrict_plus_operands.go +127 -0
  287. package/linthost/rules_ts_restrict_template_expressions.go +95 -0
  288. package/linthost/rules_ts_simple.go +387 -0
  289. package/linthost/rules_ts_sort_type_constituents.go +144 -0
  290. package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
  291. package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
  292. package/linthost/rules_ts_unbound_method.go +126 -0
  293. package/linthost/rules_tsdoc.go +136 -0
  294. package/linthost/rules_unicorn_better_regex.go +27 -0
  295. package/linthost/rules_unicorn_catch_error_name.go +46 -0
  296. package/linthost/rules_unicorn_consistent_assert.go +47 -0
  297. package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
  298. package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
  299. package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
  300. package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
  301. package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
  302. package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
  303. package/linthost/rules_unicorn_custom_error_definition.go +138 -0
  304. package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
  305. package/linthost/rules_unicorn_error_message.go +64 -0
  306. package/linthost/rules_unicorn_escape_case.go +51 -0
  307. package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
  308. package/linthost/rules_unicorn_explicit_length_check.go +88 -0
  309. package/linthost/rules_unicorn_filename_case.go +29 -0
  310. package/linthost/rules_unicorn_import_style.go +28 -0
  311. package/linthost/rules_unicorn_isolated_functions.go +29 -0
  312. package/linthost/rules_unicorn_new_for_builtins.go +87 -0
  313. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
  314. package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
  315. package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
  316. package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
  317. package/linthost/rules_unicorn_no_array_for_each.go +40 -0
  318. package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
  319. package/linthost/rules_unicorn_no_array_reduce.go +41 -0
  320. package/linthost/rules_unicorn_no_array_reverse.go +44 -0
  321. package/linthost/rules_unicorn_no_array_sort.go +43 -0
  322. package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
  323. package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
  324. package/linthost/rules_unicorn_no_console_spaces.go +61 -0
  325. package/linthost/rules_unicorn_no_document_cookie.go +41 -0
  326. package/linthost/rules_unicorn_no_empty_file.go +44 -0
  327. package/linthost/rules_unicorn_no_for_loop.go +102 -0
  328. package/linthost/rules_unicorn_no_hex_escape.go +42 -0
  329. package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
  330. package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
  331. package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
  332. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
  333. package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
  334. package/linthost/rules_unicorn_no_lonely_if.go +44 -0
  335. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
  336. package/linthost/rules_unicorn_no_named_default.go +54 -0
  337. package/linthost/rules_unicorn_no_negated_condition.go +85 -0
  338. package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
  339. package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
  340. package/linthost/rules_unicorn_no_new_array.go +30 -0
  341. package/linthost/rules_unicorn_no_new_buffer.go +31 -0
  342. package/linthost/rules_unicorn_no_null.go +24 -0
  343. package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
  344. package/linthost/rules_unicorn_no_process_exit.go +43 -0
  345. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
  346. package/linthost/rules_unicorn_no_static_only_class.go +56 -0
  347. package/linthost/rules_unicorn_no_thenable.go +74 -0
  348. package/linthost/rules_unicorn_no_this_assignment.go +35 -0
  349. package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
  350. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
  351. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
  352. package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
  353. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
  354. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
  355. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
  356. package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
  357. package/linthost/rules_unicorn_no_unused_properties.go +30 -0
  358. package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
  359. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
  360. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
  361. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
  362. package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
  363. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
  364. package/linthost/rules_unicorn_no_useless_spread.go +67 -0
  365. package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
  366. package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
  367. package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
  368. package/linthost/rules_unicorn_number_literal_case.go +69 -0
  369. package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
  370. package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
  371. package/linthost/rules_unicorn_prefer_array_find.go +51 -0
  372. package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
  373. package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
  374. package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
  375. package/linthost/rules_unicorn_prefer_array_some.go +67 -0
  376. package/linthost/rules_unicorn_prefer_at.go +77 -0
  377. package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
  378. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
  379. package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
  380. package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
  381. package/linthost/rules_unicorn_prefer_code_point.go +49 -0
  382. package/linthost/rules_unicorn_prefer_date_now.go +78 -0
  383. package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
  384. package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
  385. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
  386. package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
  387. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
  388. package/linthost/rules_unicorn_prefer_event_target.go +36 -0
  389. package/linthost/rules_unicorn_prefer_export_from.go +129 -0
  390. package/linthost/rules_unicorn_prefer_global_this.go +127 -0
  391. package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
  392. package/linthost/rules_unicorn_prefer_includes.go +149 -0
  393. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
  394. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
  395. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
  396. package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
  397. package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
  398. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
  399. package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
  400. package/linthost/rules_unicorn_prefer_module.go +50 -0
  401. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
  402. package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
  403. package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
  404. package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
  405. package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
  406. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
  407. package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
  408. package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
  409. package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
  410. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
  411. package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
  412. package/linthost/rules_unicorn_prefer_set_has.go +42 -0
  413. package/linthost/rules_unicorn_prefer_set_size.go +48 -0
  414. package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
  415. package/linthost/rules_unicorn_prefer_single_call.go +96 -0
  416. package/linthost/rules_unicorn_prefer_spread.go +51 -0
  417. package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
  418. package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
  419. package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
  420. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
  421. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
  422. package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
  423. package/linthost/rules_unicorn_prefer_switch.go +103 -0
  424. package/linthost/rules_unicorn_prefer_ternary.go +71 -0
  425. package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
  426. package/linthost/rules_unicorn_prefer_type_error.go +127 -0
  427. package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
  428. package/linthost/rules_unicorn_relative_url_style.go +49 -0
  429. package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
  430. package/linthost/rules_unicorn_require_module_attributes.go +54 -0
  431. package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
  432. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
  433. package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
  434. package/linthost/rules_unicorn_string_content.go +27 -0
  435. package/linthost/rules_unicorn_switch_case_braces.go +42 -0
  436. package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
  437. package/linthost/rules_unicorn_template_indent.go +28 -0
  438. package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
  439. package/linthost/rules_unicorn_throw_new_error.go +61 -0
  440. package/linthost/rules_var.go +10 -4
  441. package/linthost/rules_vitest.go +568 -0
  442. package/package.json +3 -3
  443. package/plugin/main.go +49 -3
  444. package/rule/astutil/astutil.go +10 -3
  445. package/rule/rule.go +12 -10
  446. package/src/defaultFormat.ts +17 -10
  447. package/src/index.ts +65 -43
  448. package/src/structures/ITtscLintConfig.ts +14 -8
  449. package/src/structures/TtscLintRuleSetting.ts +55 -0
  450. package/src/structures/format/ITtscLintFormat.ts +141 -0
  451. package/src/structures/format/ITtscLintFormatJsDoc.ts +18 -0
  452. package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
  453. package/src/structures/format/index.ts +3 -0
  454. package/src/structures/index.ts +3 -4
  455. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +117 -0
  456. package/src/structures/rules/ITtscLintBoundariesRules.ts +91 -0
  457. package/src/structures/rules/ITtscLintContributorRules.ts +20 -0
  458. package/src/structures/rules/ITtscLintCoreRules.ts +1378 -0
  459. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
  460. package/src/structures/rules/ITtscLintCypressRules.ts +141 -0
  461. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +200 -0
  462. package/src/structures/rules/ITtscLintFunctionalRules.ts +197 -0
  463. package/src/structures/rules/ITtscLintJestRules.ts +209 -0
  464. package/src/structures/rules/ITtscLintJsdocRules.ts +146 -0
  465. package/src/structures/rules/ITtscLintJsxA11yRules.ts +331 -0
  466. package/src/structures/rules/ITtscLintNextjsRules.ts +201 -0
  467. package/src/structures/rules/ITtscLintPlaywrightRules.ts +343 -0
  468. package/src/structures/rules/ITtscLintPromiseRules.ts +196 -0
  469. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
  470. package/src/structures/rules/ITtscLintReactPerfRules.ts +53 -0
  471. package/src/structures/rules/ITtscLintReactRuleOptions.ts +41 -0
  472. package/src/structures/rules/ITtscLintReactRules.ts +308 -0
  473. package/src/structures/rules/ITtscLintRegexpRules.ts +204 -0
  474. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -0
  475. package/src/structures/rules/ITtscLintRules.ts +77 -0
  476. package/src/structures/rules/ITtscLintSecurityRules.ts +155 -0
  477. package/src/structures/rules/ITtscLintSolidRules.ts +199 -0
  478. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +22 -0
  479. package/src/structures/rules/ITtscLintStorybookRules.ts +184 -0
  480. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +96 -0
  481. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +23 -0
  482. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +325 -0
  483. package/src/structures/rules/ITtscLintTypeScriptRules.ts +1172 -0
  484. package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
  485. package/src/structures/rules/ITtscLintVitestRules.ts +146 -0
  486. package/src/structures/rules/index.ts +31 -0
  487. package/lib/structures/ITtscLintFormatConfig.d.ts +0 -153
  488. package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
  489. package/lib/structures/TtscLintRule.d.ts +0 -17
  490. package/lib/structures/TtscLintRule.js.map +0 -1
  491. package/lib/structures/TtscLintRuleMap.d.ts +0 -46
  492. package/lib/structures/TtscLintRuleMap.js.map +0 -1
  493. package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
  494. package/lib/structures/TtscLintRuleOptions.js.map +0 -1
  495. package/src/structures/ITtscLintFormatConfig.ts +0 -168
  496. package/src/structures/TtscLintRule.ts +0 -160
  497. package/src/structures/TtscLintRuleMap.ts +0 -55
  498. package/src/structures/TtscLintRuleOptions.ts +0 -151
@@ -0,0 +1,360 @@
1
+ // typescript/prefer-string-starts-ends-with: prefer
2
+ // `str.startsWith(prefix)` / `str.endsWith(suffix)` over the older
3
+ // `indexOf` / `lastIndexOf` / anchored-regex shapes:
4
+ //
5
+ // - `str.indexOf(p) === 0` → startsWith
6
+ // - `str.indexOf(p, str.length - p.length) !== -1` → endsWith
7
+ // - `str.lastIndexOf(p) === str.length - p.length` → endsWith
8
+ // - `/^prefix/.test(str)` (a static-prefix regex) → startsWith
9
+ // - `/suffix$/.test(str)` (a static-suffix regex) → endsWith
10
+ //
11
+ // The dedicated methods state the intent directly and avoid the
12
+ // off-by-one arithmetic and regex-anchor pitfalls of the older shapes.
13
+ // typescript-eslint:
14
+ // https://typescript-eslint.io/rules/prefer-string-starts-ends-with/
15
+ //
16
+ // Type-aware. Without a Checker the rule cannot prove the receiver of
17
+ // `indexOf` / `lastIndexOf` (or the argument of `.test(str)`) is a
18
+ // `string`, so Context.Checker == nil short-circuits each Check to a
19
+ // no-op the way `no-base-to-string` and `prefer-includes` do. `any` /
20
+ // `unknown` / `never` are intentionally NOT treated as matching — they
21
+ // leak from generic helpers and would explode the false-positive
22
+ // volume on user-defined `indexOf` / `test` methods.
23
+ package linthost
24
+
25
+ import (
26
+ "strings"
27
+
28
+ shimast "github.com/microsoft/typescript-go/shim/ast"
29
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
30
+ )
31
+
32
+ type preferStringStartsEndsWith struct{}
33
+
34
+ func (preferStringStartsEndsWith) Name() string {
35
+ return "typescript/prefer-string-starts-ends-with"
36
+ }
37
+ func (preferStringStartsEndsWith) NeedsTypeChecker() bool {
38
+ return true
39
+ }
40
+ func (preferStringStartsEndsWith) Visits() []shimast.Kind {
41
+ return []shimast.Kind{
42
+ shimast.KindBinaryExpression,
43
+ shimast.KindCallExpression,
44
+ }
45
+ }
46
+ func (preferStringStartsEndsWith) Check(ctx *Context, node *shimast.Node) {
47
+ if ctx.Checker == nil {
48
+ return
49
+ }
50
+ switch node.Kind {
51
+ case shimast.KindBinaryExpression:
52
+ bin := node.AsBinaryExpression()
53
+ if bin == nil || bin.OperatorToken == nil {
54
+ return
55
+ }
56
+ if !preferStringStartsEndsWithIsEqualityOp(bin.OperatorToken.Kind) {
57
+ return
58
+ }
59
+ if preferStringStartsEndsWithBinary(ctx, bin.Left, bin.Right) {
60
+ ctx.Report(node, preferStringStartsEndsWithMessage)
61
+ return
62
+ }
63
+ if preferStringStartsEndsWithBinary(ctx, bin.Right, bin.Left) {
64
+ ctx.Report(node, preferStringStartsEndsWithMessage)
65
+ }
66
+ case shimast.KindCallExpression:
67
+ call := node.AsCallExpression()
68
+ if call == nil {
69
+ return
70
+ }
71
+ if preferStringStartsEndsWithRegexTest(ctx, call) {
72
+ ctx.Report(node, preferStringStartsEndsWithMessage)
73
+ }
74
+ }
75
+ }
76
+
77
+ const preferStringStartsEndsWithMessage = "Prefer `String#startsWith` / `String#endsWith` over the equivalent `indexOf` / `lastIndexOf` / anchored-regex idioms — clearer and avoids the off-by-one arithmetic."
78
+
79
+ // preferStringStartsEndsWithIsEqualityOp reports whether `kind` is one
80
+ // of the equality tokens the rule cares about. Only `==`, `===`,
81
+ // `!=`, `!==` matter: every recognized shape compares two integers
82
+ // for equality (or inequality with a sentinel) rather than ordering.
83
+ func preferStringStartsEndsWithIsEqualityOp(kind shimast.Kind) bool {
84
+ switch kind {
85
+ case shimast.KindEqualsEqualsToken,
86
+ shimast.KindEqualsEqualsEqualsToken,
87
+ shimast.KindExclamationEqualsToken,
88
+ shimast.KindExclamationEqualsEqualsToken:
89
+ return true
90
+ }
91
+ return false
92
+ }
93
+
94
+ // preferStringStartsEndsWithBinary inspects `call op other` for the
95
+ // `indexOf` / `lastIndexOf` shapes. Returns true when the receiver of
96
+ // the call is provably string-typed AND the right-hand side matches
97
+ // the sentinel of one of the recognized rewrites:
98
+ //
99
+ // str.indexOf(p) === 0
100
+ // str.indexOf(p, str.length - p.length) !== -1
101
+ // str.lastIndexOf(p) === str.length - p.length
102
+ func preferStringStartsEndsWithBinary(
103
+ ctx *Context,
104
+ callNode, otherNode *shimast.Node,
105
+ ) bool {
106
+ callNode = stripParens(callNode)
107
+ otherNode = stripParens(otherNode)
108
+ if callNode == nil || otherNode == nil {
109
+ return false
110
+ }
111
+ if callNode.Kind != shimast.KindCallExpression {
112
+ return false
113
+ }
114
+ call := callNode.AsCallExpression()
115
+ if call == nil || call.Expression == nil {
116
+ return false
117
+ }
118
+ receiver, method, ok := promisePropertyAccessParts(call.Expression)
119
+ if !ok {
120
+ return false
121
+ }
122
+ if method != "indexOf" && method != "lastIndexOf" {
123
+ return false
124
+ }
125
+ if receiver == nil {
126
+ return false
127
+ }
128
+ t := ctx.Checker.GetTypeAtLocation(receiver)
129
+ if !preferStringStartsEndsWithIsString(t) {
130
+ return false
131
+ }
132
+ args := call.Arguments
133
+ if args == nil || len(args.Nodes) == 0 {
134
+ return false
135
+ }
136
+ arg := stripParens(args.Nodes[0])
137
+ if arg == nil {
138
+ return false
139
+ }
140
+ receiverText := nodeText(ctx.File, receiver)
141
+ argText := nodeText(ctx.File, arg)
142
+ otherText := nodeText(ctx.File, otherNode)
143
+ if receiverText == "" || argText == "" {
144
+ return false
145
+ }
146
+ switch method {
147
+ case "indexOf":
148
+ // startsWith: `str.indexOf(p) === 0`
149
+ if len(args.Nodes) == 1 {
150
+ return otherText == "0"
151
+ }
152
+ // endsWith via positional indexOf: the second argument must
153
+ // equal `str.length - arg.length`, and the comparison must be
154
+ // against -1 (the sentinel for "not present at that
155
+ // position").
156
+ if len(args.Nodes) != 2 {
157
+ return false
158
+ }
159
+ second := stripParens(args.Nodes[1])
160
+ if !preferStringStartsEndsWithIsLengthMinusLength(ctx, second, receiverText, argText) {
161
+ return false
162
+ }
163
+ return preferStringStartsEndsWithIsMinusOne(otherText)
164
+ case "lastIndexOf":
165
+ // endsWith: `str.lastIndexOf(p) === str.length - p.length`
166
+ if len(args.Nodes) != 1 {
167
+ return false
168
+ }
169
+ return preferStringStartsEndsWithIsLengthMinusLengthText(otherText, receiverText, argText)
170
+ }
171
+ return false
172
+ }
173
+
174
+ // preferStringStartsEndsWithRegexTest reports whether `call` is a
175
+ // `/^.../.test(str)` or `/...$/.test(str)` invocation on a regex
176
+ // literal whose body is a static prefix or suffix — no metacharacters
177
+ // other than the anchor and no flags that change the matching
178
+ // semantics. The argument to `.test` must be a `string`-typed value;
179
+ // without that the call is not equivalent to a startsWith/endsWith
180
+ // rewrite (e.g. coercing a non-string regex argument).
181
+ func preferStringStartsEndsWithRegexTest(
182
+ ctx *Context,
183
+ call *shimast.CallExpression,
184
+ ) bool {
185
+ if call == nil || call.Expression == nil || call.Arguments == nil || len(call.Arguments.Nodes) != 1 {
186
+ return false
187
+ }
188
+ receiver, method, ok := promisePropertyAccessParts(call.Expression)
189
+ if !ok || method != "test" {
190
+ return false
191
+ }
192
+ receiver = stripParens(receiver)
193
+ if receiver == nil || receiver.Kind != shimast.KindRegularExpressionLiteral {
194
+ return false
195
+ }
196
+ raw := nodeText(ctx.File, receiver)
197
+ if !preferStringStartsEndsWithIsAnchoredStaticRegex(raw) {
198
+ return false
199
+ }
200
+ arg := stripParens(call.Arguments.Nodes[0])
201
+ if arg == nil {
202
+ return false
203
+ }
204
+ return preferStringStartsEndsWithIsString(ctx.Checker.GetTypeAtLocation(arg))
205
+ }
206
+
207
+ // preferStringStartsEndsWithIsAnchoredStaticRegex reports whether
208
+ // `raw` (the source text of a RegularExpressionLiteral, including the
209
+ // surrounding `/`) encodes either `^prefix` or `suffix$` with no
210
+ // metacharacters other than the anchor itself, and carries no flags
211
+ // that change matching semantics (`i`, `m`, `s`, `u`, `v`). A flag
212
+ // like `i` would lower-case both sides, which `startsWith` does not
213
+ // do — so the rewrite would change meaning.
214
+ func preferStringStartsEndsWithIsAnchoredStaticRegex(raw string) bool {
215
+ if len(raw) < 3 || raw[0] != '/' {
216
+ return false
217
+ }
218
+ closing := strings.LastIndexByte(raw, '/')
219
+ if closing <= 0 {
220
+ return false
221
+ }
222
+ pattern := raw[1:closing]
223
+ flags := raw[closing+1:]
224
+ if pattern == "" {
225
+ return false
226
+ }
227
+ for i := 0; i < len(flags); i++ {
228
+ switch flags[i] {
229
+ case 'g', 'y', 'd':
230
+ // stateful/position flags — fine for `.test` since we
231
+ // only call once
232
+ default:
233
+ return false
234
+ }
235
+ }
236
+ if pattern[0] == '^' {
237
+ return preferStringStartsEndsWithIsStaticString(pattern[1:])
238
+ }
239
+ if pattern[len(pattern)-1] == '$' {
240
+ return preferStringStartsEndsWithIsStaticString(pattern[:len(pattern)-1])
241
+ }
242
+ return false
243
+ }
244
+
245
+ // preferStringStartsEndsWithIsStaticString reports whether `pattern`
246
+ // is a regex body made up only of literal characters (and accepted
247
+ // escape sequences `\.`, `\\`, etc. that represent a single literal
248
+ // character). Any metacharacter — `^`, `$`, `*`, `+`, `?`, `(`, `)`,
249
+ // `[`, `]`, `{`, `}`, `|`, `.` — disqualifies the body because the
250
+ // startsWith/endsWith rewrite would change matching semantics.
251
+ func preferStringStartsEndsWithIsStaticString(pattern string) bool {
252
+ if pattern == "" {
253
+ return false
254
+ }
255
+ for i := 0; i < len(pattern); i++ {
256
+ ch := pattern[i]
257
+ if ch == '\\' {
258
+ // Accept only escapes that name a single literal
259
+ // character (`\.`, `\$`, `\^`, `\/`, `\\`, `\(`, `\)`,
260
+ // `\[`, `\]`, `\{`, `\}`, `\|`, `\+`, `\*`, `\?`).
261
+ // Reject character-class shortcuts (`\d`, `\w`, `\s`)
262
+ // and back-references — those would not survive a
263
+ // rewrite to startsWith/endsWith.
264
+ if i+1 >= len(pattern) {
265
+ return false
266
+ }
267
+ next := pattern[i+1]
268
+ switch next {
269
+ case '.', '$', '^', '/', '\\', '(', ')', '[', ']', '{', '}', '|', '+', '*', '?', '-':
270
+ i++
271
+ continue
272
+ }
273
+ return false
274
+ }
275
+ switch ch {
276
+ case '^', '$', '*', '+', '?', '(', ')', '[', ']', '{', '}', '|', '.':
277
+ return false
278
+ }
279
+ }
280
+ return true
281
+ }
282
+
283
+ // preferStringStartsEndsWithIsLengthMinusLength reports whether
284
+ // `node` textually evaluates to `<receiver>.length - <arg>.length`.
285
+ // The comparison is text-based (after stripping parens) because the
286
+ // rewrite only fires when the call site spelled the offset that way
287
+ // — matching the upstream rule's textual shape detector.
288
+ func preferStringStartsEndsWithIsLengthMinusLength(
289
+ ctx *Context,
290
+ node *shimast.Node,
291
+ receiverText, argText string,
292
+ ) bool {
293
+ if node == nil {
294
+ return false
295
+ }
296
+ return preferStringStartsEndsWithIsLengthMinusLengthText(nodeText(ctx.File, node), receiverText, argText)
297
+ }
298
+
299
+ // preferStringStartsEndsWithIsLengthMinusLengthText is the text-level
300
+ // half of `preferStringStartsEndsWithIsLengthMinusLength` — split out
301
+ // so the `lastIndexOf === str.length - p.length` shape can use the
302
+ // same matcher against the binary RHS text.
303
+ func preferStringStartsEndsWithIsLengthMinusLengthText(text, receiverText, argText string) bool {
304
+ wanted := receiverText + ".length-" + argText + ".length"
305
+ return preferStringStartsEndsWithStripSpaces(text) == wanted
306
+ }
307
+
308
+ // preferStringStartsEndsWithStripSpaces removes whitespace from `s`.
309
+ // Used for the textual equality match — `a.length - b.length` and
310
+ // `a.length-b.length` should both match the canonical form.
311
+ func preferStringStartsEndsWithStripSpaces(s string) string {
312
+ var b strings.Builder
313
+ for i := 0; i < len(s); i++ {
314
+ ch := s[i]
315
+ if ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' {
316
+ continue
317
+ }
318
+ b.WriteByte(ch)
319
+ }
320
+ return b.String()
321
+ }
322
+
323
+ // preferStringStartsEndsWithIsMinusOne reports whether `text` is the
324
+ // numeric literal `-1` (with or without internal whitespace).
325
+ func preferStringStartsEndsWithIsMinusOne(text string) bool {
326
+ return preferStringStartsEndsWithStripSpaces(text) == "-1"
327
+ }
328
+
329
+ // preferStringStartsEndsWithIsString reports whether t is provably
330
+ // string-like. Mirrors `isStringLikeType` in no-base-to-string but
331
+ // rejects `any` / `unknown` / `never` so generic helpers don't fire.
332
+ // Union and intersection types must have every constituent string-like.
333
+ func preferStringStartsEndsWithIsString(t *shimchecker.Type) bool {
334
+ if t == nil {
335
+ return false
336
+ }
337
+ flags := t.Flags()
338
+ if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
339
+ return false
340
+ }
341
+ if flags&shimchecker.TypeFlagsStringLike != 0 {
342
+ return true
343
+ }
344
+ if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
345
+ for _, part := range t.Types() {
346
+ if part == nil {
347
+ continue
348
+ }
349
+ if !preferStringStartsEndsWithIsString(part) {
350
+ return false
351
+ }
352
+ }
353
+ return true
354
+ }
355
+ return false
356
+ }
357
+
358
+ func init() {
359
+ Register(preferStringStartsEndsWith{})
360
+ }
@@ -0,0 +1,77 @@
1
+ // promiseFunctionAsync requires a function whose return type is
2
+ // `Promise<T>` (or a Promise-like thenable) to be declared with the
3
+ // `async` keyword. typescript-eslint:
4
+ // https://typescript-eslint.io/rules/promise-function-async/
5
+ //
6
+ // Type-aware. An `async` function wraps a synchronous `throw` into a
7
+ // rejected Promise so the caller's `await` / `.catch(...)` observes
8
+ // it; the non-async equivalent throws synchronously and bypasses every
9
+ // Promise-aware handler downstream. Marking the function `async` keeps
10
+ // the rejection channel consistent with the declared return type.
11
+ //
12
+ // Skipped:
13
+ // - functions already declared `async` (the contract holds);
14
+ // - abstract methods and overload signatures (no body to wrap — the
15
+ // `async` keyword can only be applied to the implementation);
16
+ // - functions whose declared / inferred return type is not a
17
+ // Promise (`any` / `unknown` / `never` pass through, matching the
18
+ // conservative `isPromiseTypedExpression` baseline used by the
19
+ // surrounding async family).
20
+ package linthost
21
+
22
+ import (
23
+ shimast "github.com/microsoft/typescript-go/shim/ast"
24
+ )
25
+
26
+ type promiseFunctionAsync struct{}
27
+
28
+ func (promiseFunctionAsync) Name() string { return "typescript/promise-function-async" }
29
+ func (promiseFunctionAsync) NeedsTypeChecker() bool {
30
+ return true
31
+ }
32
+ func (promiseFunctionAsync) Visits() []shimast.Kind {
33
+ return []shimast.Kind{
34
+ shimast.KindFunctionDeclaration,
35
+ shimast.KindFunctionExpression,
36
+ shimast.KindArrowFunction,
37
+ shimast.KindMethodDeclaration,
38
+ }
39
+ }
40
+ func (promiseFunctionAsync) Check(ctx *Context, node *shimast.Node) {
41
+ if ctx.Checker == nil {
42
+ return
43
+ }
44
+ if hasAsyncModifier(node) {
45
+ return
46
+ }
47
+ // Skip overload signatures and abstract methods — only the
48
+ // implementation signature carries an executable body that the
49
+ // `async` keyword could wrap.
50
+ if node.Body() == nil {
51
+ return
52
+ }
53
+ if hasModifier(node, shimast.KindAbstractKeyword) {
54
+ return
55
+ }
56
+ sig := ctx.Checker.GetSignatureFromDeclaration(node)
57
+ if sig == nil {
58
+ return
59
+ }
60
+ returnType := ctx.Checker.GetReturnTypeOfSignature(sig)
61
+ if returnType == nil {
62
+ return
63
+ }
64
+ if !isPromiseTypedExpression(ctx.Checker, returnType) {
65
+ return
66
+ }
67
+ startPos := keywordStart(ctx.File, node, "function")
68
+ if startPos >= 0 {
69
+ ctx.ReportRange(startPos, startPos+len("function"), "Function returning a Promise should be declared `async` so synchronous throws surface as a rejected Promise.")
70
+ return
71
+ }
72
+ ctx.Report(node, "Function returning a Promise should be declared `async` so synchronous throws surface as a rejected Promise.")
73
+ }
74
+
75
+ func init() {
76
+ Register(promiseFunctionAsync{})
77
+ }
@@ -0,0 +1,113 @@
1
+ // typescript/related-getter-setter-pairs reports a `get` accessor
2
+ // whose declared return type does not match the parameter type of the
3
+ // companion `set` accessor on the same class. The accessor pair
4
+ // presents a single conceptual field at the call site — `obj.value =
5
+ // X` followed by `Y = obj.value` should round-trip with compatible
6
+ // types — but TypeScript otherwise lets the two accessors carry
7
+ // independent annotations. The rule fires when the writer cannot
8
+ // accept what the reader is about to surface.
9
+ // https://typescript-eslint.io/rules/related-getter-setter-pairs/
10
+ //
11
+ // Type-aware. The comparison resolves both sides through
12
+ // `GetTypeFromTypeNode` so type aliases, generic parameters, and union
13
+ // constituents collapse to the same set of values before the
14
+ // equality check. Without a Checker the rule short-circuits to a
15
+ // no-op the way the other type-aware rules do.
16
+ //
17
+ // The rule visits each `KindGetAccessor`. For every getter that
18
+ // carries a return-type annotation and lives inside a class
19
+ // declaration / expression, it locates the matching `set` accessor by
20
+ // name (using the same `classMembers` + `classMemberName` helpers as
21
+ // `class-literal-property-style`) and compares the annotated types
22
+ // bidirectionally — both sides must describe the same set of values
23
+ // for the accessor pair to round-trip safely.
24
+ package linthost
25
+
26
+ import (
27
+ shimast "github.com/microsoft/typescript-go/shim/ast"
28
+ )
29
+
30
+ type relatedGetterSetterPairs struct{}
31
+
32
+ func (relatedGetterSetterPairs) Name() string {
33
+ return "typescript/related-getter-setter-pairs"
34
+ }
35
+ func (relatedGetterSetterPairs) NeedsTypeChecker() bool { return true }
36
+ func (relatedGetterSetterPairs) Visits() []shimast.Kind {
37
+ return []shimast.Kind{shimast.KindGetAccessor}
38
+ }
39
+ func (relatedGetterSetterPairs) Check(ctx *Context, node *shimast.Node) {
40
+ if ctx.Checker == nil {
41
+ return
42
+ }
43
+ parent := node.Parent
44
+ if parent == nil ||
45
+ (parent.Kind != shimast.KindClassDeclaration && parent.Kind != shimast.KindClassExpression) {
46
+ return
47
+ }
48
+ getReturnType := node.Type()
49
+ if getReturnType == nil {
50
+ // No annotated return type — nothing to compare against.
51
+ return
52
+ }
53
+ name := classMemberName(node)
54
+ if name == "" {
55
+ return
56
+ }
57
+ setter := findClassSetterFor(parent, name)
58
+ if setter == nil {
59
+ return
60
+ }
61
+ setParamType := setterParameterType(setter)
62
+ if setParamType == nil {
63
+ return
64
+ }
65
+ getType := ctx.Checker.GetTypeFromTypeNode(getReturnType)
66
+ setType := ctx.Checker.GetTypeFromTypeNode(setParamType)
67
+ if getType == nil || setType == nil {
68
+ return
69
+ }
70
+ // Bidirectional assignability stands in for type identity — the
71
+ // same shape `no-unnecessary-type-assertion` uses. The accessor
72
+ // pair is safe only when reading and writing describe the same
73
+ // set of values.
74
+ if ctx.Checker.IsTypeAssignableTo(getType, setType) &&
75
+ ctx.Checker.IsTypeAssignableTo(setType, getType) {
76
+ return
77
+ }
78
+ ctx.Report(node, "Getter return type does not match the corresponding setter parameter type on `"+name+"`.")
79
+ }
80
+
81
+ // findClassSetterFor returns the `set` accessor declared on `class`
82
+ // whose name matches `name`, or nil when no such setter exists.
83
+ func findClassSetterFor(class *shimast.Node, name string) *shimast.Node {
84
+ for _, member := range classMembers(class) {
85
+ if member == nil || member.Kind != shimast.KindSetAccessor {
86
+ continue
87
+ }
88
+ if classMemberName(member) == name {
89
+ return member
90
+ }
91
+ }
92
+ return nil
93
+ }
94
+
95
+ // setterParameterType returns the type-annotation node of a `set`
96
+ // accessor's single parameter. A setter without a parameter, or with a
97
+ // parameter that carries no annotation, returns nil — the rule cannot
98
+ // compare against an inferred type.
99
+ func setterParameterType(setter *shimast.Node) *shimast.Node {
100
+ params := setter.Parameters()
101
+ if len(params) == 0 {
102
+ return nil
103
+ }
104
+ decl := params[0].AsParameterDeclaration()
105
+ if decl == nil {
106
+ return nil
107
+ }
108
+ return decl.Type
109
+ }
110
+
111
+ func init() {
112
+ Register(relatedGetterSetterPairs{})
113
+ }
@@ -0,0 +1,96 @@
1
+ // typescript/require-array-sort-compare: require an explicit comparator
2
+ // for `arr.sort()` and `arr.toSorted()`. Without a comparator, both
3
+ // methods coerce elements to strings before ordering them, so
4
+ // `[10, 2, 1].sort()` evaluates to `[1, 10, 2]` — almost never what the
5
+ // caller meant. The fix is to pass `(a, b) => a - b`-style comparator.
6
+ // typescript-eslint:
7
+ // https://typescript-eslint.io/rules/require-array-sort-compare/
8
+ package linthost
9
+
10
+ import (
11
+ shimast "github.com/microsoft/typescript-go/shim/ast"
12
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
13
+ )
14
+
15
+ // requireArraySortCompare reports `<arr>.sort()` and `<arr>.toSorted()`
16
+ // when no comparator argument is supplied. Type-aware: the Checker
17
+ // confirms the receiver is an array (or tuple) so user-defined methods
18
+ // named `sort` / `toSorted` on non-array types do not trip the rule.
19
+ type requireArraySortCompare struct{}
20
+
21
+ func (requireArraySortCompare) Name() string {
22
+ return "typescript/require-array-sort-compare"
23
+ }
24
+ func (requireArraySortCompare) NeedsTypeChecker() bool {
25
+ return true
26
+ }
27
+ func (requireArraySortCompare) Visits() []shimast.Kind {
28
+ return []shimast.Kind{shimast.KindCallExpression}
29
+ }
30
+ func (requireArraySortCompare) Check(ctx *Context, node *shimast.Node) {
31
+ if ctx.Checker == nil {
32
+ return
33
+ }
34
+ call := node.AsCallExpression()
35
+ if call == nil || call.Expression == nil {
36
+ return
37
+ }
38
+ // Only fire when the callee is a property access ending in `.sort`
39
+ // or `.toSorted` and the call passes zero arguments.
40
+ receiver, method, ok := promisePropertyAccessParts(call.Expression)
41
+ if !ok || (method != "sort" && method != "toSorted") {
42
+ return
43
+ }
44
+ if call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
45
+ return
46
+ }
47
+ if receiver == nil {
48
+ return
49
+ }
50
+ t := ctx.Checker.GetTypeAtLocation(receiver)
51
+ if t == nil {
52
+ return
53
+ }
54
+ if !requireArraySortCompareIsArrayLike(ctx.Checker, t) {
55
+ return
56
+ }
57
+ ctx.Report(node, "Require an explicit compareFunction for `."+method+"()` — without one, elements are coerced to strings before being ordered.")
58
+ }
59
+
60
+ // requireArraySortCompareIsArrayLike reports whether t is provably an
61
+ // array or a tuple. The check follows the same recurse-into-constituents
62
+ // pattern as `isAwaitable` so a union like `number[] | string[]` still
63
+ // resolves to "array" via every constituent. `any` / `unknown` / `never`
64
+ // are intentionally NOT treated as array-like — they propagate from
65
+ // generic helpers and would explode the false-positive volume.
66
+ func requireArraySortCompareIsArrayLike(checker *shimchecker.Checker, t *shimchecker.Type) bool {
67
+ if checker == nil || t == nil {
68
+ return false
69
+ }
70
+ flags := t.Flags()
71
+ if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
72
+ return false
73
+ }
74
+ if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
75
+ for _, part := range t.Types() {
76
+ if part == nil {
77
+ continue
78
+ }
79
+ if !requireArraySortCompareIsArrayLike(checker, part) {
80
+ return false
81
+ }
82
+ }
83
+ return true
84
+ }
85
+ if shimchecker.Checker_isArrayType(checker, t) {
86
+ return true
87
+ }
88
+ if shimchecker.IsTupleType(t) {
89
+ return true
90
+ }
91
+ return false
92
+ }
93
+
94
+ func init() {
95
+ Register(requireArraySortCompare{})
96
+ }