@ttsc/lint 0.14.0-dev.20260529.2 → 0.14.0

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 (380) hide show
  1. package/README.md +21 -23
  2. package/lib/defaultFormat.d.ts +10 -6
  3. package/lib/defaultFormat.js +10 -6
  4. package/lib/defaultFormat.js.map +1 -1
  5. package/lib/structures/ITtscLintConfig.d.ts +5 -5
  6. package/lib/structures/TtscLintRuleSetting.d.ts +19 -19
  7. package/lib/structures/{ITtscLintFormat.d.ts → format/ITtscLintFormat.d.ts} +45 -70
  8. package/lib/structures/format/ITtscLintFormat.js.map +1 -0
  9. package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +15 -0
  10. package/lib/structures/format/ITtscLintFormatJsDoc.js +3 -0
  11. package/lib/structures/format/ITtscLintFormatJsDoc.js.map +1 -0
  12. package/lib/structures/format/ITtscLintFormatSortImports.d.ts +54 -0
  13. package/lib/structures/format/ITtscLintFormatSortImports.js +3 -0
  14. package/lib/structures/format/ITtscLintFormatSortImports.js.map +1 -0
  15. package/lib/structures/format/index.d.ts +3 -0
  16. package/lib/structures/format/index.js +20 -0
  17. package/lib/structures/format/index.js.map +1 -0
  18. package/lib/structures/index.d.ts +1 -1
  19. package/lib/structures/index.js +1 -1
  20. package/lib/structures/index.js.map +1 -1
  21. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +18 -27
  22. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +4 -5
  23. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -1
  24. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +29 -33
  25. package/lib/structures/rules/ITtscLintContributorRules.d.ts +6 -7
  26. package/lib/structures/rules/ITtscLintCoreRules.d.ts +380 -408
  27. package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +5 -5
  28. package/lib/structures/rules/ITtscLintCypressRuleOptions.js +2 -2
  29. package/lib/structures/rules/ITtscLintCypressRules.d.ts +34 -38
  30. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +21 -24
  31. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +2 -2
  32. package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +51 -56
  33. package/lib/structures/rules/ITtscLintJestRules.d.ts +63 -74
  34. package/lib/structures/rules/ITtscLintJsdocRules.d.ts +47 -54
  35. package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +94 -102
  36. package/lib/structures/rules/ITtscLintNextjsRules.d.ts +50 -55
  37. package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +104 -122
  38. package/lib/structures/rules/ITtscLintPromiseRules.d.ts +62 -71
  39. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +4 -4
  40. package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +17 -19
  41. package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +8 -10
  42. package/lib/structures/rules/ITtscLintReactRuleOptions.js +1 -2
  43. package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -1
  44. package/lib/structures/rules/ITtscLintReactRules.d.ts +91 -104
  45. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +46 -54
  46. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +7 -8
  47. package/lib/structures/rules/ITtscLintSecurityRules.d.ts +45 -52
  48. package/lib/structures/rules/ITtscLintSolidRules.d.ts +53 -56
  49. package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +3 -4
  50. package/lib/structures/rules/ITtscLintStorybookRules.d.ts +60 -69
  51. package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +31 -37
  52. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +2 -3
  53. package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +102 -119
  54. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +460 -518
  55. package/lib/structures/rules/ITtscLintVitestRules.d.ts +43 -51
  56. package/linthost/config.go +89 -72
  57. package/linthost/config_format.go +265 -99
  58. package/linthost/directives.go +5 -1
  59. package/linthost/display_width.go +72 -0
  60. package/linthost/fix.go +26 -1
  61. package/linthost/format.go +222 -25
  62. package/linthost/format_editor_settings.go +262 -0
  63. package/linthost/lsp.go +8 -4
  64. package/linthost/print_dispatch.go +3 -0
  65. package/linthost/print_doc.go +13 -0
  66. package/linthost/print_engine.go +68 -11
  67. package/linthost/print_nodes_array.go +108 -5
  68. package/linthost/print_nodes_call.go +515 -74
  69. package/linthost/print_nodes_function.go +25 -0
  70. package/linthost/print_nodes_imports.go +47 -14
  71. package/linthost/print_nodes_list.go +191 -8
  72. package/linthost/print_nodes_object.go +12 -6
  73. package/linthost/print_nodes_ternary.go +106 -0
  74. package/linthost/rules_boundaries.go +400 -400
  75. package/linthost/rules_camelcase.go +81 -81
  76. package/linthost/rules_complexity.go +52 -51
  77. package/linthost/rules_consistent_return.go +81 -81
  78. package/linthost/rules_core_extra.go +472 -472
  79. package/linthost/rules_curly.go +43 -43
  80. package/linthost/rules_default_case.go +15 -15
  81. package/linthost/rules_default_case_last.go +22 -22
  82. package/linthost/rules_format_arrow_parens.go +167 -0
  83. package/linthost/rules_format_bracket_spacing.go +117 -0
  84. package/linthost/rules_format_clause_join.go +172 -0
  85. package/linthost/rules_format_declaration_header.go +664 -0
  86. package/linthost/rules_format_indent.go +616 -0
  87. package/linthost/rules_format_jsdoc.go +4 -4
  88. package/linthost/rules_format_orphan_semi.go +106 -0
  89. package/linthost/rules_format_parameter_properties.go +175 -0
  90. package/linthost/rules_format_print_width.go +185 -69
  91. package/linthost/rules_format_quote_props.go +169 -0
  92. package/linthost/rules_format_quotes.go +75 -29
  93. package/linthost/rules_format_semi.go +185 -10
  94. package/linthost/rules_format_sort_imports.go +456 -121
  95. package/linthost/rules_format_statement_split.go +368 -0
  96. package/linthost/rules_format_ternary_nullish_parens.go +77 -0
  97. package/linthost/rules_format_trailing_comma.go +21 -2
  98. package/linthost/rules_format_whitespace.go +336 -0
  99. package/linthost/rules_functional.go +688 -688
  100. package/linthost/rules_gap.go +146 -11
  101. package/linthost/rules_grouped_accessor_pairs.go +60 -60
  102. package/linthost/rules_guard_for_in.go +122 -122
  103. package/linthost/rules_id_length.go +44 -44
  104. package/linthost/rules_imports.go +11 -3
  105. package/linthost/rules_init_declarations.go +25 -25
  106. package/linthost/rules_jsx_a11y.go +844 -844
  107. package/linthost/rules_max_classes_per_file.go +24 -24
  108. package/linthost/rules_max_depth.go +43 -43
  109. package/linthost/rules_max_lines.go +53 -53
  110. package/linthost/rules_max_lines_per_function.go +28 -28
  111. package/linthost/rules_max_nested_callbacks.go +26 -26
  112. package/linthost/rules_max_params.go +20 -20
  113. package/linthost/rules_max_statements.go +29 -29
  114. package/linthost/rules_nextjs.go +5 -5
  115. package/linthost/rules_no_else_return.go +38 -38
  116. package/linthost/rules_no_empty_named_blocks.go +45 -45
  117. package/linthost/rules_no_import_assign.go +96 -96
  118. package/linthost/rules_no_invalid_this.go +19 -19
  119. package/linthost/rules_no_loop_func.go +43 -43
  120. package/linthost/rules_no_magic_numbers.go +60 -60
  121. package/linthost/rules_no_mixed_operators.go +44 -44
  122. package/linthost/rules_no_param_reassign.go +64 -64
  123. package/linthost/rules_no_restricted_imports.go +45 -45
  124. package/linthost/rules_no_restricted_syntax.go +17 -17
  125. package/linthost/rules_no_shadow.go +112 -112
  126. package/linthost/rules_no_unreachable.go +36 -36
  127. package/linthost/rules_no_useless_assignment.go +79 -79
  128. package/linthost/rules_no_useless_constructor.go +167 -167
  129. package/linthost/rules_no_useless_return.go +28 -28
  130. package/linthost/rules_prefer_arrow_callback.go +74 -74
  131. package/linthost/rules_prefer_destructuring.go +61 -61
  132. package/linthost/rules_prefer_named_capture_group.go +87 -87
  133. package/linthost/rules_prefer_rest_params.go +61 -61
  134. package/linthost/rules_promise.go +800 -800
  135. package/linthost/rules_react.go +600 -600
  136. package/linthost/rules_react_extras.go +162 -162
  137. package/linthost/rules_react_hooks.go +2 -2
  138. package/linthost/rules_react_perf.go +231 -231
  139. package/linthost/rules_regexp.go +293 -293
  140. package/linthost/rules_security.go +613 -613
  141. package/linthost/rules_sort_imports.go +37 -37
  142. package/linthost/rules_sort_keys.go +88 -88
  143. package/linthost/rules_suggestions.go +13 -0
  144. package/linthost/rules_ts.go +2 -2
  145. package/linthost/rules_ts_async.go +247 -247
  146. package/linthost/rules_ts_class_literal_property_style.go +64 -64
  147. package/linthost/rules_ts_consistent_type_exports.go +209 -209
  148. package/linthost/rules_ts_explicit_function_return_type.go +35 -35
  149. package/linthost/rules_ts_extra.go +6 -2
  150. package/linthost/rules_ts_no_base_to_string.go +140 -140
  151. package/linthost/rules_ts_no_confusing_void.go +28 -28
  152. package/linthost/rules_ts_no_deprecated.go +110 -110
  153. package/linthost/rules_ts_no_for_in_array.go +45 -45
  154. package/linthost/rules_ts_no_magic_numbers.go +67 -67
  155. package/linthost/rules_ts_no_meaningless_void_operator.go +44 -44
  156. package/linthost/rules_ts_no_misused_spread.go +64 -64
  157. package/linthost/rules_ts_no_redundant_type_constituents.go +72 -72
  158. package/linthost/rules_ts_no_restricted_types.go +28 -28
  159. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +87 -87
  160. package/linthost/rules_ts_no_unnecessary_condition.go +184 -184
  161. package/linthost/rules_ts_no_unnecessary_qualifier.go +72 -72
  162. package/linthost/rules_ts_no_unnecessary_template_expression.go +120 -120
  163. package/linthost/rules_ts_no_unnecessary_type_arguments.go +171 -171
  164. package/linthost/rules_ts_no_unnecessary_type_assertion.go +90 -90
  165. package/linthost/rules_ts_no_unsafe_argument.go +50 -50
  166. package/linthost/rules_ts_no_unsafe_assignment.go +60 -60
  167. package/linthost/rules_ts_no_unsafe_call.go +40 -40
  168. package/linthost/rules_ts_no_unsafe_enum_comparison.go +147 -147
  169. package/linthost/rules_ts_no_unsafe_member_access.go +29 -29
  170. package/linthost/rules_ts_no_unsafe_return.go +64 -64
  171. package/linthost/rules_ts_no_unsafe_unary_minus.go +42 -42
  172. package/linthost/rules_ts_no_useless_constructor.go +20 -20
  173. package/linthost/rules_ts_non_nullable_type_assertion_style.go +60 -60
  174. package/linthost/rules_ts_parameter_properties.go +10 -10
  175. package/linthost/rules_ts_prefer_find.go +116 -116
  176. package/linthost/rules_ts_prefer_includes.go +173 -173
  177. package/linthost/rules_ts_prefer_nullish_coalescing.go +54 -54
  178. package/linthost/rules_ts_prefer_optional_chain.go +156 -156
  179. package/linthost/rules_ts_prefer_promise_reject_errors.go +104 -104
  180. package/linthost/rules_ts_prefer_readonly.go +31 -31
  181. package/linthost/rules_ts_prefer_reduce_type_parameter.go +74 -74
  182. package/linthost/rules_ts_prefer_regexp_exec.go +69 -69
  183. package/linthost/rules_ts_prefer_return_this_type.go +79 -79
  184. package/linthost/rules_ts_prefer_string_starts_ends_with.go +237 -237
  185. package/linthost/rules_ts_promise_function_async.go +41 -41
  186. package/linthost/rules_ts_related_getter_setter_pairs.go +61 -61
  187. package/linthost/rules_ts_require_array_sort_compare.go +58 -58
  188. package/linthost/rules_ts_restrict_plus_operands.go +82 -82
  189. package/linthost/rules_ts_restrict_template_expressions.go +50 -50
  190. package/linthost/rules_ts_simple.go +249 -249
  191. package/linthost/rules_ts_sort_type_constituents.go +88 -88
  192. package/linthost/rules_ts_strict_boolean_expressions.go +103 -103
  193. package/linthost/rules_ts_switch_exhaustiveness_check.go +90 -90
  194. package/linthost/rules_ts_unbound_method.go +65 -65
  195. package/linthost/rules_tsdoc.go +88 -88
  196. package/linthost/rules_unicorn_better_regex.go +1 -1
  197. package/linthost/rules_unicorn_catch_error_name.go +19 -19
  198. package/linthost/rules_unicorn_consistent_assert.go +20 -20
  199. package/linthost/rules_unicorn_consistent_date_clone.go +27 -27
  200. package/linthost/rules_unicorn_consistent_destructuring.go +3 -3
  201. package/linthost/rules_unicorn_consistent_empty_array_spread.go +34 -34
  202. package/linthost/rules_unicorn_consistent_existence_index_check.go +42 -42
  203. package/linthost/rules_unicorn_consistent_function_scoping.go +3 -3
  204. package/linthost/rules_unicorn_consistent_template_literal_escape.go +3 -3
  205. package/linthost/rules_unicorn_custom_error_definition.go +92 -92
  206. package/linthost/rules_unicorn_empty_brace_spaces.go +47 -47
  207. package/linthost/rules_unicorn_error_message.go +34 -34
  208. package/linthost/rules_unicorn_escape_case.go +14 -14
  209. package/linthost/rules_unicorn_expiring_todo_comments.go +36 -36
  210. package/linthost/rules_unicorn_explicit_length_check.go +51 -51
  211. package/linthost/rules_unicorn_filename_case.go +1 -1
  212. package/linthost/rules_unicorn_import_style.go +1 -1
  213. package/linthost/rules_unicorn_isolated_functions.go +2 -2
  214. package/linthost/rules_unicorn_new_for_builtins.go +49 -49
  215. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +31 -31
  216. package/linthost/rules_unicorn_no_accessor_recursion.go +69 -69
  217. package/linthost/rules_unicorn_no_anonymous_default_export.go +40 -40
  218. package/linthost/rules_unicorn_no_array_callback_reference.go +33 -33
  219. package/linthost/rules_unicorn_no_array_for_each.go +14 -14
  220. package/linthost/rules_unicorn_no_array_method_this_argument.go +29 -29
  221. package/linthost/rules_unicorn_no_array_reduce.go +15 -15
  222. package/linthost/rules_unicorn_no_array_reverse.go +17 -17
  223. package/linthost/rules_unicorn_no_array_sort.go +17 -17
  224. package/linthost/rules_unicorn_no_await_expression_member.go +12 -12
  225. package/linthost/rules_unicorn_no_await_in_promise_methods.go +38 -38
  226. package/linthost/rules_unicorn_no_console_spaces.go +30 -30
  227. package/linthost/rules_unicorn_no_document_cookie.go +13 -13
  228. package/linthost/rules_unicorn_no_empty_file.go +16 -16
  229. package/linthost/rules_unicorn_no_for_loop.go +58 -58
  230. package/linthost/rules_unicorn_no_hex_escape.go +11 -11
  231. package/linthost/rules_unicorn_no_immediate_mutation.go +55 -55
  232. package/linthost/rules_unicorn_no_instanceof_builtins.go +34 -34
  233. package/linthost/rules_unicorn_no_invalid_fetch_options.go +52 -52
  234. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +25 -25
  235. package/linthost/rules_unicorn_no_keyword_prefix.go +56 -56
  236. package/linthost/rules_unicorn_no_lonely_if.go +21 -21
  237. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +22 -22
  238. package/linthost/rules_unicorn_no_named_default.go +25 -25
  239. package/linthost/rules_unicorn_no_negated_condition.go +54 -54
  240. package/linthost/rules_unicorn_no_negation_in_equality_check.go +23 -23
  241. package/linthost/rules_unicorn_no_nested_ternary.go +15 -15
  242. package/linthost/rules_unicorn_no_new_array.go +5 -5
  243. package/linthost/rules_unicorn_no_new_buffer.go +5 -5
  244. package/linthost/rules_unicorn_no_null.go +2 -2
  245. package/linthost/rules_unicorn_no_object_as_default_parameter.go +19 -19
  246. package/linthost/rules_unicorn_no_process_exit.go +17 -17
  247. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +29 -29
  248. package/linthost/rules_unicorn_no_static_only_class.go +27 -27
  249. package/linthost/rules_unicorn_no_thenable.go +47 -47
  250. package/linthost/rules_unicorn_no_this_assignment.go +10 -10
  251. package/linthost/rules_unicorn_no_typeof_undefined.go +31 -31
  252. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +23 -23
  253. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +37 -37
  254. package/linthost/rules_unicorn_no_unnecessary_await.go +24 -24
  255. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +22 -22
  256. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +67 -67
  257. package/linthost/rules_unicorn_no_unreadable_iife.go +22 -22
  258. package/linthost/rules_unicorn_no_useless_collection_argument.go +34 -34
  259. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +18 -18
  260. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +49 -49
  261. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +41 -41
  262. package/linthost/rules_unicorn_no_useless_length_check.go +82 -82
  263. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +42 -42
  264. package/linthost/rules_unicorn_no_useless_spread.go +38 -38
  265. package/linthost/rules_unicorn_no_useless_switch_case.go +29 -29
  266. package/linthost/rules_unicorn_no_useless_undefined.go +25 -25
  267. package/linthost/rules_unicorn_no_zero_fractions.go +17 -17
  268. package/linthost/rules_unicorn_number_literal_case.go +33 -33
  269. package/linthost/rules_unicorn_numeric_separators_style.go +36 -36
  270. package/linthost/rules_unicorn_prefer_add_event_listener.go +30 -30
  271. package/linthost/rules_unicorn_prefer_array_find.go +27 -27
  272. package/linthost/rules_unicorn_prefer_array_flat.go +25 -25
  273. package/linthost/rules_unicorn_prefer_array_flat_map.go +37 -37
  274. package/linthost/rules_unicorn_prefer_array_index_of.go +108 -108
  275. package/linthost/rules_unicorn_prefer_array_some.go +43 -43
  276. package/linthost/rules_unicorn_prefer_at.go +42 -42
  277. package/linthost/rules_unicorn_prefer_bigint_literals.go +42 -42
  278. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +15 -15
  279. package/linthost/rules_unicorn_prefer_class_fields.go +57 -57
  280. package/linthost/rules_unicorn_prefer_classlist_toggle.go +76 -76
  281. package/linthost/rules_unicorn_prefer_code_point.go +21 -21
  282. package/linthost/rules_unicorn_prefer_date_now.go +47 -47
  283. package/linthost/rules_unicorn_prefer_default_parameters.go +96 -96
  284. package/linthost/rules_unicorn_prefer_dom_node_append.go +14 -14
  285. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +27 -27
  286. package/linthost/rules_unicorn_prefer_dom_node_remove.go +17 -17
  287. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +11 -11
  288. package/linthost/rules_unicorn_prefer_event_target.go +10 -10
  289. package/linthost/rules_unicorn_prefer_export_from.go +99 -99
  290. package/linthost/rules_unicorn_prefer_global_this.go +91 -91
  291. package/linthost/rules_unicorn_prefer_import_meta_properties.go +38 -38
  292. package/linthost/rules_unicorn_prefer_includes.go +108 -108
  293. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +37 -37
  294. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +10 -10
  295. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +20 -20
  296. package/linthost/rules_unicorn_prefer_math_min_max.go +33 -33
  297. package/linthost/rules_unicorn_prefer_math_trunc.go +32 -32
  298. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +18 -18
  299. package/linthost/rules_unicorn_prefer_modern_math_apis.go +38 -38
  300. package/linthost/rules_unicorn_prefer_module.go +22 -22
  301. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +73 -73
  302. package/linthost/rules_unicorn_prefer_negative_index.go +46 -46
  303. package/linthost/rules_unicorn_prefer_node_protocol.go +90 -90
  304. package/linthost/rules_unicorn_prefer_number_properties.go +57 -57
  305. package/linthost/rules_unicorn_prefer_object_from_entries.go +26 -26
  306. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
  307. package/linthost/rules_unicorn_prefer_prototype_methods.go +25 -25
  308. package/linthost/rules_unicorn_prefer_query_selector.go +25 -25
  309. package/linthost/rules_unicorn_prefer_reflect_apply.go +10 -10
  310. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +47 -47
  311. package/linthost/rules_unicorn_prefer_response_static_json.go +26 -26
  312. package/linthost/rules_unicorn_prefer_set_has.go +16 -16
  313. package/linthost/rules_unicorn_prefer_set_size.go +22 -22
  314. package/linthost/rules_unicorn_prefer_simple_condition_first.go +3 -3
  315. package/linthost/rules_unicorn_prefer_single_call.go +59 -59
  316. package/linthost/rules_unicorn_prefer_spread.go +24 -24
  317. package/linthost/rules_unicorn_prefer_string_raw.go +12 -12
  318. package/linthost/rules_unicorn_prefer_string_replace_all.go +46 -46
  319. package/linthost/rules_unicorn_prefer_string_slice.go +17 -17
  320. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +104 -104
  321. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +21 -21
  322. package/linthost/rules_unicorn_prefer_structured_clone.go +36 -36
  323. package/linthost/rules_unicorn_prefer_switch.go +65 -65
  324. package/linthost/rules_unicorn_prefer_ternary.go +35 -35
  325. package/linthost/rules_unicorn_prefer_top_level_await.go +34 -34
  326. package/linthost/rules_unicorn_prefer_type_error.go +84 -84
  327. package/linthost/rules_unicorn_prevent_abbreviations.go +68 -68
  328. package/linthost/rules_unicorn_relative_url_style.go +19 -19
  329. package/linthost/rules_unicorn_require_array_join_separator.go +18 -18
  330. package/linthost/rules_unicorn_require_module_attributes.go +25 -25
  331. package/linthost/rules_unicorn_require_module_specifiers.go +28 -28
  332. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +18 -18
  333. package/linthost/rules_unicorn_require_post_message_target_origin.go +18 -18
  334. package/linthost/rules_unicorn_string_content.go +1 -1
  335. package/linthost/rules_unicorn_switch_case_braces.go +16 -16
  336. package/linthost/rules_unicorn_switch_case_break_position.go +3 -3
  337. package/linthost/rules_unicorn_template_indent.go +1 -1
  338. package/linthost/rules_unicorn_text_encoding_identifier_case.go +36 -36
  339. package/linthost/rules_unicorn_throw_new_error.go +30 -30
  340. package/linthost/rules_var.go +273 -3
  341. package/package.json +3 -3
  342. package/src/defaultFormat.ts +11 -7
  343. package/src/structures/ITtscLintConfig.ts +5 -5
  344. package/src/structures/TtscLintRuleSetting.ts +19 -19
  345. package/src/structures/{ITtscLintFormat.ts → format/ITtscLintFormat.ts} +48 -75
  346. package/src/structures/format/ITtscLintFormatJsDoc.ts +16 -0
  347. package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
  348. package/src/structures/format/index.ts +3 -0
  349. package/src/structures/index.ts +1 -1
  350. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +20 -31
  351. package/src/structures/rules/ITtscLintBoundariesRules.ts +32 -36
  352. package/src/structures/rules/ITtscLintContributorRules.ts +6 -7
  353. package/src/structures/rules/ITtscLintCoreRules.ts +380 -408
  354. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +5 -5
  355. package/src/structures/rules/ITtscLintCypressRules.ts +34 -38
  356. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +28 -41
  357. package/src/structures/rules/ITtscLintFunctionalRules.ts +51 -56
  358. package/src/structures/rules/ITtscLintJestRules.ts +63 -74
  359. package/src/structures/rules/ITtscLintJsdocRules.ts +47 -54
  360. package/src/structures/rules/ITtscLintJsxA11yRules.ts +94 -102
  361. package/src/structures/rules/ITtscLintNextjsRules.ts +50 -55
  362. package/src/structures/rules/ITtscLintPlaywrightRules.ts +104 -122
  363. package/src/structures/rules/ITtscLintPromiseRules.ts +62 -71
  364. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +4 -4
  365. package/src/structures/rules/ITtscLintReactPerfRules.ts +17 -19
  366. package/src/structures/rules/ITtscLintReactRuleOptions.ts +8 -10
  367. package/src/structures/rules/ITtscLintReactRules.ts +91 -104
  368. package/src/structures/rules/ITtscLintRegexpRules.ts +46 -54
  369. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +7 -8
  370. package/src/structures/rules/ITtscLintSecurityRules.ts +45 -52
  371. package/src/structures/rules/ITtscLintSolidRules.ts +53 -56
  372. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +3 -4
  373. package/src/structures/rules/ITtscLintStorybookRules.ts +60 -69
  374. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +31 -37
  375. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +2 -3
  376. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +102 -119
  377. package/src/structures/rules/ITtscLintTypeScriptRules.ts +460 -518
  378. package/src/structures/rules/ITtscLintVitestRules.ts +43 -51
  379. package/lib/structures/ITtscLintFormat.js.map +0 -1
  380. /package/lib/structures/{ITtscLintFormat.js → format/ITtscLintFormat.js} +0 -0
