@ttsc/lint 0.14.0-dev.20260529.2 → 0.14.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 (380) hide show
  1. package/README.md +23 -25
  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 +167 -5
  68. package/linthost/print_nodes_call.go +786 -77
  69. package/linthost/print_nodes_function.go +43 -0
  70. package/linthost/print_nodes_imports.go +64 -14
  71. package/linthost/print_nodes_list.go +214 -8
  72. package/linthost/print_nodes_object.go +14 -6
  73. package/linthost/print_nodes_ternary.go +147 -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 +213 -0
  83. package/linthost/rules_format_bracket_spacing.go +117 -0
  84. package/linthost/rules_format_clause_join.go +179 -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 +180 -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 +517 -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
@@ -46,12 +46,34 @@ func printCallExpression(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
46
46
  }
47
47
  if call.TypeArguments != nil {
48
48
  // Verbatim range covering `<A, B>` punctuation and members.
49
- parts = append(parts, verbatimRange(ctx.Source, callTypeArgsStart(ctx, call), callTypeArgsEnd(ctx, call)))
49
+ parts = append(parts, verbatimRange(ctx.Source, typeArgsStart(ctx.Source, call.TypeArguments), typeArgsEnd(ctx.Source, call.TypeArguments)))
50
50
  }
51
51
  if hasNilEntry(call.Arguments) {
52
52
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
53
53
  }
54
- argDoc, argCovered := printArgList(ctx, call.Arguments)
54
+ // A comment in an argument gap (`foo(a, /* c */ b)`) would be dropped by the
55
+ // freshly minted `, ` separators in printArgList. The top-level print-width
56
+ // scan only masks a *direct* child's comments, so a comment inside a NESTED
57
+ // call's argument list slips through and is lost on reflow. Self-guard the
58
+ // way the object/array printers do: bail to verbatim and report UNCOVERED so
59
+ // an enclosing reflow abstains too. Uncovered must be hard `false`, not
60
+ // `!nodeSpansMultipleLines`: a single-line comment-bearing call would
61
+ // otherwise report covered and let the outer reflow break around it, moving
62
+ // the verbatim node off its line (the assertFormatUnchanged contract).
63
+ if listHasInterItemComments(ctx, node) {
64
+ return verbatim(ctx, node), false
65
+ }
66
+ // Prettier never appends a trailing comma inside a dynamic
67
+ // `import(...)`, so the printer's reflow must agree with
68
+ // format/trailing-comma's same exception (see isDynamicImportCall).
69
+ addComma := ctx.allowsCallArgumentTrailingComma() && !isDynamicImportCall(call)
70
+ // A decorator's call hugs its last argument even past leading callbacks
71
+ // (`@OneToMany(() => P, (p) => p.c, { … })`); a plain call explodes there.
72
+ decoratorCall := node.Parent != nil && node.Parent.Kind == shimast.KindDecorator
73
+ // A test-framework call (`test("desc", () => { … })`) hugs its callback even
74
+ // when the description string pushes the opening line past printWidth, so the
75
+ // exploded fallback is dropped for it. See isTestCall.
76
+ argDoc, argCovered := printArgList(ctx, call.Arguments, addComma, decoratorCall, isTestCall(node))
55
77
  parts = append(parts, argDoc)
56
78
  return Concat(parts...), covered && argCovered
57
79
  }
@@ -77,13 +99,23 @@ func printNewExpression(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
77
99
  covered = covered && !nodeSpansMultipleLines(ctx, ne.Expression)
78
100
  }
79
101
  if ne.TypeArguments != nil {
80
- parts = append(parts, verbatimRange(ctx.Source, newTypeArgsStart(ctx, ne), newTypeArgsEnd(ctx, ne)))
102
+ parts = append(parts, verbatimRange(ctx.Source, typeArgsStart(ctx.Source, ne.TypeArguments), typeArgsEnd(ctx.Source, ne.TypeArguments)))
103
+ }
104
+ // A comment in the `new`->constructee gap (`new /* c */ Foo`) or an argument
105
+ // gap would be dropped by the minted `Text("new ")` / fresh separators — those
106
+ // gaps are not AST children, so a nested new-expression's comment slips past
107
+ // the top-level scan. Guard unconditionally (the no-args `new Foo` path mints
108
+ // `new ` too), mirroring printCallExpression's unconditional guard. Report
109
+ // UNCOVERED (hard `false`, not `!nodeSpansMultipleLines`) so an enclosing
110
+ // reflow abstains rather than breaking around this single-line verbatim node.
111
+ if listHasInterItemComments(ctx, node) {
112
+ return verbatim(ctx, node), false
81
113
  }
82
114
  if ne.Arguments != nil {
83
115
  if hasNilEntry(ne.Arguments) {
84
116
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
85
117
  }
86
- argDoc, argCovered := printArgList(ctx, ne.Arguments)
118
+ argDoc, argCovered := printArgList(ctx, ne.Arguments, ctx.allowsCallArgumentTrailingComma(), false, false)
87
119
  parts = append(parts, argDoc)
88
120
  covered = covered && argCovered
89
121
  }
