@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
@@ -0,0 +1,664 @@
1
+ package linthost
2
+
3
+ import (
4
+ "strings"
5
+
6
+ shimast "github.com/microsoft/typescript-go/shim/ast"
7
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
8
+ )
9
+
10
+ // formatDeclarationHeader reflows the header of a class or interface
11
+ // declaration, its type-parameter list and `extends`/`implements`
12
+ // clauses up to the opening `{`, to match Prettier 3. The member body
13
+ // is never touched (members keep reflowing independently via
14
+ // format/print-width), so the rule edits only the byte range
15
+ // `[header start, '{']` and can never overlap a member edit.
16
+ //
17
+ // It reconstructs the header canonically and emits an edit only when the
18
+ // result differs from the source, so it both fixes a Prettier-2-style
19
+ // broken header and leaves an already-correct one alone (idempotent).
20
+ //
21
+ // Verified Prettier 3 shapes (printWidth W, the header overflows):
22
+ //
23
+ // - Single heritage type, no type parameters -> kept inline (Prettier
24
+ // never breaks a lone `extends X<Y>`), so the rule abstains.
25
+ // - One clause with multiple types -> break before the keyword and keep
26
+ // the types inline; explode them one-per-line only when that inline
27
+ // line still overflows:
28
+ // interface B
29
+ // extends First, Second {
30
+ // - Multiple clauses -> break before each keyword, types inline:
31
+ // class C
32
+ // extends Base
33
+ // implements First, Second
34
+ // {
35
+ // - Opening brace placement -> Prettier moves `{` onto its own line only
36
+ // for a class with a non-empty body; an interface and any empty body
37
+ // keep it glued to the last header line (so `… implements X {}`).
38
+ // - Type-parameter list overflow -> explode the `<...>` list (trailing
39
+ // comma, `>` at the base indent), heritage inline after `>`:
40
+ // interface D<
41
+ // TKey extends string,
42
+ // > extends Base<TKey> {
43
+ // A single parameter whose `extends C = D` overflows breaks after `=`.
44
+ //
45
+ // Any combination the rule has not verified (e.g. type parameters AND a
46
+ // breaking heritage clause at once), a header carrying comments, or a
47
+ // type/parameter that is itself multi-line makes the rule ABSTAIN, so it
48
+ // never emits a header it cannot reproduce exactly.
49
+ type formatDeclarationHeader struct{}
50
+
51
+ type formatDeclarationHeaderOptions struct {
52
+ PrintWidth *int `json:"printWidth"`
53
+ TabWidth *int `json:"tabWidth"`
54
+ UseTabs *bool `json:"useTabs"`
55
+ EndOfLine *string `json:"endOfLine"`
56
+ }
57
+
58
+ func (formatDeclarationHeader) Name() string { return "format/declaration-header" }
59
+ func (formatDeclarationHeader) IsFormat() bool { return true }
60
+
61
+ func (formatDeclarationHeader) Visits() []shimast.Kind {
62
+ return []shimast.Kind{
63
+ shimast.KindClassDeclaration,
64
+ shimast.KindInterfaceDeclaration,
65
+ }
66
+ }
67
+
68
+ func (formatDeclarationHeader) Check(ctx *Context, node *shimast.Node) {
69
+ if ctx == nil || ctx.File == nil || node == nil {
70
+ return
71
+ }
72
+ typeParams, heritage, name := declarationHeaderParts(node)
73
+ if name == nil {
74
+ return
75
+ }
76
+ hasTypeParams := typeParams != nil && len(typeParams.Nodes) > 0
77
+ hasHeritage := heritage != nil && len(heritage.Nodes) > 0
78
+ if !hasTypeParams && !hasHeritage {
79
+ return // nothing in the header to reflow
80
+ }
81
+
82
+ src := ctx.File.Text()
83
+ nameEnd := name.End()
84
+ if nameEnd <= 0 || nameEnd > len(src) {
85
+ return
86
+ }
87
+
88
+ // The header line starts at the first non-whitespace byte of the line
89
+ // holding the declaration name; decorators and modifiers on that line
90
+ // are carried verbatim in the prefix.
91
+ headerStart := lineFirstNonSpace(src, name.Pos())
92
+ if headerStart < 0 {
93
+ return
94
+ }
95
+
96
+ // Locate the `{` that opens the body, scanning from the end of the
97
+ // last header element so a `{` inside a type-parameter default does
98
+ // not fool the search.
99
+ scanFrom := nameEnd
100
+ if hasTypeParams {
101
+ // A type-parameter NodeList ends at the last parameter, before the
102
+ // closing `>`. Advance past that `>` so the body-brace search does
103
+ // not stop on it (the heritage case is already past it via
104
+ // heritage.End()).
105
+ scanFrom = maxInt(scanFrom, typeParams.End())
106
+ if gt := findCloseTokenAfter(src, typeParams.End(), '>'); gt >= 0 {
107
+ scanFrom = maxInt(scanFrom, gt+1)
108
+ }
109
+ }
110
+ if hasHeritage {
111
+ scanFrom = maxInt(scanFrom, heritage.End())
112
+ }
113
+ bracePos := findCloseTokenAfter(src, scanFrom, '{')
114
+ if bracePos < 0 {
115
+ return
116
+ }
117
+
118
+ // Everything from the end of the verbatim prefix (the name) to the
119
+ // brace is discarded and rebuilt element by element, so a comment
120
+ // anywhere in that span would be lost, including one between the name
121
+ // and `extends`, or between `extends` and its first type. Scan the whole
122
+ // [nameEnd, brace) region and abstain on any comment.
123
+ if containsComment(src[nameEnd:bracePos]) {
124
+ return
125
+ }
126
+
127
+ layout := loadDeclarationHeaderLayout(ctx)
128
+ // The prefix is the modifiers + keyword + name, which live between the
129
+ // header line's first token and the name's end. Type parameters and
130
+ // heritage are rebuilt separately, so anything after the name (incl.
131
+ // newlines an already-broken header introduced) is not part of it.
132
+ prefix := strings.TrimRight(src[headerStart:nameEnd], " \t")
133
+ if strings.ContainsRune(prefix, '\n') {
134
+ return // modifiers split across lines: abstain
135
+ }
136
+
137
+ // Gather verbatim element texts; abstain if any spans multiple lines.
138
+ paramTexts, ok := nodeListTexts(src, typeParams)
139
+ if !ok {
140
+ return
141
+ }
142
+ clauses, ok := heritageClauseInfos(src, heritage)
143
+ if !ok {
144
+ return
145
+ }
146
+
147
+ base := src[lineStartOffset(src, headerStart):headerStart]
148
+ startCol := visualWidth(base, layout.tabWidth)
149
+ flat := prefix + flatTypeParams(paramTexts) + flatHeritage(clauses) + " {"
150
+ region := src[headerStart : bracePos+1]
151
+
152
+ // An empty body renders its closing `}` on the same line (`… {}`), so the
153
+ // flat form is one column wider than the rebuilt region (which ends at the
154
+ // `{`). Charge that column when deciding whether the header fits, or the
155
+ // fit check is off by one against Prettier at the width boundary.
156
+ isClass := node.Kind == shimast.KindClassDeclaration
157
+ emptyBody := headerBodyIsEmpty(src, bracePos)
158
+ flatExtra := 0
159
+ if emptyBody {
160
+ flatExtra = 1
161
+ }
162
+
163
+ var target string
164
+ if startCol+visualWidth(flat, layout.tabWidth)+flatExtra <= layout.printWidth {
165
+ target = flat
166
+ } else if t, ok2 := singleGenericHeritageHeader(src, base, prefix, typeParams, heritage, layout); ok2 {
167
+ // A lone heritage clause whose single generic type has two or more type
168
+ // arguments breaks the argument list, not the clause (`extends Omit<\n …\n>`).
169
+ target = t
170
+ } else {
171
+ // Prettier drops the opening brace onto its own line only for a class
172
+ // with a non-empty body; an interface (any body) and an empty body
173
+ // keep `{` glued to the last header line (so an empty body reads
174
+ // `… {}`). isClass && !emptyBody captures that.
175
+ brace := headerBrace(isClass, emptyBody, base)
176
+ target, ok = brokenDeclarationHeader(src, base, prefix, typeParams, paramTexts, clauses, layout, brace, emptyBody, isClass)
177
+ if !ok {
178
+ return // unverified combination: abstain
179
+ }
180
+ }
181
+ if target == region {
182
+ return
183
+ }
184
+ ctx.ReportRangeFix(
185
+ headerStart,
186
+ bracePos+1,
187
+ "Reflow class/interface header to match Prettier.",
188
+ TextEdit{Pos: headerStart, End: bracePos + 1, Text: target},
189
+ )
190
+ }
191
+
192
+ // declarationHeaderLayout bundles the resolved width/indent settings.
193
+ type declarationHeaderLayout struct {
194
+ printWidth int
195
+ tabWidth int
196
+ oneLevel string
197
+ }
198
+
199
+ func (l declarationHeaderLayout) indent(base string, depth int) string {
200
+ return base + strings.Repeat(l.oneLevel, depth)
201
+ }
202
+
203
+ func loadDeclarationHeaderLayout(ctx *Context) declarationHeaderLayout {
204
+ var opts formatDeclarationHeaderOptions
205
+ _ = ctx.DecodeOptions(&opts)
206
+ l := declarationHeaderLayout{printWidth: 80, tabWidth: 2, oneLevel: " "}
207
+ if opts.PrintWidth != nil && *opts.PrintWidth > 0 {
208
+ l.printWidth = *opts.PrintWidth
209
+ }
210
+ if opts.TabWidth != nil && *opts.TabWidth > 0 {
211
+ l.tabWidth = *opts.TabWidth
212
+ }
213
+ if opts.UseTabs != nil && *opts.UseTabs {
214
+ l.oneLevel = "\t"
215
+ } else if opts.TabWidth != nil && *opts.TabWidth > 0 {
216
+ l.oneLevel = strings.Repeat(" ", *opts.TabWidth)
217
+ }
218
+ return l
219
+ }
220
+
221
+ // heritageClauseText pairs a clause keyword with its type texts.
222
+ type heritageClauseText struct {
223
+ keyword string
224
+ types []string
225
+ }
226
+
227
+ // declarationHeaderParts returns the type-parameter list, heritage clause
228
+ // list, and name node for a class or interface declaration.
229
+ func declarationHeaderParts(node *shimast.Node) (*shimast.NodeList, *shimast.NodeList, *shimast.Node) {
230
+ switch node.Kind {
231
+ case shimast.KindClassDeclaration:
232
+ d := node.AsClassDeclaration()
233
+ if d == nil {
234
+ return nil, nil, nil
235
+ }
236
+ return d.TypeParameters, d.HeritageClauses, node.Name()
237
+ case shimast.KindInterfaceDeclaration:
238
+ d := node.AsInterfaceDeclaration()
239
+ if d == nil {
240
+ return nil, nil, nil
241
+ }
242
+ return d.TypeParameters, d.HeritageClauses, node.Name()
243
+ }
244
+ return nil, nil, nil
245
+ }
246
+
247
+ // nodeListTexts returns the trimmed verbatim source of each node in the
248
+ // list. ok is false if any node spans multiple lines, so the caller
249
+ // abstains rather than collapsing a multi-line element.
250
+ func nodeListTexts(src string, list *shimast.NodeList) ([]string, bool) {
251
+ if list == nil {
252
+ return nil, true
253
+ }
254
+ out := make([]string, 0, len(list.Nodes))
255
+ for _, n := range list.Nodes {
256
+ if n == nil {
257
+ return nil, false
258
+ }
259
+ s := shimscanner.SkipTrivia(src, n.Pos())
260
+ e := n.End()
261
+ if s < 0 || e < s || e > len(src) {
262
+ return nil, false
263
+ }
264
+ text := strings.TrimRight(src[s:e], " \t")
265
+ if strings.ContainsRune(text, '\n') {
266
+ return nil, false
267
+ }
268
+ out = append(out, text)
269
+ }
270
+ return out, true
271
+ }
272
+
273
+ // heritageClauseInfos extracts keyword + type texts for each heritage
274
+ // clause. ok is false on any multi-line type, so the caller abstains.
275
+ func heritageClauseInfos(src string, list *shimast.NodeList) ([]heritageClauseText, bool) {
276
+ if list == nil {
277
+ return nil, true
278
+ }
279
+ out := make([]heritageClauseText, 0, len(list.Nodes))
280
+ for _, clauseNode := range list.Nodes {
281
+ if clauseNode == nil {
282
+ return nil, false
283
+ }
284
+ clause := clauseNode.AsHeritageClause()
285
+ if clause == nil || clause.Types == nil {
286
+ return nil, false
287
+ }
288
+ keyword := "implements"
289
+ if clause.Token == shimast.KindExtendsKeyword {
290
+ keyword = "extends"
291
+ }
292
+ types, ok := nodeListTexts(src, clause.Types)
293
+ if !ok || len(types) == 0 {
294
+ return nil, false
295
+ }
296
+ out = append(out, heritageClauseText{keyword: keyword, types: types})
297
+ }
298
+ return out, true
299
+ }
300
+
301
+ func flatTypeParams(params []string) string {
302
+ if len(params) == 0 {
303
+ return ""
304
+ }
305
+ return "<" + strings.Join(params, ", ") + ">"
306
+ }
307
+
308
+ func flatHeritage(clauses []heritageClauseText) string {
309
+ var b strings.Builder
310
+ for _, c := range clauses {
311
+ b.WriteString(" ")
312
+ b.WriteString(c.keyword)
313
+ b.WriteString(" ")
314
+ b.WriteString(strings.Join(c.types, ", "))
315
+ }
316
+ return b.String()
317
+ }
318
+
319
+ // brokenDeclarationHeader builds the multi-line header for the verified
320
+ // strategies, or returns ok=false for an unverified combination. `base`
321
+ // is the declaration line's leading indent; continuation lines indent
322
+ // from it while the first line keeps its existing indent (the edit
323
+ // starts at the line's first non-space byte).
324
+ func brokenDeclarationHeader(
325
+ src string,
326
+ base string,
327
+ prefix string,
328
+ typeParams *shimast.NodeList,
329
+ paramTexts []string,
330
+ clauses []heritageClauseText,
331
+ layout declarationHeaderLayout,
332
+ brace string,
333
+ emptyBody bool,
334
+ isClass bool,
335
+ ) (string, bool) {
336
+ hasTypeParams := len(paramTexts) > 0
337
+ switch {
338
+ case len(clauses) >= 2:
339
+ if hasTypeParams {
340
+ return "", false
341
+ }
342
+ return multiClauseHeader(prefix, clauses, layout, base, brace), true
343
+ case len(clauses) == 1 && len(clauses[0].types) >= 2:
344
+ if hasTypeParams {
345
+ return "", false
346
+ }
347
+ return multiTypeHeader(prefix, clauses[0], layout, base, brace, emptyBody), true
348
+ case hasTypeParams:
349
+ if len(clauses) == 1 && len(clauses[0].types) >= 2 {
350
+ return "", false
351
+ }
352
+ return typeParamExplodeHeader(src, base, prefix, typeParams, clauses, layout, isClass), true
353
+ }
354
+ return "", false
355
+ }
356
+
357
+ // interfaceHeritageOnOwnLine reports whether Prettier moves a broken-type-
358
+ // parameter interface's heritage onto its own line. Verified against Prettier
359
+ // 3: it does so only for a single `extends` clause whose single type is a
360
+ // qualified name (`extends IPage.IRequest`), a member expression. A bare
361
+ // identifier (`extends IBase`, any length) and a generic type (`extends
362
+ // Base<T>`) both stay inline after `>`. A class always keeps the heritage
363
+ // inline regardless. The `.`-and-no-`<` test distinguishes the qualified
364
+ // non-generic case; a generic-qualified type (`A.B<C>`) is left inline,
365
+ // unverified, so the rule never invents a break it has not confirmed.
366
+ func interfaceHeritageOnOwnLine(isClass bool, clauses []heritageClauseText) bool {
367
+ if isClass || len(clauses) != 1 {
368
+ return false
369
+ }
370
+ c := clauses[0]
371
+ if c.keyword != "extends" || len(c.types) != 1 {
372
+ return false
373
+ }
374
+ return strings.Contains(c.types[0], ".") && !strings.Contains(c.types[0], "<")
375
+ }
376
+
377
+ // multiClauseHeader: break before each keyword. Per clause, the types stay
378
+ // inline (`implements A, B`) when they fit, and break onto their own line(s) at
379
+ // the next indent level when the inline clause overflows. A single-type clause
380
+ // is no exception inside a multi-clause header: Prettier breaks after the
381
+ // keyword and drops the lone type to the next line when `keyword Type<…>` would
382
+ // overflow (`implements\n ITreeRenderer<…>`), generic or not. (A lone
383
+ // single-clause header behaves differently and never reaches here, a single
384
+ // generic type breaks its argument list, a single bare name stays inline.) Only
385
+ // a class carries multiple clauses (`extends` + `implements`), so `brace` is the
386
+ // class brace placement decided by the caller; when it is glued (` {`, an empty
387
+ // class body) it shares the final clause's line and is charged against that
388
+ // clause's fit check.
389
+ func multiClauseHeader(prefix string, clauses []heritageClauseText, layout declarationHeaderLayout, base, brace string) string {
390
+ var b strings.Builder
391
+ b.WriteString(prefix)
392
+ indent1 := layout.indent(base, 1)
393
+ indent2 := layout.indent(base, 2)
394
+ for ci, c := range clauses {
395
+ b.WriteString("\n")
396
+ b.WriteString(indent1)
397
+ b.WriteString(c.keyword)
398
+ inlineWidth := visualWidth(indent1+c.keyword+" "+strings.Join(c.types, ", "), layout.tabWidth)
399
+ if ci == len(clauses)-1 && !strings.HasPrefix(brace, "\n") {
400
+ inlineWidth += visualWidth(brace, layout.tabWidth)
401
+ }
402
+ if inlineWidth <= layout.printWidth {
403
+ b.WriteString(" ")
404
+ b.WriteString(strings.Join(c.types, ", "))
405
+ continue
406
+ }
407
+ for i, t := range c.types {
408
+ b.WriteString("\n")
409
+ b.WriteString(indent2)
410
+ b.WriteString(t)
411
+ if i < len(c.types)-1 {
412
+ b.WriteString(",")
413
+ }
414
+ }
415
+ }
416
+ b.WriteString(brace)
417
+ return b.String()
418
+ }
419
+
420
+ // multiTypeHeader renders a single heritage clause with two or more types.
421
+ // Prettier breaks before the keyword and first tries to keep the types
422
+ // inline on that continuation line (`extends A, B, C`); it only explodes
423
+ // them one-per-line when the inline line itself still overflows. `brace`
424
+ // carries the caller's brace placement; when glued it shares the final
425
+ // line, so it is charged against the width budget for the tier decision.
426
+ func multiTypeHeader(prefix string, clause heritageClauseText, layout declarationHeaderLayout, base, brace string, emptyBody bool) string {
427
+ indent1 := layout.indent(base, 1)
428
+ inline := indent1 + clause.keyword + " " + strings.Join(clause.types, ", ")
429
+ inlineWidth := visualWidth(inline, layout.tabWidth)
430
+ if !strings.HasPrefix(brace, "\n") {
431
+ inlineWidth += visualWidth(brace, layout.tabWidth)
432
+ }
433
+ if emptyBody {
434
+ // An empty body renders its `}` on the inline line (`… {}`), one column
435
+ // past the `{` in `brace`; charge it so the fit check matches Prettier at
436
+ // the width boundary instead of keeping a 1-over header inline.
437
+ inlineWidth++
438
+ }
439
+ if inlineWidth <= layout.printWidth {
440
+ return prefix + "\n" + inline + brace
441
+ }
442
+
443
+ var b strings.Builder
444
+ b.WriteString(prefix)
445
+ b.WriteString("\n")
446
+ b.WriteString(indent1)
447
+ b.WriteString(clause.keyword)
448
+ for i, t := range clause.types {
449
+ b.WriteString("\n")
450
+ b.WriteString(layout.indent(base, 2))
451
+ b.WriteString(t)
452
+ if i < len(clause.types)-1 {
453
+ b.WriteString(",")
454
+ } else {
455
+ b.WriteString(brace)
456
+ }
457
+ }
458
+ return b.String()
459
+ }
460
+
461
+ // singleGenericHeritageHeader handles the Prettier-3 shape for a lone
462
+ // heritage clause whose single type is generic with two or more type
463
+ // arguments: instead of breaking before the keyword, Prettier keeps
464
+ // `<keyword> <TypeName><` on the first line and breaks the type-argument
465
+ // list one per line, with `>` back at the declaration's base indent and
466
+ // the brace glued:
467
+ //
468
+ // export class C implements Serializer<
469
+ // any,
470
+ // KafkaRequest | Promise<KafkaRequest>
471
+ // > {
472
+ //
473
+ // The list takes no trailing comma (Prettier omits it for heritage type
474
+ // arguments) and the brace stays glued even for a class with a non-empty
475
+ // body. Returns ok=false for any shape it has not verified, type
476
+ // parameters present, more than one clause or type, a non-generic type, a
477
+ // single type argument (Prettier leaves `extends Base<OneArg>` inline even
478
+ // when it overflows), or a multi-line argument, so the caller falls back
479
+ // to the clause-breaking strategies or abstains.
480
+ //
481
+ // On a second pass the rewritten type spans multiple lines, so
482
+ // heritageClauseInfos abstains before this point and the broken form is a
483
+ // stable fixed point.
484
+ func singleGenericHeritageHeader(src, base, prefix string, typeParams, heritage *shimast.NodeList, layout declarationHeaderLayout) (string, bool) {
485
+ if typeParams != nil && len(typeParams.Nodes) > 0 {
486
+ return "", false
487
+ }
488
+ if heritage == nil || len(heritage.Nodes) != 1 || heritage.Nodes[0] == nil {
489
+ return "", false
490
+ }
491
+ clause := heritage.Nodes[0].AsHeritageClause()
492
+ if clause == nil || clause.Types == nil || len(clause.Types.Nodes) != 1 {
493
+ return "", false
494
+ }
495
+ typeNode := clause.Types.Nodes[0]
496
+ if typeNode == nil {
497
+ return "", false
498
+ }
499
+ ewta := typeNode.AsExpressionWithTypeArguments()
500
+ if ewta == nil || ewta.TypeArguments == nil || len(ewta.TypeArguments.Nodes) < 2 {
501
+ return "", false
502
+ }
503
+ nameStart := shimscanner.SkipTrivia(src, typeNode.Pos())
504
+ ltPos := typeArgsStart(src, ewta.TypeArguments)
505
+ if nameStart < 0 || ltPos <= nameStart {
506
+ return "", false
507
+ }
508
+ typeName := strings.TrimRight(src[nameStart:ltPos], " \t")
509
+ if strings.ContainsRune(typeName, '\n') {
510
+ return "", false
511
+ }
512
+ args, ok := nodeListTexts(src, ewta.TypeArguments)
513
+ if !ok {
514
+ return "", false
515
+ }
516
+ keyword := "implements"
517
+ if clause.Token == shimast.KindExtendsKeyword {
518
+ keyword = "extends"
519
+ }
520
+
521
+ var b strings.Builder
522
+ b.WriteString(prefix)
523
+ b.WriteString(" ")
524
+ b.WriteString(keyword)
525
+ b.WriteString(" ")
526
+ b.WriteString(typeName)
527
+ b.WriteString("<\n")
528
+ for i, a := range args {
529
+ b.WriteString(layout.indent(base, 1))
530
+ b.WriteString(a)
531
+ if i < len(args)-1 {
532
+ b.WriteString(",")
533
+ }
534
+ b.WriteString("\n")
535
+ }
536
+ b.WriteString(base)
537
+ b.WriteString("> {")
538
+ return b.String(), true
539
+ }
540
+
541
+ // headerBrace returns the opening-brace fragment that closes a broken
542
+ // header. Prettier puts `{` on its own line only for a class with a
543
+ // non-empty body; an interface and any empty body keep it glued to the
544
+ // last header line.
545
+ func headerBrace(isClass, emptyBody bool, base string) string {
546
+ if isClass && !emptyBody {
547
+ return "\n" + base + "{"
548
+ }
549
+ return " {"
550
+ }
551
+
552
+ // headerBodyIsEmpty reports whether the body opened at bracePos holds no
553
+ // members, its first non-whitespace byte is the closing `}`. A body with
554
+ // a comment counts as non-empty (conservative: the comment keeps the
555
+ // brace where the source had it rather than forcing a glue).
556
+ func headerBodyIsEmpty(src string, bracePos int) bool {
557
+ for i := bracePos + 1; i < len(src); i++ {
558
+ switch src[i] {
559
+ case ' ', '\t', '\r', '\n':
560
+ continue
561
+ case '}':
562
+ return true
563
+ default:
564
+ return false
565
+ }
566
+ }
567
+ return false
568
+ }
569
+
570
+ // typeParamExplodeHeader: explode the `<...>` list, heritage inline after `>`.
571
+ func typeParamExplodeHeader(
572
+ src string,
573
+ base string,
574
+ prefix string,
575
+ typeParams *shimast.NodeList,
576
+ clauses []heritageClauseText,
577
+ layout declarationHeaderLayout,
578
+ isClass bool,
579
+ ) string {
580
+ var b strings.Builder
581
+ b.WriteString(prefix)
582
+ b.WriteString("<\n")
583
+ paramIndent := layout.indent(base, 1)
584
+ for _, p := range typeParams.Nodes {
585
+ b.WriteString(renderExplodedTypeParam(src, p, layout, paramIndent))
586
+ b.WriteString(",\n")
587
+ }
588
+ b.WriteString(base)
589
+ b.WriteString(">")
590
+ if interfaceHeritageOnOwnLine(isClass, clauses) {
591
+ // Interface with a qualified heritage type: Prettier breaks before the
592
+ // `extends`, indented one level past the `>`:
593
+ //
594
+ // >
595
+ // extends IPage.IRequest {
596
+ c := clauses[0]
597
+ b.WriteString("\n")
598
+ b.WriteString(layout.indent(base, 1))
599
+ b.WriteString(c.keyword)
600
+ b.WriteString(" ")
601
+ b.WriteString(c.types[0])
602
+ } else {
603
+ b.WriteString(flatHeritage(clauses))
604
+ }
605
+ b.WriteString(" {")
606
+ return b.String()
607
+ }
608
+
609
+ // renderExplodedTypeParam renders one type parameter on its indented
610
+ // line, breaking after `=` when `name extends C = Default` overflows.
611
+ func renderExplodedTypeParam(src string, p *shimast.Node, layout declarationHeaderLayout, paramIndent string) string {
612
+ start := shimscanner.SkipTrivia(src, p.Pos())
613
+ end := p.End()
614
+ if start < 0 || end < start || end > len(src) {
615
+ return paramIndent
616
+ }
617
+ full := strings.TrimRight(src[start:end], " \t")
618
+ decl := p.AsTypeParameterDeclaration()
619
+ // Break after `=` only when the flat parameter line overflows and the
620
+ // parameter has a default; Prettier hangs the default one level deeper.
621
+ if decl != nil && decl.DefaultType != nil {
622
+ flatWidth := visualWidth(paramIndent, layout.tabWidth) + visualWidth(full, layout.tabWidth) + 1
623
+ if flatWidth > layout.printWidth {
624
+ defStart := shimscanner.SkipTrivia(src, decl.DefaultType.Pos())
625
+ // Guard the slice: defStart must sit strictly inside the parameter
626
+ // (between its name and end) for the head/default split to be sound.
627
+ if defStart > start && defStart < end {
628
+ head := strings.TrimRight(src[start:defStart], " \t") // ends in `=`
629
+ def := strings.TrimSpace(src[defStart:end])
630
+ if head != "" && def != "" {
631
+ return paramIndent + head + "\n" + paramIndent + layout.oneLevel + def
632
+ }
633
+ }
634
+ }
635
+ }
636
+ return paramIndent + full
637
+ }
638
+
639
+ // lineFirstNonSpace returns the offset of the first non-space, non-tab
640
+ // byte on the line containing `pos`.
641
+ func lineFirstNonSpace(src string, pos int) int {
642
+ ls := lineStartOffset(src, pos)
643
+ i := ls
644
+ for i < len(src) && (src[i] == ' ' || src[i] == '\t') {
645
+ i++
646
+ }
647
+ return i
648
+ }
649
+
650
+ // containsComment reports whether `s` holds a line or block comment.
651
+ func containsComment(s string) bool {
652
+ return strings.Contains(s, "//") || strings.Contains(s, "/*")
653
+ }
654
+
655
+ func maxInt(a, b int) int {
656
+ if a > b {
657
+ return a
658
+ }
659
+ return b
660
+ }
661
+
662
+ func init() {
663
+ Register(formatDeclarationHeader{})
664
+ }