@@ -2,6 +2,7 @@ package linthost
2
2
 
3
3
  import (
4
4
  shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
5
6
  )
6
7
 
7
8
  // printCallExpression renders a CallExpression with width-aware
@@ -46,12 +47,22 @@ func printCallExpression(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
46
47
  }
47
48
  if call.TypeArguments != nil {
48
49
  // Verbatim range covering `<A, B>` punctuation and members.
49
- parts = append(parts, verbatimRange(ctx.Source, callTypeArgsStart(ctx, call), callTypeArgsEnd(ctx, call)))
50
+ parts = append(parts, verbatimRange(ctx.Source, typeArgsStart(ctx.Source, call.TypeArguments), typeArgsEnd(ctx.Source, call.TypeArguments)))
50
51
  }
51
52
  if hasNilEntry(call.Arguments) {
52
53
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
53
54
  }
54
- argDoc, argCovered := printArgList(ctx, call.Arguments)
55
+ // Prettier never appends a trailing comma inside a dynamic
56
+ // `import(...)`, so the printer's reflow must agree with
57
+ // format/trailing-comma's same exception (see isDynamicImportCall).
58
+ addComma := ctx.allowsCallArgumentTrailingComma() && !isDynamicImportCall(call)
59
+ // A decorator's call hugs its last argument even past leading callbacks
60
+ // (`@OneToMany(() => P, (p) => p.c, { … })`); a plain call explodes there.
61
+ decoratorCall := node.Parent != nil && node.Parent.Kind == shimast.KindDecorator
62
+ // A test-framework call (`test("desc", () => { … })`) hugs its callback even
63
+ // when the description string pushes the opening line past printWidth, so the
64
+ // exploded fallback is dropped for it. See isTestCall.
65
+ argDoc, argCovered := printArgList(ctx, call.Arguments, addComma, decoratorCall, isTestCall(node))
55
66
  parts = append(parts, argDoc)