@@ -116,7 +148,14 @@ func hasNilEntry(list *shimast.NodeList) bool {
116
148
  // layout, so the parens stay attached and the preceding arguments are
117
149
  // not exploded onto their own lines. This is the Prettier behavior for
118
150
  // `foo(x, () => { … })`.
119
- func printArgList(ctx *PrintContext, list *shimast.NodeList) (Doc, bool) {
151
+ // `addComma` is supplied by the caller (which honors `format.trailingComma`
152
+ // and the dynamic-import exception) rather than read here, so call and new
153
+ // expressions can diverge on the comma without printArgList knowing the
154
+ // callee. Call / new argument lists accepted trailing commas only in
155
+ // ES2017+, so Prettier's "es5" and "none" modes pass false; otherwise the
156
+ // printer would oscillate against format/trailing-comma on every cascade
157
+ // pass (rxjs hit this on ajax.ts and several operators / testing helpers).
158
+ func printArgList(ctx *PrintContext, list *shimast.NodeList, addComma bool, decoratorCall bool, forceHugLast bool) (Doc, bool) {
120
159
  if list == nil {
121
160
  return Text("()"), true
122
161
  }
@@ -127,24 +166,340 @@ func printArgList(ctx *PrintContext, list *shimast.NodeList) (Doc, bool) {
127
166
  covered = covered && childCovered
128
167
  items = append(items, doc)
129
168
  }
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).
169
+ // Last-argument hugging wins when both predicates match; first-arg
170
+ // hugging only applies to the two-argument `callback, simpleArg` shape.
171
+ // Decline last-arg hugging when a leading argument forces the call
172
+ // multi-line on its own, so Prettier explodes the whole call rather than
173
+ // hugging the last argument. Two leading shapes trigger that: a function
174
+ // or arrow expression (`useMemo(() => x, [deps])`, even with an inline
175
+ // expression body Prettier never hugs after it), and any argument whose
176
+ // doc carries hard breaks (a block-bodied callback). A leading object or
177
+ // array that merely fits flat does NOT decline — Prettier still hugs the
178
+ // last argument there (`doConfigure({ … }, () => { … })`).
179
+ // The useMemo/useEffect deps shape (`f(() => x, [deps])`) explodes its 2-arg
180
+ // arrow+array one-per-line even under a decorator: Prettier's shouldExpandLastArg
181
+ // declines `args.length === 2 && penultimate is ArrowFunction && last is Array`
182
+ // unconditionally (it is NOT decorator-gated). The genuine decorator hug
183
+ // (`@OneToMany(() => P, (p) => p.c, { … })`) is 3-arg, so isUseMemoArrowArrayShape
184
+ // already returns false for it; a 2-or-more-callback composition is handled by
185
+ // argListForcesFunctionBreak, and a block-bodied leading callback by
186
+ // anyLeadingItemBreaks.
187
+ hugLast := shouldHugLastArgument(list.Nodes) &&
188
+ !anyLeadingItemBreaks(items) &&
189
+ !isUseMemoArrowArrayShape(list.Nodes)
190
+ // Two or more function/arrow arguments force the list to explode, one per
191
+ // line, even when it would fit flat: Prettier always breaks a call carrying
192
+ // multiple callbacks (`promise.then(() => a, () => b)`), the
193
+ // function-composition rule. The sole exception is a decorator hugging a
194
+ // huggable trailing argument (`@OneToMany(() => P, (p) => p.c, { … })`),
195
+ // where the leading arrows stay inline and only the object breaks — so the
196
+ // gate defers to hugLast off a decorator. See argListForcesFunctionBreak.
197
+ forceFnBreak := argListForcesFunctionBreak(list.Nodes, decoratorCall)
198
+ if forceFnBreak {
199
+ hugLast = false
200
+ }
201
+ // A test-framework call always hugs its trailing callback (the description
202
+ // string may overflow the open line); force the hug and drop the exploded
203
+ // fallback via HugLastForce.
204
+ if forceHugLast {
205
+ hugLast = true
206
+ forceFnBreak = false
207
+ }
208
+ hugFirst := !hugLast && !forceFnBreak && shouldHugFirstArgument(ctx, list.Nodes)
137
209
  shape := listShape{
138
- OpenTok: "(",
139
- CloseTok: ")",
140
- Items: items,
141
- Space: false,
142
- AddComma: ctx.allowsCallArgumentTrailingComma(),
143
- HugLast: shouldHugLastArgument(list.Nodes),
210
+ OpenTok: "(",
211
+ CloseTok: ")",
212
+ Items: items,
213
+ Space: false,
214
+ AddComma: addComma,
215
+ HugLast: hugLast,
216
+ HugLastForce: forceHugLast,
217
+ HugFirst: hugFirst,
218
+ // A non-empty array second argument reaches HugFirst only via the
219
+ // React-hook deps branch of shouldHugFirstArgument; let that deps array
220
+ // break one-per-line instead of being pinned flat on the close line.
221
+ HugFirstTrailingBreaks: hugFirst && len(list.Nodes) == 2 &&
222
+ list.Nodes[1] != nil &&
223
+ list.Nodes[1].Kind == shimast.KindArrayLiteralExpression,
224
+ HugFirstForce: hugFirst && isReactHookDepsCall(list.Nodes),
225
+ ForceBreak: forceFnBreak,
226
+ BlankBefore: blankBeforeItems(ctx.Source, list.Nodes),
144
227
  }
145
228
  return printList(ctx, shape), covered
146
229
  }
147
230
 
231
+ // testCalleePatterns is Prettier's exact testCallCalleePatterns set (the dotted
232
+ // callee chains its isTestCallCallee matches). Matching the precise set, rather
233
+ // than "any base with a {only,skip,…} tail", avoids both over-matching
234
+ // (`test.each`, `it.todo` are NOT test calls) and under-matching (`test.fixme`,
235
+ // `test.describe.only` ARE).
236
+ var testCalleePatterns = map[string]bool{
237
+ "it": true, "it.only": true, "it.skip": true,
238
+ "describe": true, "describe.only": true, "describe.skip": true,
239
+ "test": true, "test.only": true, "test.skip": true,
240
+ "test.fixme": true, "test.step": true,
241
+ "test.describe": true, "test.describe.only": true,
242
+ "test.describe.skip": true, "test.describe.fixme": true,
243
+ "test.describe.parallel": true, "test.describe.parallel.only": true,
244
+ "test.describe.serial": true, "test.describe.serial.only": true,
245
+ "skip": true, "xit": true, "xdescribe": true, "xtest": true,
246
+ "fit": true, "fdescribe": true, "ftest": true,
247
+ }
248
+
249
+ // calleeChain builds the dotted member-access chain of a callee rooted at a
250
+ // plain identifier (`test.describe.only` becomes "test.describe.only"), or ""
251
+ // when the callee is not a pure identifier / property-access chain (a computed
252
+ // access, a call, `this`, etc.).
253
+ func calleeChain(node *shimast.Node) string {
254
+ switch node.Kind {
255
+ case shimast.KindIdentifier:
256
+ return identifierText(node)
257
+ case shimast.KindPropertyAccessExpression:
258
+ pa := node.AsPropertyAccessExpression()
259
+ if pa == nil || pa.Name() == nil || pa.Name().Kind != shimast.KindIdentifier {
260
+ return ""
261
+ }
262
+ base := calleeChain(pa.Expression)
263
+ if base == "" {
264
+ return ""
265
+ }
266
+ return base + "." + identifierText(pa.Name())
267
+ }
268
+ return ""
269
+ }
270
+
271
+ // testCalleeName reports whether `callee` is one of Prettier's recognized
272
+ // test-framework callees (see testCalleePatterns).
273
+ func testCalleeName(callee *shimast.Node) bool {
274
+ return callee != nil && testCalleePatterns[calleeChain(callee)]
275
+ }
276
+
277
+ // isTestCall reports whether `node` is a test-framework call Prettier prints
278
+ // with its callback hugged regardless of width: `it(...)` / `test(...)` /
279
+ // `describe(...)` (and focus/skip variants) called with a string or template
280
+ // description and a trailing function/arrow. Prettier never explodes such a
281
+ // call's arguments, so the printer keeps the callback on the open-paren line
282
+ // even when the description string overflows printWidth (the callback's
283
+ // parameter count does not matter, verified against Prettier). Scoped to the
284
+ // two-argument form (the overwhelming shape); a three-argument timeout variant
285
+ // falls through to ordinary hugging.
286
+ func isTestCall(node *shimast.Node) bool {
287
+ if node == nil || node.Kind != shimast.KindCallExpression {
288
+ return false
289
+ }
290
+ call := node.AsCallExpression()
291
+ if call == nil || call.QuestionDotToken != nil || call.Arguments == nil {
292
+ return false
293
+ }
294
+ args := call.Arguments.Nodes
295
+ // Prettier accepts a two- or three-argument test call (the third is a numeric
296
+ // timeout): `it("name", () => { … })` and `it("name", () => { … }, 2500)`.
297
+ if len(args) != 2 && len(args) != 3 {
298
+ return false
299
+ }
300
+ if args[0] == nil || args[1] == nil {
301
+ return false
302
+ }
303
+ switch args[0].Kind {
304
+ case shimast.KindStringLiteral,
305
+ shimast.KindNoSubstitutionTemplateLiteral,
306
+ shimast.KindTemplateExpression:
307
+ default:
308
+ return false
309
+ }
310
+ if !testCalleeName(call.Expression) {
311
+ return false
312
+ }
313
+ if len(args) == 3 {
314
+ // The timeout argument must be numeric, and the callback a block-bodied
315
+ // function/arrow taking at most one parameter (Prettier's
316
+ // isFunctionOrArrowExpressionWithBody + parameter-count gate).
317
+ if args[2] == nil || args[2].Kind != shimast.KindNumericLiteral {
318
+ return false
319
+ }
320
+ return isBlockBodiedCallback(args[1]) && len(args[1].Parameters()) <= 1
321
+ }
322
+ return isFunctionLikeArg(args[1])
323
+ }
324
+
325
+ // isBlockBodiedCallback reports whether a node is a function expression or an
326
+ // arrow with a block body (Prettier's isFunctionOrArrowExpressionWithBody).
327
+ func isBlockBodiedCallback(node *shimast.Node) bool {
328
+ if node == nil {
329
+ return false
330
+ }
331
+ switch node.Kind {
332
+ case shimast.KindFunctionExpression:
333
+ return true
334
+ case shimast.KindArrowFunction:
335
+ a := node.AsArrowFunction()
336
+ return a != nil && a.Body != nil && a.Body.Kind == shimast.KindBlock
337
+ }
338
+ return false
339
+ }
340
+
341
+ // isFunctionLikeArg reports whether an argument is a function or arrow
342
+ // expression, the two shapes Prettier counts as "function" arguments.
343
+ func isFunctionLikeArg(node *shimast.Node) bool {
344
+ if node == nil {
345
+ return false
346
+ }
347
+ switch node.Kind {
348
+ case shimast.KindArrowFunction, shimast.KindFunctionExpression:
349
+ return true
350
+ }
351
+ return false
352
+ }
353
+
354
+ // callArgsContainFunctionLike reports whether a CALL expression's own argument
355
+ // list carries a function/arrow argument. Prettier's function-composition test
356
+ // treats `foo.map((x) => x)` as a composed call, so a call carrying it
357
+ // alongside any second argument explodes. Only a call counts: Prettier's
358
+ // isCallExpression matches CallExpression / OptionalCallExpression but NOT a
359
+ // NewExpression, so `foo(new Bar(() => x), other)` is left inline.
360
+ func callArgsContainFunctionLike(node *shimast.Node) bool {
361
+ if node == nil || node.Kind != shimast.KindCallExpression {
362
+ return false
363
+ }
364
+ c := node.AsCallExpression()
365
+ if c == nil || c.Arguments == nil {
366
+ return false
367
+ }
368
+ for _, a := range c.Arguments.Nodes {
369
+ if isFunctionLikeArg(a) {
370
+ return true
371
+ }
372
+ }
373
+ return false
374
+ }
375
+
376
+ // isFunctionCompositionArgs mirrors Prettier's predicate of the same name: a
377
+ // call with two or more arguments is "function composition" when either two of
378
+ // them are themselves functions/arrows, or one is a call/new whose own
379
+ // arguments include a function/arrow (`stream.pipe(map((x) => x), other)`).
380
+ // Prettier breaks such a list onto one-argument-per-line unconditionally.
381
+ func isFunctionCompositionArgs(args []*shimast.Node) bool {
382
+ if len(args) <= 1 {
383
+ return false
384
+ }
385
+ fnCount := 0
386
+ for _, a := range args {
387
+ if isFunctionLikeArg(a) {
388
+ fnCount++
389
+ if fnCount > 1 {
390
+ return true
391
+ }
392
+ } else if callArgsContainFunctionLike(a) {
393
+ return true
394
+ }
395
+ }
396
+ return false
397
+ }
398
+
399
+ // argListForcesFunctionBreak reports whether a call's argument list must
400
+ // explode under Prettier's function-composition rule (see
401
+ // isFunctionCompositionArgs), which fires even when the call would fit on one
402
+ // line. A DECORATOR is exempt entirely: Prettier guards the rule with
403
+ // `path.parent.type !== "Decorator"` (printCallArguments in call-arguments.js),
404
+ // so `@ManyToOne(() => User, (u) => u.addresses)` stays flat when it fits and
405
+ // breaks only on width. Shared by printArgList (which sets ForceBreak) and the
406
+ // print-width rule (whose fit fast-path must not skip such a call when it is
407
+ // written flat in source).
408
+ func argListForcesFunctionBreak(args []*shimast.Node, decoratorCall bool) bool {
409
+ if decoratorCall {
410
+ return false
411
+ }
412
+ return isFunctionCompositionArgs(args)
413
+ }
414
+
415
+ // callForcesFunctionBreak reports whether a node is a call OR new expression
416
+ // that the multiple-callback rule forces to explode. Prettier's printCallArguments
417
+ // is shared by NewExpression (its function-composition break is gated only by
418
+ // `path.parent.type !== "Decorator"`, not by call-vs-new), so `new Foo(() => a,
419
+ // () => b)` explodes the same as a call. The print-width rule consults this so
420
+ // its flat-fit fast path does not leave such a call/new inline when the source
421
+ // wrote it on one line.
422
+ func callForcesFunctionBreak(node *shimast.Node) bool {
423
+ if node == nil {
424
+ return false
425
+ }
426
+ var args *shimast.NodeList
427
+ switch node.Kind {
428
+ case shimast.KindCallExpression:
429
+ if c := node.AsCallExpression(); c != nil {
430
+ args = c.Arguments
431
+ }
432
+ case shimast.KindNewExpression:
433
+ if n := node.AsNewExpression(); n != nil {
434
+ args = n.Arguments
435
+ }
436
+ default:
437
+ return false
438
+ }
439
+ if args == nil {
440
+ return false
441
+ }
442
+ // A new expression is never a decorator's call.
443
+ decoratorCall := node.Kind == shimast.KindCallExpression &&
444
+ node.Parent != nil && node.Parent.Kind == shimast.KindDecorator
445
+ return argListForcesFunctionBreak(args.Nodes, decoratorCall)
446
+ }
447
+
448
+ // isUseMemoArrowArrayShape reports the ONE leading-argument shape Prettier
449
+ // declines last-argument hugging for: exactly two arguments where the first is
450
+ // an arrow function and the last is an array literal — the
451
+ // `useMemo(() => x, [deps])` / `useEffect(() => { … }, [deps])` shape.
452
+ // Prettier's shouldExpandLastArg (call-arguments.js:260-262) has NO general
453
+ // "a leading function declines hugging" rule; its only leading-argument clause
454
+ // is `args.length === 2 && penultimate is ArrowFunctionExpression && last is
455
+ // ArrayExpression`. A block-bodied leading callback is handled independently by
456
+ // anyLeadingItemBreaks (it `willBreak`), and two-or-more callbacks by
457
+ // argListForcesFunctionBreak, so a leading expression-bodied arrow before a
458
+ // non-array huggable last argument (`f((x) => g(x), { a: 1 })`) must still hug.
459
+ func isUseMemoArrowArrayShape(args []*shimast.Node) bool {
460
+ if len(args) != 2 || args[0] == nil || args[1] == nil {
461
+ return false
462
+ }
463
+ return args[0].Kind == shimast.KindArrowFunction &&
464
+ args[1].Kind == shimast.KindArrayLiteralExpression
465
+ }
466
+
467
+ // isReactHookDepsCall reports the React-hook deps shape Prettier hugs WITHOUT an
468
+ // exploded fallback: exactly two arguments, the first a ZERO-parameter
469
+ // block-bodied arrow, the second an array literal (`useEffect(() => { … },
470
+ // [deps])`). Prettier's isReactHookCallWithDepsArray / isValidHookCallbackAndDepsFormat
471
+ // keys on this and never explodes the args — it keeps the callback hugged and
472
+ // lets the open line overflow. A parameterized callback (`subscribe((e) => { … },
473
+ // [])`) is NOT this shape and keeps the fallback, so the zero-parameter +
474
+ // block-body gate is load-bearing (distinct from isUseMemoArrowArrayShape and
475
+ // from the HugFirstTrailingBreaks array check).
476
+ func isReactHookDepsCall(args []*shimast.Node) bool {
477
+ if len(args) != 2 || args[0] == nil || args[1] == nil {
478
+ return false
479
+ }
480
+ if args[1].Kind != shimast.KindArrayLiteralExpression {
481
+ return false
482
+ }
483
+ arrow := args[0].AsArrowFunction()
484
+ return arrow != nil && arrow.Body != nil &&
485
+ arrow.Body.Kind == shimast.KindBlock &&
486
+ len(args[0].Parameters()) == 0
487
+ }
488
+
489
+ // anyLeadingItemBreaks reports whether any item before the last carries a
490
+ // hard line break — it cannot render flat. Such an item (a block-bodied
491
+ // callback) forces the call multi-line on its own, so last-argument
492
+ // hugging must decline and let the whole list explode, matching Prettier's
493
+ // willBreak gate on the non-last arguments.
494
+ func anyLeadingItemBreaks(items []Doc) bool {
495
+ for i := 0; i+1 < len(items); i++ {
496
+ if _, ok := flatten(items[i]); !ok {
497
+ return true
498
+ }
499
+ }
500
+ return false
501
+ }
502
+
148
503
  // shouldHugLastArgument reports whether the final entry of `args` is a
149
504
  // shape Prettier keeps hugging the closing paren: an object or array
150
505
  // literal, a function expression, or an arrow function whose body is a
@@ -164,14 +519,55 @@ func shouldHugLastArgument(args []*shimast.Node) bool {
164
519
  return false
165
520
  }
166
521
  last := args[len(args)-1]
167
- if last == nil {
522
+ if last == nil || !lastArgHuggableShape(last) {
168
523
  return false
169
524
  }
525
+ // Prettier's shouldGroupLastArg declines only when the penultimate argument
526
+ // is the SAME node kind as the last: two objects or two arrays compete for
527
+ // the break, so `new Sash(x, { … }, { … })` and `bar({ … }, { … })` explode.
528
+ // A different-kind penultimate (an arrow before an object, `@OneToMany(() =>
529
+ // P, (p) => p.c, { … })`, or a plain `foo(a, b, { … })`) still hugs.
530
+ if len(args) >= 2 {
531
+ if pen := args[len(args)-2]; pen != nil && pen.Kind == last.Kind {
532
+ return false
533
+ }
534
+ // Prettier's shouldExpandLastArg also declines a CONCISELY-PRINTED numeric
535
+ // array as the last of two-plus arguments: it fills on its own line, so
536
+ // `drawPolygon(ctx, [1, 2, 3, …])` explodes rather than hugging.
537
+ if last.Kind == shimast.KindArrayLiteralExpression {
538
+ if arr := last.AsArrayLiteralExpression(); arr != nil && arr.Elements != nil &&
539
+ isConciselyPrintedArray(arr.Elements.Nodes) {
540
+ return false
541
+ }
542
+ }
543
+ }
544
+ return true
545
+ }
546
+
547
+ // lastArgHuggableShape reports whether `node` is the shape Prettier keeps
548
+ // hugging the closing paren: an object/array literal, a function expression,
549
+ // or an arrow whose body is a block, object, or array. An expression-bodied
550
+ // arrow (`(x) => x.id`) is excluded: it has no internal break point, so the
551
+ // flat hugging Concat would pin an overflowing call to one line.
552
+ func lastArgHuggableShape(last *shimast.Node) bool {
170
553
  switch last.Kind {
171
- case shimast.KindFunctionExpression,
172
- shimast.KindObjectLiteralExpression,
173
- shimast.KindArrayLiteralExpression:
554
+ case shimast.KindFunctionExpression:
174
555
  return true
556
+ case shimast.KindObjectLiteralExpression:
557
+ // Prettier's couldExpandArg requires a NON-EMPTY object (`properties.length
558
+ // > 0`); an empty `{}` is not expandable, so `foo(a, b, {})` explodes the
559
+ // list rather than hugging. (Inter-item comments route to verbatim, so the
560
+ // hasComment branch is moot here.)
561
+ if obj := last.AsObjectLiteralExpression(); obj != nil {
562
+ return obj.Properties != nil && len(obj.Properties.Nodes) > 0
563
+ }
564
+ return false
565
+ case shimast.KindArrayLiteralExpression:
566
+ // Likewise a non-empty array; an empty `[]` is not expandable.
567
+ if arr := last.AsArrayLiteralExpression(); arr != nil {
568
+ return arr.Elements != nil && len(arr.Elements.Nodes) > 0
569
+ }
570
+ return false
175
571
  case shimast.KindArrowFunction:
176
572
  arrow := last.AsArrowFunction()
177
573
  if arrow == nil || arrow.Body == nil {
@@ -186,15 +582,366 @@ func shouldHugLastArgument(args []*shimast.Node) bool {
186
582
  }
187
583
  }
188
584
  switch body.Kind {
189
- case shimast.KindBlock,
190
- shimast.KindObjectLiteralExpression,
585
+ case shimast.KindBlock:
586
+ // A block-bodied arrow hugs regardless of any return-type annotation.
587
+ return true
588
+ case shimast.KindObjectLiteralExpression,
191
589
  shimast.KindArrayLiteralExpression:
590
+ // An object/array-bodied arrow hugs unless its return type is a type
591
+ // REFERENCE. Prettier's couldExpandArg gates only on
592
+ // `returnType.typeAnnotation.type === "TSTypeReference"`, so a keyword,
593
+ // union, array, or literal return type (`map((r): void => ({ … }))`)
594
+ // still hugs while a named-reference return (`map((r): Foo => ({ … })))`)
595
+ // explodes the whole list. A block body hugs regardless (handled above).
596
+ return arrow.Type == nil || arrow.Type.Kind != shimast.KindTypeReference
597
+ }
598
+ }
599
+ // DEFERRED (architectural, not a one-line predicate fix; do not re-add to the
600
+ // switch above without the supporting layout work):
601
+ // - A call/conditional/JSX-expression arrow body. Prettier's couldExpandArg
602
+ // hugs it, but ttsc emits the arrow signature (`=> `) verbatim with no
603
+ // break point after `=>`, so hugging here would force the FIRST group
604
+ // inside the call instead of breaking after `=>` — a different shape, not
605
+ // parity. Needs a break-after-`=>` arrow-body layout variant first.
606
+ // - A trailing `as`/`satisfies` cast wrapping a huggable last argument.
607
+ // Prettier's couldExpandArg recurses into the cast, but ttsc does not
608
+ // dispatch KindAsExpression (it prints verbatim), so forceBreakFirstGroup
609
+ // finds no group and the hug is inert. Needs a cast printer first.
610
+ return false
611
+ }
612
+
613
+ // shouldHugFirstArgument reports whether a call's argument list takes
614
+ // Prettier's "first-argument hugging" shape: exactly two arguments where
615
+ // the first is a block-bodied callback and the second is a short, simple
616
+ // value. `foo(() => { … }, target)` keeps the callback attached to the
617
+ // open paren and flows `, target)` after its closing brace, instead of
618
+ // exploding both arguments onto their own lines.
619
+ //
620
+ // Mirrors Prettier's shouldGroupFirstArg: it declines when the second
621
+ // argument is itself a function, arrow, conditional, or any expandable
622
+ // shape, so `both(() => {}, () => {})` falls through to the exploded
623
+ // list rather than hugging.
624
+ func shouldHugFirstArgument(ctx *PrintContext, args []*shimast.Node) bool {
625
+ if len(args) != 2 {
626
+ return false
627
+ }
628
+ first, second := args[0], args[1]
629
+ if first == nil || second == nil {
630
+ return false
631
+ }
632
+ if !isFirstArgHuggableCallback(first) {
633
+ return false
634
+ }
635
+ // A NON-EMPTY array trailing arg hugs only in Prettier's React-hook deps
636
+ // shape (isReactHookCallWithDepsArray): a ZERO-parameter arrow callback plus
637
+ // an array, e.g. `useEffect(() => { … }, [a, b])`. With a parameter the call
638
+ // explodes (`subscribe((event) => { … }, [a, b])`), and a function expression
639
+ // (not an arrow) never qualifies. An EMPTY array falls through to
640
+ // isSimpleTrailingArg.
641
+ if second.Kind == shimast.KindArrayLiteralExpression {
642
+ if arr := second.AsArrayLiteralExpression(); arr != nil &&
643
+ arr.Elements != nil && len(arr.Elements.Nodes) > 0 {
644
+ return first.Kind == shimast.KindArrowFunction && len(first.Parameters()) == 0
645
+ }
646
+ }
647
+ return isSimpleTrailingArg(ctx, second)
648
+ }
649
+
650
+ // isFirstArgHuggableCallback reports whether `node` is the callback shape
651
+ // Prettier hugs in the first-argument position: a function expression or
652
+ // an arrow with a block body. An expression-bodied arrow is excluded
653
+ // because it carries no internal break point.
654
+ func isFirstArgHuggableCallback(node *shimast.Node) bool {
655
+ switch node.Kind {
656
+ case shimast.KindFunctionExpression:
657
+ return true
658
+ case shimast.KindArrowFunction:
659
+ arrow := node.AsArrowFunction()
660
+ return arrow != nil && arrow.Body != nil && arrow.Body.Kind == shimast.KindBlock
661
+ }
662
+ return false
663
+ }
664
+
665
+ // isSimpleTrailingArg reports whether `node` is a value that may trail a
666
+ // hugged first-argument callback. Prettier hugs the leading callback when
667
+ // the trailing argument is an identifier, member access, literal, `this`,
668
+ // or an EMPTY array/object literal. It also accepts a short call/new (at
669
+ // most one value argument) and a short arithmetic/logical expression
670
+ // (`setTimeout(fn, 1000 - x)`). A function, arrow, conditional, a non-empty
671
+ // array, or a non-empty object literal is excluded, so first-argument hugging
672
+ // declines and the whole list explodes. (A non-empty array after a
673
+ // zero-parameter arrow is the React-hook deps shape, hugged earlier in
674
+ // shouldHugFirstArgument and never routed here.)
675
+ //
676
+ // The call/new case rides the close line: the conditional-group fit check
677
+ // only measures an option's first line, so a hugged-first option whose
678
+ // trailing call overflows the closing line is still selected. Prettier
679
+ // accepts the same trade for a short trailing call.
680
+ func isSimpleTrailingArg(ctx *PrintContext, node *shimast.Node) bool {
681
+ switch node.Kind {
682
+ case shimast.KindIdentifier,
683
+ shimast.KindStringLiteral,
684
+ shimast.KindNumericLiteral,
685
+ shimast.KindBigIntLiteral,
686
+ shimast.KindTrueKeyword,
687
+ shimast.KindFalseKeyword,
688
+ shimast.KindNullKeyword,
689
+ shimast.KindThisKeyword,
690
+ shimast.KindPropertyAccessExpression,
691
+ shimast.KindElementAccessExpression:
692
+ return true
693
+ case shimast.KindArrayLiteralExpression:
694
+ // Only an EMPTY array is simple-trailing (the `[] as T[]` cast idiom).
695
+ // Prettier's couldExpandArg treats an array with elements as expandable,
696
+ // so a non-empty array cast (`[x] as number[]`) explodes the list rather
697
+ // than hugging. The non-empty bare-array deps shape (zero-param arrow) is
698
+ // handled in shouldHugFirstArgument before reaching here.
699
+ if arr := node.AsArrayLiteralExpression(); arr != nil {
700
+ return arr.Elements == nil || len(arr.Elements.Nodes) == 0
701
+ }
702
+ return false
703
+ case shimast.KindAsExpression:
704
+ // `[] as string[]`: the `reduce(fn, [] as T[])` idiom. Hug when the cast
705
+ // target is a simple type AND its inner expression is simple at depth 1 —
706
+ // Prettier's isHopefullyShortCallArgument cast branch is exactly
707
+ // `isSimpleType(typeAnnotation) && isSimpleCallArgument(node.expression, 1)`.
708
+ // Depth 1 (not the general isSimpleTrailingArg) is deliberate: it has no
709
+ // binaryish branch and bottoms a nested call's argument out at depth 0, so
710
+ // `(a - b) as T` and `makeInit(x) as T[]` are NOT simple and explode.
711
+ if as := node.AsAsExpression(); as != nil && as.Expression != nil {
712
+ return isSimpleCallArg(as.Expression, 1) && isSimpleCastType(as.Type)
713
+ }
714
+ case shimast.KindSatisfiesExpression:
715
+ // `[] satisfies T[]`: Prettier's isBinaryCastExpression covers
716
+ // TSSatisfiesExpression identically to TSAsExpression, so a satisfies cast
717
+ // takes the same simple-type + depth-1 simple-expression path as `as`.
718
+ if s := node.AsSatisfiesExpression(); s != nil && s.Expression != nil {
719
+ return isSimpleCallArg(s.Expression, 1) && isSimpleCastType(s.Type)
720
+ }
721
+ case shimast.KindCallExpression, shimast.KindNewExpression:
722
+ // `reduce(fn, Object.create(null))` / `reduce(fn, new Map<…>())`: Prettier
723
+ // hugs the first arg over a trailing call/new only when it has at most one
724
+ // value argument AND that argument is itself structurally simple. Its
725
+ // isHopefullyShortCallArgument early-rejects >1 args, then falls through to
726
+ // isSimpleCallArgument (depth 2), so a 1-arg call whose sole argument is a
727
+ // non-trivial nested call/object/array (`reduce(fn, makeInit(deep(arg)))`)
728
+ // is NOT simple and the whole list explodes. Type arguments and long names
729
+ // still do not matter; the close line is allowed to overflow.
730
+ return callValueArgCount(node) <= 1 && isSimpleCallArg(node, 2)
731
+ case shimast.KindObjectLiteralExpression:
732
+ // `reduce(fn, {})`: an EMPTY object literal hugs (Prettier's couldGroupArg
733
+ // excludes a property-less object); an object with properties expands and
734
+ // explodes the list instead (`f(fn, { a: 1 })`).
735
+ if obj := node.AsObjectLiteralExpression(); obj != nil {
736
+ return obj.Properties == nil || len(obj.Properties.Nodes) == 0
737
+ }
738
+ case shimast.KindBinaryExpression:
739
+ // `setTimeout(fn, 1000 - ellapsed)`: a SINGLE arithmetic/logical operation
740
+ // whose two operands are simple leaves rides the close line and hugs. A
741
+ // chained binary (`60 * 60 * 1000`, an operand itself a binary) is not
742
+ // simple and explodes the list, matching Prettier's isHopefullyShortCallArgument
743
+ // (which only treats a binaryish short when both sides are themselves short).
744
+ if bin := node.AsBinaryExpression(); bin != nil {
745
+ return isSimpleBinaryOperand(bin.Left) && isSimpleBinaryOperand(bin.Right)
746
+ }
747
+ case shimast.KindPrefixUnaryExpression:
748
+ // `reduce(fn, -1)`: a unary applied to a simple leaf rides the close line.
749
+ if u := node.AsPrefixUnaryExpression(); u != nil {
750
+ return isSimpleBinaryOperand(u.Operand)
751
+ }
752
+ }
753
+ return false
754
+ }
755
+
756
+ // isSimpleCallArg ports Prettier's isSimpleCallArgument (utilities/index.js): a
757
+ // depth-bounded structural simplicity check. A node is simple when it is a leaf
758
+ // (identifier, literal, this, member access) or a shallow composite whose parts
759
+ // are simple at a reduced depth — a call/new is simple only when its callee is
760
+ // simple, it has at most `depth` arguments, and every argument is simple at
761
+ // `depth-1`. The recursion floor (`depth <= 0`) is what makes a nested call
762
+ // like `makeInit(deep(arg))` non-simple, so first-argument hugging declines and
763
+ // the list explodes, matching Prettier. A non-empty object is treated as
764
+ // non-simple (its values bottom out below the depth floor anyway — the safe
765
+ // under-match direction). Element access is treated as a simple leaf, matching
766
+ // ttsc's existing member-access handling, rather than recursing into its
767
+ // computed key (a pre-existing, rare residual over-match left out of scope).
768
+ func isSimpleCallArg(node *shimast.Node, depth int) bool {
769
+ if node == nil || depth <= 0 {
770
+ return false
771
+ }
772
+ switch node.Kind {
773
+ case shimast.KindIdentifier,
774
+ shimast.KindStringLiteral,
775
+ shimast.KindNumericLiteral,
776
+ shimast.KindBigIntLiteral,
777
+ shimast.KindTrueKeyword,
778
+ shimast.KindFalseKeyword,
779
+ shimast.KindNullKeyword,
780
+ shimast.KindThisKeyword,
781
+ shimast.KindElementAccessExpression:
782
+ return true
783
+ case shimast.KindParenthesizedExpression:
784
+ if p := node.AsParenthesizedExpression(); p != nil {
785
+ return isSimpleCallArg(p.Expression, depth)
786
+ }
787
+ case shimast.KindNonNullExpression:
788
+ // Prettier's isSimpleCallArgument unwraps TSNonNullExpression at the same
789
+ // depth, so `foo!()` / `target!` recurse into the asserted expression.
790
+ if n := node.AsNonNullExpression(); n != nil {
791
+ return isSimpleCallArg(n.Expression, depth)
792
+ }
793
+ case shimast.KindPrefixUnaryExpression:
794
+ if u := node.AsPrefixUnaryExpression(); u != nil {
795
+ return isSimpleCallArg(u.Operand, depth)
796
+ }
797
+ case shimast.KindPropertyAccessExpression:
798
+ // The `.name` half is an identifier (trivially simple); recurse the object
799
+ // so `a.b(x, y).c` (a member of a multi-argument call) is correctly not
800
+ // simple, matching Prettier's member recursion.
801
+ if m := node.AsPropertyAccessExpression(); m != nil {
802
+ return isSimpleCallArg(m.Expression, depth)
803
+ }
804
+ case shimast.KindArrayLiteralExpression:
805
+ if arr := node.AsArrayLiteralExpression(); arr != nil {
806
+ if arr.Elements == nil {
807
+ return true
808
+ }
809
+ for _, el := range arr.Elements.Nodes {
810
+ if !isSimpleCallArg(el, depth-1) {
811
+ return false
812
+ }
813
+ }
192
814
  return true
193
815
  }
816
+ case shimast.KindObjectLiteralExpression:
817
+ // Only an empty object is treated as simple. A non-empty object's values
818
+ // are checked at depth-1, which bottoms out to false at every depth that
819
+ // occurs as a trailing-call argument, so declining one never over-matches.
820
+ if obj := node.AsObjectLiteralExpression(); obj != nil {
821
+ return obj.Properties == nil || len(obj.Properties.Nodes) == 0
822
+ }
823
+ case shimast.KindCallExpression:
824
+ if c := node.AsCallExpression(); c != nil {
825
+ var args []*shimast.Node
826
+ if c.Arguments != nil {
827
+ args = c.Arguments.Nodes
828
+ }
829
+ if !isSimpleCallArg(c.Expression, depth) || len(args) > depth {
830
+ return false
831
+ }
832
+ for _, a := range args {
833
+ if !isSimpleCallArg(a, depth-1) {
834
+ return false
835
+ }
836
+ }
837
+ return true
838
+ }
839
+ case shimast.KindNewExpression:
840
+ if n := node.AsNewExpression(); n != nil {
841
+ var args []*shimast.Node
842
+ if n.Arguments != nil {
843
+ args = n.Arguments.Nodes
844
+ }
845
+ if !isSimpleCallArg(n.Expression, depth) || len(args) > depth {
846
+ return false
847
+ }
848
+ for _, a := range args {
849
+ if !isSimpleCallArg(a, depth-1) {
850
+ return false
851
+ }
852
+ }
853
+ return true
854
+ }
855
+ }
856
+ return false
857
+ }
858
+
859
+ // isSimpleBinaryOperand reports whether a node is a short, non-recursive
860
+ // operand, an identifier, a literal, `this`, or a member access. A binary,
861
+ // call, conditional, or other compound expression is not, so a binary built
862
+ // from such operands is treated as too complex to ride a hugged close line.
863
+ func isSimpleBinaryOperand(node *shimast.Node) bool {
864
+ if node == nil {
865
+ return false
866
+ }
867
+ switch node.Kind {
868
+ case shimast.KindIdentifier,
869
+ shimast.KindNumericLiteral,
870
+ shimast.KindStringLiteral,
871
+ shimast.KindBigIntLiteral,
872
+ shimast.KindTrueKeyword,
873
+ shimast.KindFalseKeyword,
874
+ shimast.KindNullKeyword,
875
+ shimast.KindThisKeyword,
876
+ shimast.KindPropertyAccessExpression,
877
+ shimast.KindElementAccessExpression:
878
+ return true
879
+ }
880
+ return false
881
+ }
882
+
883
+ // isSimpleCastType ports the type half of Prettier's isHopefullyShortCallArgument
884
+ // cast branch: unwrap an array type up to two levels (`T[]`, `T[][]` -> `T`) and
885
+ // a single-type-argument reference (`Ref<X>` -> `X`), then require a SIMPLE type
886
+ // (isSimpleTypeNode). A reference still carrying type arguments after the unwrap
887
+ // (`Foo<A, B>`, `Array<Array<string>>`), or a union / intersection / function /
888
+ // tuple / object type, is not simple, so the cast declines the first-argument
889
+ // hug and the list explodes, matching Prettier.
890
+ func isSimpleCastType(typeNode *shimast.Node) bool {
891
+ node := typeNode
892
+ for i := 0; i < 2 && node != nil && node.Kind == shimast.KindArrayType; i++ {
893
+ at := node.AsArrayTypeNode()
894
+ if at == nil || at.ElementType == nil {
895
+ return false
896
+ }
897
+ node = at.ElementType
898
+ }
899
+ if node != nil && node.Kind == shimast.KindTypeReference {
900
+ if ref := node.AsTypeReferenceNode(); ref != nil && ref.TypeArguments != nil &&
901
+ len(ref.TypeArguments.Nodes) == 1 {
902
+ node = ref.TypeArguments.Nodes[0]
903
+ }
904
+ }
905
+ return isSimpleTypeNode(node)
906
+ }
907
+
908
+ // isSimpleTypeNode mirrors Prettier's isSimpleType: a keyword/primitive type, a
909
+ // literal type, `this`, or a bare type reference with NO type arguments.
910
+ func isSimpleTypeNode(node *shimast.Node) bool {
911
+ if node == nil {
912
+ return false
913
+ }
914
+ switch node.Kind {
915
+ case shimast.KindStringKeyword, shimast.KindNumberKeyword, shimast.KindBooleanKeyword,
916
+ shimast.KindAnyKeyword, shimast.KindUnknownKeyword, shimast.KindVoidKeyword,
917
+ shimast.KindNeverKeyword, shimast.KindUndefinedKeyword, shimast.KindNullKeyword,
918
+ shimast.KindObjectKeyword, shimast.KindSymbolKeyword, shimast.KindBigIntKeyword,
919
+ shimast.KindThisType, shimast.KindLiteralType, shimast.KindTemplateLiteralType:
920
+ return true
921
+ case shimast.KindTypeReference:
922
+ ref := node.AsTypeReferenceNode()
923
+ return ref != nil && (ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) == 0)
194
924
  }
195
925
  return false
196
926
  }