56
67
  return Concat(parts...), covered && argCovered
57
68
  }
@@ -77,13 +88,13 @@ func printNewExpression(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
77
88
  covered = covered && !nodeSpansMultipleLines(ctx, ne.Expression)
78
89
  }
79
90
  if ne.TypeArguments != nil {
80
- parts = append(parts, verbatimRange(ctx.Source, newTypeArgsStart(ctx, ne), newTypeArgsEnd(ctx, ne)))
91
+ parts = append(parts, verbatimRange(ctx.Source, typeArgsStart(ctx.Source, ne.TypeArguments), typeArgsEnd(ctx.Source, ne.TypeArguments)))
81
92
  }
82
93
  if ne.Arguments != nil {
83
94
  if hasNilEntry(ne.Arguments) {
84
95
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
85
96
  }
86
- argDoc, argCovered := printArgList(ctx, ne.Arguments)
97
+ argDoc, argCovered := printArgList(ctx, ne.Arguments, ctx.allowsCallArgumentTrailingComma(), false, false)
87
98
  parts = append(parts, argDoc)
88
99
  covered = covered && argCovered
89
100
  }
@@ -116,7 +127,14 @@ func hasNilEntry(list *shimast.NodeList) bool {
116
127
  // layout, so the parens stay attached and the preceding arguments are
117
128
  // not exploded onto their own lines. This is the Prettier behavior for
118
129
  // `foo(x, () => { … })`.
119
- func printArgList(ctx *PrintContext, list *shimast.NodeList) (Doc, bool) {
130
+ // `addComma` is supplied by the caller (which honors `format.trailingComma`
131
+ // and the dynamic-import exception) rather than read here, so call and new
132
+ // expressions can diverge on the comma without printArgList knowing the
133
+ // callee. Call / new argument lists accepted trailing commas only in
134
+ // ES2017+, so Prettier's "es5" and "none" modes pass false; otherwise the
135
+ // printer would oscillate against format/trailing-comma on every cascade
136
+ // pass (rxjs hit this on ajax.ts and several operators / testing helpers).
137
+ func printArgList(ctx *PrintContext, list *shimast.NodeList, addComma bool, decoratorCall bool, forceHugLast bool) (Doc, bool) {
120
138
  if list == nil {
121
139
  return Text("()"), true
122
140
  }
@@ -127,24 +145,290 @@ func printArgList(ctx *PrintContext, list *shimast.NodeList) (Doc, bool) {
127
145
  covered = covered && childCovered
128
146
  items = append(items, doc)
129
147
  }
130
- // AddComma honors `format.trailingComma`: call / new argument lists
131
- // accepted trailing commas only in ES2017+, so Prettier's "es5" and
132
- // "none" modes skip them here. Hardcoding `true` would oscillate
133
- // against Prettier on every cascade pass on any project configured
134
- // with "es5" (rxjs hit this on ajax.ts and several operators / testing
135
- // helpers the rule said "no comma needed" while the printer added
136
- // one back on its own reflow).
148
+ // Last-argument hugging wins when both predicates match; first-arg
149
+ // hugging only applies to the two-argument `callback, simpleArg` shape.
150
+ // Decline last-arg hugging when a leading argument forces the call
151
+ // multi-line on its own, so Prettier explodes the whole call rather than
152
+ // hugging the last argument. Two leading shapes trigger that: a function
153
+ // or arrow expression (`useMemo(() => x, [deps])`, even with an inline
154
+ // expression body Prettier never hugs after it), and any argument whose
155
+ // doc carries hard breaks (a block-bodied callback). A leading object or
156
+ // array that merely fits flat does NOT decline — Prettier still hugs the
157
+ // last argument there (`doConfigure({ … }, () => { … })`).
158
+ // A decorator's call hugs its last argument even past leading callbacks
159
+ // (`@OneToMany(() => P, (p) => p.c, { … })` keeps the arrows inline and
160
+ // breaks only the object); a plain call with two or more leading callbacks
161
+ // explodes instead, so the function-arg gate applies only off a decorator.
162
+ hugLast := shouldHugLastArgument(list.Nodes) &&
163
+ !anyLeadingItemBreaks(items) &&
164
+ (decoratorCall || !anyLeadingArgIsFunctionLike(list.Nodes))
165
+ // Two or more function/arrow arguments force the list to explode, one per
166
+ // line, even when it would fit flat: Prettier always breaks a call carrying
167
+ // multiple callbacks (`promise.then(() => a, () => b)`), the
168
+ // function-composition rule. The sole exception is a decorator hugging a
169
+ // huggable trailing argument (`@OneToMany(() => P, (p) => p.c, { … })`),
170
+ // where the leading arrows stay inline and only the object breaks — so the
171
+ // gate defers to hugLast off a decorator. See argListForcesFunctionBreak.
172
+ forceFnBreak := argListForcesFunctionBreak(list.Nodes, decoratorCall)
173
+ if forceFnBreak {
174
+ hugLast = false
175
+ }
176
+ // A test-framework call always hugs its trailing callback (the description
177
+ // string may overflow the open line); force the hug and drop the exploded
178
+ // fallback via HugLastForce.
179
+ if forceHugLast {
180
+ hugLast = true
181
+ forceFnBreak = false
182
+ }
137
183
  shape := listShape{
138
- OpenTok: "(",
139
- CloseTok: ")",
140
- Items: items,
141
- Space: false,
142
- AddComma: ctx.allowsCallArgumentTrailingComma(),
143
- HugLast: shouldHugLastArgument(list.Nodes),
184
+ OpenTok: "(",
185
+ CloseTok: ")",
186
+ Items: items,
187
+ Space: false,
188
+ AddComma: addComma,
189
+ HugLast: hugLast,
190
+ HugLastForce: forceHugLast,
191
+ HugFirst: !hugLast && !forceFnBreak && shouldHugFirstArgument(ctx, list.Nodes),
192
+ ForceBreak: forceFnBreak,
193
+ BlankBefore: blankBeforeItems(ctx.Source, list.Nodes),
144
194
  }
145
195
  return printList(ctx, shape), covered
146
196
  }
147
197
 
198
+ // testCalleePatterns is Prettier's exact testCallCalleePatterns set (the dotted
199
+ // callee chains its isTestCallCallee matches). Matching the precise set, rather
200
+ // than "any base with a {only,skip,…} tail", avoids both over-matching
201
+ // (`test.each`, `it.todo` are NOT test calls) and under-matching (`test.fixme`,
202
+ // `test.describe.only` ARE).
203
+ var testCalleePatterns = map[string]bool{
204
+ "it": true, "it.only": true, "it.skip": true,
205
+ "describe": true, "describe.only": true, "describe.skip": true,
206
+ "test": true, "test.only": true, "test.skip": true,
207
+ "test.fixme": true, "test.step": true,
208
+ "test.describe": true, "test.describe.only": true,
209
+ "test.describe.skip": true, "test.describe.fixme": true,
210
+ "test.describe.parallel": true, "test.describe.parallel.only": true,
211
+ "test.describe.serial": true, "test.describe.serial.only": true,
212
+ "skip": true, "xit": true, "xdescribe": true, "xtest": true,
213
+ "fit": true, "fdescribe": true, "ftest": true,
214
+ }
215
+
216
+ // calleeChain builds the dotted member-access chain of a callee rooted at a
217
+ // plain identifier (`test.describe.only` becomes "test.describe.only"), or ""
218
+ // when the callee is not a pure identifier / property-access chain (a computed
219
+ // access, a call, `this`, etc.).
220
+ func calleeChain(node *shimast.Node) string {
221
+ switch node.Kind {
222
+ case shimast.KindIdentifier:
223
+ return identifierText(node)
224
+ case shimast.KindPropertyAccessExpression:
225
+ pa := node.AsPropertyAccessExpression()
226
+ if pa == nil || pa.Name() == nil || pa.Name().Kind != shimast.KindIdentifier {
227
+ return ""
228
+ }
229
+ base := calleeChain(pa.Expression)
230
+ if base == "" {
231
+ return ""
232
+ }
233
+ return base + "." + identifierText(pa.Name())
234
+ }
235
+ return ""
236
+ }
237
+
238
+ // testCalleeName reports whether `callee` is one of Prettier's recognized
239
+ // test-framework callees (see testCalleePatterns).
240
+ func testCalleeName(callee *shimast.Node) bool {
241
+ return callee != nil && testCalleePatterns[calleeChain(callee)]
242
+ }
243
+
244
+ // isTestCall reports whether `node` is a test-framework call Prettier prints
245
+ // with its callback hugged regardless of width: `it(...)` / `test(...)` /
246
+ // `describe(...)` (and focus/skip variants) called with a string or template
247
+ // description and a trailing function/arrow. Prettier never explodes such a
248
+ // call's arguments, so the printer keeps the callback on the open-paren line
249
+ // even when the description string overflows printWidth (the callback's
250
+ // parameter count does not matter, verified against Prettier). Scoped to the
251
+ // two-argument form (the overwhelming shape); a three-argument timeout variant
252
+ // falls through to ordinary hugging.
253
+ func isTestCall(node *shimast.Node) bool {
254
+ if node == nil || node.Kind != shimast.KindCallExpression {
255
+ return false
256
+ }
257
+ call := node.AsCallExpression()
258
+ if call == nil || call.QuestionDotToken != nil || call.Arguments == nil {
259
+ return false
260
+ }
261
+ args := call.Arguments.Nodes
262
+ // Prettier accepts a two- or three-argument test call (the third is a numeric
263
+ // timeout): `it("name", () => { … })` and `it("name", () => { … }, 2500)`.
264
+ if len(args) != 2 && len(args) != 3 {
265
+ return false
266
+ }
267
+ if args[0] == nil || args[1] == nil {
268
+ return false
269
+ }
270
+ switch args[0].Kind {
271
+ case shimast.KindStringLiteral,
272
+ shimast.KindNoSubstitutionTemplateLiteral,
273
+ shimast.KindTemplateExpression:
274
+ default:
275
+ return false
276
+ }
277
+ if !testCalleeName(call.Expression) {
278
+ return false
279
+ }
280
+ if len(args) == 3 {
281
+ // The timeout argument must be numeric, and the callback a block-bodied
282
+ // function/arrow taking at most one parameter (Prettier's
283
+ // isFunctionOrArrowExpressionWithBody + parameter-count gate).
284
+ if args[2] == nil || args[2].Kind != shimast.KindNumericLiteral {
285
+ return false
286
+ }
287
+ return isBlockBodiedCallback(args[1]) && len(args[1].Parameters()) <= 1
288
+ }
289
+ return isFunctionLikeArg(args[1])
290
+ }
291
+
292
+ // isBlockBodiedCallback reports whether a node is a function expression or an
293
+ // arrow with a block body (Prettier's isFunctionOrArrowExpressionWithBody).
294
+ func isBlockBodiedCallback(node *shimast.Node) bool {
295
+ if node == nil {
296
+ return false
297
+ }
298
+ switch node.Kind {
299
+ case shimast.KindFunctionExpression:
300
+ return true
301
+ case shimast.KindArrowFunction:
302
+ a := node.AsArrowFunction()
303
+ return a != nil && a.Body != nil && a.Body.Kind == shimast.KindBlock
304
+ }
305
+ return false
306
+ }
307
+
308
+ // isFunctionLikeArg reports whether an argument is a function or arrow
309
+ // expression, the two shapes Prettier counts as "function" arguments.
310
+ func isFunctionLikeArg(node *shimast.Node) bool {
311
+ if node == nil {
312
+ return false
313
+ }
314
+ switch node.Kind {
315
+ case shimast.KindArrowFunction, shimast.KindFunctionExpression:
316
+ return true
317
+ }
318
+ return false
319
+ }
320
+
321
+ // callArgsContainFunctionLike reports whether a CALL expression's own argument
322
+ // list carries a function/arrow argument. Prettier's function-composition test
323
+ // treats `foo.map((x) => x)` as a composed call, so a call carrying it
324
+ // alongside any second argument explodes. Only a call counts: Prettier's
325
+ // isCallExpression matches CallExpression / OptionalCallExpression but NOT a
326
+ // NewExpression, so `foo(new Bar(() => x), other)` is left inline.
327
+ func callArgsContainFunctionLike(node *shimast.Node) bool {
328
+ if node == nil || node.Kind != shimast.KindCallExpression {
329
+ return false
330
+ }
331
+ c := node.AsCallExpression()
332
+ if c == nil || c.Arguments == nil {
333
+ return false
334
+ }
335
+ for _, a := range c.Arguments.Nodes {
336
+ if isFunctionLikeArg(a) {
337
+ return true
338
+ }
339
+ }
340
+ return false
341
+ }
342
+
343
+ // isFunctionCompositionArgs mirrors Prettier's predicate of the same name: a
344
+ // call with two or more arguments is "function composition" when either two of
345
+ // them are themselves functions/arrows, or one is a call/new whose own
346
+ // arguments include a function/arrow (`stream.pipe(map((x) => x), other)`).
347
+ // Prettier breaks such a list onto one-argument-per-line unconditionally.
348
+ func isFunctionCompositionArgs(args []*shimast.Node) bool {
349
+ if len(args) <= 1 {
350
+ return false
351
+ }
352
+ fnCount := 0
353
+ for _, a := range args {
354
+ if isFunctionLikeArg(a) {
355
+ fnCount++
356
+ if fnCount > 1 {
357
+ return true
358
+ }
359
+ } else if callArgsContainFunctionLike(a) {
360
+ return true
361
+ }
362
+ }
363
+ return false
364
+ }
365
+
366
+ // argListForcesFunctionBreak reports whether a call's argument list must
367
+ // explode under Prettier's function-composition rule (see
368
+ // isFunctionCompositionArgs), which fires even when the call would fit on one
369
+ // line. A DECORATOR is exempt entirely: Prettier guards the rule with
370
+ // `path.parent.type !== "Decorator"` (printCallArguments in call-arguments.js),
371
+ // so `@ManyToOne(() => User, (u) => u.addresses)` stays flat when it fits and
372
+ // breaks only on width. Shared by printArgList (which sets ForceBreak) and the
373
+ // print-width rule (whose fit fast-path must not skip such a call when it is
374
+ // written flat in source).
375
+ func argListForcesFunctionBreak(args []*shimast.Node, decoratorCall bool) bool {
376
+ if decoratorCall {
377
+ return false
378
+ }
379
+ return isFunctionCompositionArgs(args)
380
+ }
381
+
382
+ // callForcesFunctionBreak reports whether a node is a CallExpression that the
383
+ // multiple-callback rule forces to explode. The print-width rule consults it
384
+ // so its flat-fit fast path does not leave such a call inline when the source
385
+ // wrote it on one line.
386
+ func callForcesFunctionBreak(node *shimast.Node) bool {
387
+ if node == nil || node.Kind != shimast.KindCallExpression {
388
+ return false
389
+ }
390
+ call := node.AsCallExpression()
391
+ if call == nil || call.Arguments == nil {
392
+ return false
393
+ }
394
+ decoratorCall := node.Parent != nil && node.Parent.Kind == shimast.KindDecorator
395
+ return argListForcesFunctionBreak(call.Arguments.Nodes, decoratorCall)
396
+ }
397
+
398
+ // anyLeadingArgIsFunctionLike reports whether any argument before the last
399
+ // is a function or arrow expression. Prettier declines last-argument
400
+ // hugging when a non-last argument is itself a function/arrow, regardless
401
+ // of its body, so `useMemo(() => compute(), [deps])` explodes rather than
402
+ // hugging the trailing array. This complements anyLeadingItemBreaks, which
403
+ // only catches leads with hard line breaks (a block body) and so misses an
404
+ // expression-bodied arrow.
405
+ func anyLeadingArgIsFunctionLike(args []*shimast.Node) bool {
406
+ for i := 0; i+1 < len(args); i++ {
407
+ if args[i] == nil {
408
+ continue
409
+ }
410
+ switch args[i].Kind {
411
+ case shimast.KindArrowFunction, shimast.KindFunctionExpression:
412
+ return true
413
+ }
414
+ }
415
+ return false
416
+ }
417
+
418
+ // anyLeadingItemBreaks reports whether any item before the last carries a
419
+ // hard line break — it cannot render flat. Such an item (a block-bodied
420
+ // callback) forces the call multi-line on its own, so last-argument
421
+ // hugging must decline and let the whole list explode, matching Prettier's
422
+ // willBreak gate on the non-last arguments.
423
+ func anyLeadingItemBreaks(items []Doc) bool {
424
+ for i := 0; i+1 < len(items); i++ {
425
+ if _, ok := flatten(items[i]); !ok {
426
+ return true
427
+ }
428
+ }
429
+ return false
430
+ }
431
+
148
432
  // shouldHugLastArgument reports whether the final entry of `args` is a
149
433
  // shape Prettier keeps hugging the closing paren: an object or array
150
434
  // literal, a function expression, or an arrow function whose body is a
@@ -164,9 +448,28 @@ func shouldHugLastArgument(args []*shimast.Node) bool {
164
448
  return false
165
449
  }
166
450
  last := args[len(args)-1]
167
- if last == nil {
451
+ if last == nil || !lastArgHuggableShape(last) {
168
452
  return false
169
453
  }
454
+ // Prettier's shouldGroupLastArg declines only when the penultimate argument
455
+ // is the SAME node kind as the last: two objects or two arrays compete for
456
+ // the break, so `new Sash(x, { … }, { … })` and `bar({ … }, { … })` explode.
457
+ // A different-kind penultimate (an arrow before an object, `@OneToMany(() =>
458
+ // P, (p) => p.c, { … })`, or a plain `foo(a, b, { … })`) still hugs.
459
+ if len(args) >= 2 {
460
+ if pen := args[len(args)-2]; pen != nil && pen.Kind == last.Kind {
461
+ return false
462
+ }
463
+ }
464
+ return true
465
+ }
466
+
467
+ // lastArgHuggableShape reports whether `node` is the shape Prettier keeps
468
+ // hugging the closing paren: an object/array literal, a function expression,
469
+ // or an arrow whose body is a block, object, or array. An expression-bodied
470
+ // arrow (`(x) => x.id`) is excluded: it has no internal break point, so the
471
+ // flat hugging Concat would pin an overflowing call to one line.
472
+ func lastArgHuggableShape(last *shimast.Node) bool {
170
473
  switch last.Kind {
171
474
  case shimast.KindFunctionExpression,
172
475
  shimast.KindObjectLiteralExpression,
@@ -186,15 +489,191 @@ func shouldHugLastArgument(args []*shimast.Node) bool {
186
489
  }
187
490
  }
188
491
  switch body.Kind {
189
- case shimast.KindBlock,
190
- shimast.KindObjectLiteralExpression,
492
+ case shimast.KindBlock:
493
+ // A block-bodied arrow hugs regardless of any return-type annotation.
494
+ return true
495
+ case shimast.KindObjectLiteralExpression,
191
496
  shimast.KindArrayLiteralExpression:
497
+ // An expression-bodied arrow with an explicit return type is not hugged:
498
+ // Prettier's couldGroupArg excludes it ("avoid breaking inside composite
499
+ // return types"), so `map((r): T => ({ … }))` explodes the whole list
500
+ // while `map((r) => ({ … }))` hugs.
501
+ return arrow.Type == nil
502
+ }
503
+ }
504
+ return false
505
+ }
506
+
507
+ // shouldHugFirstArgument reports whether a call's argument list takes
508
+ // Prettier's "first-argument hugging" shape: exactly two arguments where
509
+ // the first is a block-bodied callback and the second is a short, simple
510
+ // value. `foo(() => { … }, target)` keeps the callback attached to the
511
+ // open paren and flows `, target)` after its closing brace, instead of
512
+ // exploding both arguments onto their own lines.
513
+ //
514
+ // Mirrors Prettier's shouldGroupFirstArg: it declines when the second
515
+ // argument is itself a function, arrow, conditional, or any expandable
516
+ // shape, so `both(() => {}, () => {})` falls through to the exploded
517
+ // list rather than hugging.
518
+ func shouldHugFirstArgument(ctx *PrintContext, args []*shimast.Node) bool {
519
+ if len(args) != 2 {
520
+ return false
521
+ }
522
+ first, second := args[0], args[1]
523
+ if first == nil || second == nil {
524
+ return false
525
+ }
526
+ return isFirstArgHuggableCallback(first) && isSimpleTrailingArg(ctx, second)
527
+ }
528
+
529
+ // isFirstArgHuggableCallback reports whether `node` is the callback shape
530
+ // Prettier hugs in the first-argument position: a function expression or
531
+ // an arrow with a block body. An expression-bodied arrow is excluded
532
+ // because it carries no internal break point.
533
+ func isFirstArgHuggableCallback(node *shimast.Node) bool {
534
+ switch node.Kind {
535
+ case shimast.KindFunctionExpression:
536
+ return true
537
+ case shimast.KindArrowFunction:
538
+ arrow := node.AsArrowFunction()
539
+ return arrow != nil && arrow.Body != nil && arrow.Body.Kind == shimast.KindBlock
540
+ }
541
+ return false
542
+ }
543
+
544
+ // isSimpleTrailingArg reports whether `node` is a value that may trail a
545
+ // hugged first-argument callback. Prettier hugs the leading callback when
546
+ // the trailing argument is an identifier, member access, literal, `this`,
547
+ // or an array literal — most notably the `useEffect(() => { … }, [deps])`
548
+ // idiom. It also accepts an empty object literal, a short call/new, and a
549
+ // short arithmetic/logical expression (`setTimeout(fn, 1000 - x)`). A function,
550
+ // arrow, conditional, or a non-empty object literal is excluded so
551
+ // first-argument hugging declines and the whole list explodes.
552
+ //
553
+ // A call expression is deliberately NOT included: the conditional-group
554
+ // fit check only measures an option's first line, so a hugged-first option
555
+ // whose trailing call overflows the closing line (`}, deeplyNested(…))`)
556
+ // would still be selected, where Prettier explodes. The array case shares
557
+ // that limitation in principle, but dependency arrays are short in
558
+ // practice, the same way the identifier/member cases already are.
559
+ func isSimpleTrailingArg(ctx *PrintContext, node *shimast.Node) bool {
560
+ switch node.Kind {
561
+ case shimast.KindIdentifier,
562
+ shimast.KindStringLiteral,
563
+ shimast.KindNumericLiteral,
564
+ shimast.KindBigIntLiteral,
565
+ shimast.KindTrueKeyword,
566
+ shimast.KindFalseKeyword,
567
+ shimast.KindNullKeyword,
568
+ shimast.KindThisKeyword,
569
+ shimast.KindPropertyAccessExpression,
570
+ shimast.KindElementAccessExpression,
571
+ shimast.KindArrayLiteralExpression:
572
+ return true
573
+ case shimast.KindAsExpression:
574
+ // `[] as string[]`: the `reduce(fn, [] as T[])` idiom. Hug when the cast
575
+ // wraps a value that is itself a simple trailing arg AND the target type is
576
+ // simple. A type carrying an object type literal (`[] as Array<{ … }>`) is
577
+ // not simple, Prettier expands and explodes the list there, so exclude any
578
+ // type whose source contains a `{` (a type literal).
579
+ if as := node.AsAsExpression(); as != nil && as.Expression != nil {
580
+ return isSimpleTrailingArg(ctx, as.Expression) && !typeHasObjectLiteral(ctx.Source, as.Type)
581
+ }
582
+ case shimast.KindCallExpression, shimast.KindNewExpression:
583
+ // `reduce(fn, Object.create(null))` / `reduce(fn, new Map<…>())`: Prettier
584
+ // hugs the first arg when the trailing call/new has at most one value
585
+ // argument, regardless of its length, type arguments and long names do not
586
+ // matter and the close line is allowed to overflow. A call with two or more
587
+ // arguments explodes the whole list instead. Mirrors Prettier's
588
+ // isHopefullyShortCallArgument for call-like nodes.
589
+ return callValueArgCount(node) <= 1
590
+ case shimast.KindObjectLiteralExpression:
591
+ // `reduce(fn, {})`: an EMPTY object literal hugs (Prettier's couldGroupArg
592
+ // excludes a property-less object); an object with properties expands and
593
+ // explodes the list instead (`f(fn, { a: 1 })`).
594
+ if obj := node.AsObjectLiteralExpression(); obj != nil {
595
+ return obj.Properties == nil || len(obj.Properties.Nodes) == 0
596
+ }
597
+ case shimast.KindBinaryExpression:
598
+ // `setTimeout(fn, 1000 - ellapsed)`: a SINGLE arithmetic/logical operation
599
+ // whose two operands are simple leaves rides the close line and hugs. A
600
+ // chained binary (`60 * 60 * 1000`, an operand itself a binary) is not
601
+ // simple and explodes the list, matching Prettier's isHopefullyShortCallArgument
602
+ // (which only treats a binaryish short when both sides are themselves short).
603
+ if bin := node.AsBinaryExpression(); bin != nil {
604
+ return isSimpleBinaryOperand(bin.Left) && isSimpleBinaryOperand(bin.Right)
605
+ }
606
+ case shimast.KindPrefixUnaryExpression:
607
+ // `reduce(fn, -1)`: a unary applied to a simple leaf rides the close line.
608
+ if u := node.AsPrefixUnaryExpression(); u != nil {
609
+ return isSimpleBinaryOperand(u.Operand)
610
+ }
611
+ }
612
+ return false
613
+ }
614
+
615
+ // isSimpleBinaryOperand reports whether a node is a short, non-recursive
616
+ // operand, an identifier, a literal, `this`, or a member access. A binary,
617
+ // call, conditional, or other compound expression is not, so a binary built
618
+ // from such operands is treated as too complex to ride a hugged close line.
619
+ func isSimpleBinaryOperand(node *shimast.Node) bool {
620
+ if node == nil {
621
+ return false
622
+ }
623
+ switch node.Kind {
624
+ case shimast.KindIdentifier,
625
+ shimast.KindNumericLiteral,
626
+ shimast.KindStringLiteral,
627
+ shimast.KindBigIntLiteral,
628
+ shimast.KindTrueKeyword,
629
+ shimast.KindFalseKeyword,
630
+ shimast.KindNullKeyword,
631
+ shimast.KindThisKeyword,
632
+ shimast.KindPropertyAccessExpression,
633
+ shimast.KindElementAccessExpression:
634
+ return true
635
+ }
636
+ return false
637
+ }
638
+
639
+ // typeHasObjectLiteral reports whether a type node's source contains a `{`,
640
+ // the mark of an object type literal (`{ a: 1 }`, `Array<{ … }>`). Prettier's
641
+ // isSimpleType rejects such a type; a flat type (`string[]`, `Foo<Bar>`,
642
+ // `number[][]`) has none and stays simple.
643
+ func typeHasObjectLiteral(src string, typeNode *shimast.Node) bool {
644
+ if typeNode == nil {
645
+ return false
646
+ }
647
+ start := shimscanner.SkipTrivia(src, typeNode.Pos())
648
+ end := typeNode.End()
649
+ if start < 0 || end < start || end > len(src) {
650
+ return true // unmeasurable: be conservative and decline the hug
651
+ }
652
+ for i := start; i < end; i++ {
653
+ if src[i] == '{' {
192
654
  return true
193
655
  }
194
656
  }
195
657
  return false
196
658
  }
197
659
 
660
+ // callValueArgCount returns the number of value arguments on a call or new
661
+ // expression (type arguments are not counted). A new expression with no
662
+ // argument list (`new Foo`) counts as zero.
663
+ func callValueArgCount(node *shimast.Node) int {
664
+ switch node.Kind {
665
+ case shimast.KindCallExpression:
666
+ if c := node.AsCallExpression(); c != nil && c.Arguments != nil {
667
+ return len(c.Arguments.Nodes)
668
+ }
669
+ case shimast.KindNewExpression:
670
+ if n := node.AsNewExpression(); n != nil && n.Arguments != nil {
671
+ return len(n.Arguments.Nodes)
672
+ }
673
+ }
674
+ return 0
675
+ }
676
+
198
677
  // forceBreakFirstGroup returns `doc` with the first Group found in a
199
678
  // left-to-right walk of its subtree forced broken, and reports whether
200
679
  // one was found. printListHuggingLast uses it to commit a hugged
@@ -225,72 +704,34 @@ func forceBreakFirstGroup(doc Doc) (Doc, bool) {
225
704
 
226
705
  // Type-argument byte-range helpers. The shim's NodeList.End() points
227
706
  // past the last argument; the surrounding `<` and `>` are not part of
228
- // the list's range, so we have to scan around it.
707
+ // the list's range, so we have to scan around it. Call and new
708
+ // expressions share these — only the field holding the list differs.
229
709
 
230
- // callTypeArgsStart returns the byte offset of the `<` that opens the
231
- // type-argument list of a CallExpression. Returns -1 when absent.
232
- func callTypeArgsStart(ctx *PrintContext, call *shimast.CallExpression) int {
233
- if call.TypeArguments == nil || len(call.TypeArguments.Nodes) == 0 {
234
- return -1
235
- }
236
- first := call.TypeArguments.Nodes[0]
237
- if first == nil {
710
+ // typeArgsStart returns the byte offset of the `<` that opens a
711
+ // type-argument list. Returns -1 when absent.
712
+ func typeArgsStart(src string, list *shimast.NodeList) int {
713
+ if list == nil || len(list.Nodes) == 0 || list.Nodes[0] == nil {
238
714
  return -1
239
715
  }
240
716
  // `<` is the byte immediately before the first type argument
241
717
  // (modulo whitespace).
242
- pos := first.Pos()
243
- for i := pos - 1; i >= 0; i-- {
244
- if ctx.Source[i] == '<' {
245
- return i
246
- }
247
- }
248
- return -1
249
- }
250
-
251
- // callTypeArgsEnd returns the byte offset one past the closing `>` of a
252
- // CallExpression's type-argument list. Returns -1 when absent.
253
- func callTypeArgsEnd(ctx *PrintContext, call *shimast.CallExpression) int {
254
- if call.TypeArguments == nil {
255
- return -1
256
- }
257
- end := call.TypeArguments.End()
258
- for i := end; i < len(ctx.Source); i++ {
259
- if ctx.Source[i] == '>' {
260
- return i + 1
261
- }
262
- }
263
- return end
264
- }
265
-
266
- // newTypeArgsStart returns the byte offset of the `<` that opens the
267
- // type-argument list of a NewExpression. Returns -1 when absent.
268
- func newTypeArgsStart(ctx *PrintContext, ne *shimast.NewExpression) int {
269
- if ne.TypeArguments == nil || len(ne.TypeArguments.Nodes) == 0 {
270
- return -1
271
- }
272
- first := ne.TypeArguments.Nodes[0]
273
- if first == nil {
274
- return -1
275
- }
276
- pos := first.Pos()
277
- for i := pos - 1; i >= 0; i-- {
278
- if ctx.Source[i] == '<' {
718
+ for i := list.Nodes[0].Pos() - 1; i >= 0; i-- {
719
+ if src[i] == '<' {
279
720
  return i
280
721
  }
281
722
  }
282
723
  return -1
283
724
  }
284
725
 
285
- // newTypeArgsEnd returns the byte offset one past the closing `>` of a
286
- // NewExpression's type-argument list. Returns -1 when absent.
287
- func newTypeArgsEnd(ctx *PrintContext, ne *shimast.NewExpression) int {
288
- if ne.TypeArguments == nil {
726
+ // typeArgsEnd returns the byte offset one past the closing `>` of a
727
+ // type-argument list. Returns -1 when the list is absent.
728
+ func typeArgsEnd(src string, list *shimast.NodeList) int {
729
+ if list == nil {
289
730
  return -1
290
731
  }
291
- end := ne.TypeArguments.End()
292
- for i := end; i < len(ctx.Source); i++ {
293
- if ctx.Source[i] == '>' {
732
+ end := list.End()
733
+ for i := end; i < len(src); i++ {
734
+ if src[i] == '>' {
294
735
  return i + 1
295
736
  }
296
737
  }