197
927
 
928
+ // callValueArgCount returns the number of value arguments on a call or new
929
+ // expression (type arguments are not counted). A new expression with no
930
+ // argument list (`new Foo`) counts as zero.
931
+ func callValueArgCount(node *shimast.Node) int {
932
+ switch node.Kind {
933
+ case shimast.KindCallExpression:
934
+ if c := node.AsCallExpression(); c != nil && c.Arguments != nil {
935
+ return len(c.Arguments.Nodes)
936
+ }
937
+ case shimast.KindNewExpression:
938
+ if n := node.AsNewExpression(); n != nil && n.Arguments != nil {
939
+ return len(n.Arguments.Nodes)
940
+ }
941
+ }
942
+ return 0
943
+ }
944
+
198
945
  // forceBreakFirstGroup returns `doc` with the first Group found in a
199
946
  // left-to-right walk of its subtree forced broken, and reports whether
200
947
  // one was found. printListHuggingLast uses it to commit a hugged
@@ -225,72 +972,34 @@ func forceBreakFirstGroup(doc Doc) (Doc, bool) {
225
972
 
226
973
  // Type-argument byte-range helpers. The shim's NodeList.End() points
227
974
  // past the last argument; the surrounding `<` and `>` are not part of
228
- // the list's range, so we have to scan around it.
975
+ // the list's range, so we have to scan around it. Call and new
976
+ // expressions share these — only the field holding the list differs.
229
977
 
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 {
978
+ // typeArgsStart returns the byte offset of the `<` that opens a
979
+ // type-argument list. Returns -1 when absent.
980
+ func typeArgsStart(src string, list *shimast.NodeList) int {
981
+ if list == nil || len(list.Nodes) == 0 || list.Nodes[0] == nil {
238
982
  return -1
239
983
  }
240
984
  // `<` is the byte immediately before the first type argument
241
985
  // (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] == '<' {
986
+ for i := list.Nodes[0].Pos() - 1; i >= 0; i-- {
987
+ if src[i] == '<' {
279
988
  return i
280
989
  }
281
990
  }
282
991
  return -1
283
992
  }
284
993
 
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 {
994
+ // typeArgsEnd returns the byte offset one past the closing `>` of a
995
+ // type-argument list. Returns -1 when the list is absent.
996
+ func typeArgsEnd(src string, list *shimast.NodeList) int {
997
+ if list == nil {
289
998
  return -1
290
999
  }
291
- end := ne.TypeArguments.End()
292
- for i := end; i < len(ctx.Source); i++ {
293
- if ctx.Source[i] == '>' {
1000
+ end := list.End()
1001
+ for i := end; i < len(src); i++ {
1002
+ if src[i] == '>' {
294
1003
  return i + 1
295
1004
  }
296
1005
  }