@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
@@ -113,6 +113,16 @@ func printParenthesizedExpression(ctx *PrintContext, node *shimast.Node) (Doc, b
113
113
  if paren == nil || paren.Expression == nil {
114
114
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
115
115
  }
116
+ // A comment around the parens (`(/* c */ x)`, `(x /* c */)`) would be dropped
117
+ // by the minted `(`/`)` — the parens are not AST children, so a nested
118
+ // ParenthesizedExpression's gap comment slips past the top-level print-width
119
+ // scan and is lost on reflow. Bail to verbatim and report UNCOVERED (hard
120
+ // `false`, not `!nodeSpansMultipleLines`) so an enclosing reflow abstains
121
+ // instead of breaking around this single-line verbatim paren, like the
122
+ // object/array/call printers.
123
+ if listHasInterItemComments(ctx, node) {
124
+ return verbatim(ctx, node), false
125
+ }
116
126
  inner, covered := PrintNode(ctx, paren.Expression)
117
127
  return Concat(Text("("), inner, Text(")")), covered
118
128
  }
@@ -180,6 +190,16 @@ func printBlock(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
180
190
  bodyParts := make([]Doc, 0, len(items)*2)
181
191
  for i, item := range items {
182
192
  if i > 0 {
193
+ // A lone leading-semicolon ASI guard stays glued to the statement
194
+ // it protects (`;(expr)`): when the previous statement is an empty
195
+ // `;` sitting directly against this one in the source, emit no line
196
+ // break, so the reflow agrees with format/orphan-semi instead of
197
+ // ping-ponging it back onto two lines every cascade pass.
198
+ if stmts[i-1].Kind == shimast.KindEmptyStatement &&
199
+ !rangeHasNewline(ctx.Source, stmts[i-1].End(), shimscanner.SkipTrivia(ctx.Source, stmts[i].Pos())) {
200
+ bodyParts = append(bodyParts, item)
201
+ continue
202
+ }
183
203
  if blankLineBetweenStatements(ctx.Source, stmts[i-1].End(), stmts[i].Pos()) {
184
204
  bodyParts = append(bodyParts, Literalline())
185
205
  }
@@ -196,6 +216,21 @@ func printBlock(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
196
216
  return doc, covered
197
217
  }
198
218
 
219
+ // rangeHasNewline reports whether src[start:end) contains a newline. Used
220
+ // to detect whether a leading-semicolon guard sits on the same line as
221
+ // the statement it protects.
222
+ func rangeHasNewline(src string, start, end int) bool {
223
+ if start < 0 || end > len(src) || end <= start {
224
+ return false
225
+ }
226
+ for i := start; i < end; i++ {
227
+ if src[i] == '\n' {
228
+ return true
229
+ }
230
+ }
231
+ return false
232
+ }
233
+
199
234
  // blankLineBetweenStatements reports whether the source gap between the
200
235
  // end of one block statement and the start of the next contains a blank
201
236
  // line — two or more newlines. printBlock uses it to keep a single
@@ -319,6 +354,14 @@ func printReturnStatement(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
319
354
  if !tailIsCleanTerminator(ctx.Source, stmt.Expression.End(), node.End()) {
320
355
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
321
356
  }
357
+ // A comment between `return` and its argument (`return /* c */ x`) lives in
358
+ // the argument's leading trivia and would be dropped by the minted
359
+ // `Text("return ")` — the gap is not an AST child, and a return statement is
360
+ // only reached as a nested child of a block, so the outer print-width and
361
+ // block scans mask it. Bail to verbatim (uncovered) like the round-7 printers.
362
+ if gapHasComment(ctx.Source, stmt.Expression.Pos(), shimscanner.SkipTrivia(ctx.Source, stmt.Expression.Pos())) {
363
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
364
+ }
322
365
  exprDoc, covered := PrintNode(ctx, stmt.Expression)
323
366
  parts := []Doc{Text("return "), exprDoc}
324
367
  if sourceHasStatementTerminator(ctx.Source, node.End()) {
@@ -2,6 +2,7 @@ package linthost
2
2
 
3
3
  import (
4
4
  shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
5
6
  )
6
7
 
7
8
  // printNamedImports renders `{ a, b, c }` inside an import declaration.
@@ -115,39 +116,88 @@ func printImportDeclaration(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
115
116
  if clauseData == nil || clauseData.NamedBindings == nil {
116
117
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
117
118
  }
118
- if clauseData.NamedBindings.Kind != shimast.KindNamedImports {
119
+ nb := clauseData.NamedBindings
120
+ if nb.Kind != shimast.KindNamedImports {
119
121
  // Namespace imports (`import * as ns from "x"`) have no
120
122
  // reflow surface; leave them alone.
121
123
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
122
124
  }
123
- if clause.Name() != nil {
124
- // `import Default, { … } from "x"` — keep verbatim for v1.
125
- return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
126
- }
127
125
  // AttributeClause (`with { ... }` / `assert { ... }`) lives after
128
126
  // the module specifier. Fall back to verbatim when present so we
129
127
  // don't drop attributes silently.
130
128
  if imp.Attributes != nil {
131
129
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
132
130
  }
131
+ // A comment between specifiers would be dropped by the fresh separators.
132
+ if listHasInterItemComments(ctx, nb) {
133
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
134
+ }
135
+ ni := nb.AsNamedImports()
136
+ if ni == nil || ni.Elements == nil {
137
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
138
+ }
133
139
 
134
- // Bracketed clause prefix: `import ` (and optional `type `).
140
+ // Bracketed clause prefix: `import `, `import type `, or — when a default
141
+ // binding precedes the named clause — `import Default, `. A default never
142
+ // combines with `type` (TS forbids `import type D, { … }`), so the two
143
+ // prefix forms are exclusive.
135
144
  prefix := "import "
136
145
  if clause.IsTypeOnly() {
146
+ // A comment in the `type`->`{` gap (`import type /* c */ { … }`) would be
147
+ // dropped by the minted prefix. The `type` keyword is a clause modifier
148
+ // flag, not a child node, so the top-level scan masks it (it sits inside the
149
+ // ImportClause span) and the `nb`-scoped guard starts at `{`. Bail to
150
+ // verbatim, like the default-binding gap below.
151
+ if gapHasComment(ctx.Source, shimscanner.SkipTrivia(ctx.Source, clause.Pos()), shimscanner.SkipTrivia(ctx.Source, nb.Pos())) {
152
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
153
+ }
137
154
  prefix = "import type "
138
155
  }
139
- named, covered := PrintNode(ctx, clauseData.NamedBindings)
156
+ if name := clause.Name(); name != nil {
157
+ // A comment in the default-binding-to-brace gap (`import D /* c */, { … }`)
158
+ // would be dropped by the minted `, ` prefix. That gap is not a direct child
159
+ // of the import node, so the top-level scan masks it and the `nb`-scoped
160
+ // guard above starts at `{` — same class as printReturnStatement's leading
161
+ // gap. Bail to verbatim.
162
+ if gapHasComment(ctx.Source, name.End(), shimscanner.SkipTrivia(ctx.Source, nb.Pos())) {
163
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
164
+ }
165
+ prefix = "import " + identifierText(name) + ", "
166
+ }
167
+
168
+ items := make([]Doc, 0, len(ni.Elements.Nodes))
169
+ covered := true
170
+ for _, spec := range ni.Elements.Nodes {
171
+ if spec == nil {
172
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
173
+ }
174
+ doc, childCovered := PrintNode(ctx, spec)
175
+ covered = covered && childCovered
176
+ items = append(items, doc)
177
+ }
178
+
140
179
  moduleSpec := verbatim(ctx, imp.ModuleSpecifier)
141
180
  covered = covered && !nodeSpansMultipleLines(ctx, imp.ModuleSpecifier)
142
- parts := []Doc{Text(prefix), named, Text(" from "), moduleSpec}
181
+ suffixParts := []Doc{Text(" from "), moduleSpec}
143
182
  if sourceHasStatementTerminator(ctx.Source, node.End()) {
144
- // Preserve the user's terminator decision. Emitting `;`
145
- // unconditionally would collide with `format/semi`'s
146
- // zero-width insert on the same cascade pass and produce
147
- // `;;` — `format/semi` owns terminator placement.
148
- parts = append(parts, Text(";"))
183
+ // Preserve the user's terminator decision. Emitting `;` unconditionally
184
+ // would collide with `format/semi`'s zero-width insert and produce `;;`.
185
+ suffixParts = append(suffixParts, Text(";"))
149
186
  }
150
- return Concat(parts...), covered
187
+
188
+ // The whole declaration renders as one Group via Prefix/Suffix, so the
189
+ // named-brace break decision counts the ` from "..."` tail: a brace that
190
+ // fits but whose declaration line overflows still breaks, and a
191
+ // default-combined import breaks its brace too.
192
+ return printList(ctx, listShape{
193
+ Prefix: Text(prefix),
194
+ OpenTok: "{",
195
+ CloseTok: "}",
196
+ Items: items,
197
+ Space: true,
198
+ AddComma: ctx.allowsEs5TrailingComma(),
199
+ Suffix: Concat(suffixParts...),
200
+ }), covered
151
201
  }
152
202
 
153
203
  // sourceHasStatementTerminator reports whether the last non-trivia
@@ -1,5 +1,30 @@
1
1
  package linthost
2
2
 
3
+ import (
4
+ shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
6
+ )
7
+
8
+ // listHasInterItemComments reports whether `node` (an object / array literal)
9
+ // carries a comment in a gap between its items or brackets. The list printers
10
+ // mint fresh comma/line separators that have no slot for such trivia, so a
11
+ // reflow would silently delete the comment. When a node nested inside a
12
+ // reflowing parent (an array argument of a call) carries one, the parent's
13
+ // own top-level comment guard cannot see it (it lives inside a child range),
14
+ // so each list printer must check itself and bail to verbatim — reporting the
15
+ // span uncovered so the enclosing reflow abstains and the source round-trips.
16
+ func listHasInterItemComments(ctx *PrintContext, node *shimast.Node) bool {
17
+ if node == nil {
18
+ return false
19
+ }
20
+ start := shimscanner.SkipTrivia(ctx.Source, node.Pos())
21
+ end := node.End()
22
+ if start < 0 || end < start || end > len(ctx.Source) {
23
+ return false
24
+ }
25
+ return hasNonChildComments(node, ctx.Source, start, end)
26
+ }
27
+
3
28
  // Shared helpers for comma-separated list printers.
4
29
  //
5
30
  // Object literals, array literals, call arguments, parameter lists,
@@ -35,11 +60,87 @@ type listShape struct {
35
60
  // multi-line callback body does not force every preceding argument
36
61
  // onto its own line.
37
62
  HugLast bool
63
+ // HugFirst keeps the FIRST item attached to the open paren and flows
64
+ // the remaining simple items after it, the mirror of HugLast. The
65
+ // call-argument printer sets it for the two-argument
66
+ // `callback, simpleArg` shape Prettier hugs (`foo(() => { … }, x)`).
67
+ // HugLast and HugFirst are mutually exclusive.
68
+ HugFirst bool
69
+ // HugFirstTrailingBreaks tells printListHuggingFirst NOT to flatten the
70
+ // trailing item, letting it render through its own breakable group. The
71
+ // call-argument printer sets it for the React-hook deps shape
72
+ // (`useEffect(() => { … }, [deps])`), whose deps array Prettier breaks
73
+ // one-element-per-line when it overflows rather than pinning it flat on the
74
+ // close line. The short leaf/call trailing args of every other first-arg-hug
75
+ // case still flatten (they ride the close line flat, per Prettier).
76
+ HugFirstTrailingBreaks bool
77
+ // HugFirstForce drops the exploded fallback from a HugFirst list, the mirror
78
+ // of HugLastForce. The call-argument printer sets it for a genuine React-hook
79
+ // deps call (`useEffect(() => { … }, [deps])`: a zero-parameter block-bodied
80
+ // arrow plus an array literal), which Prettier's isReactHookCallWithDepsArray
81
+ // path never explodes — it keeps the callback hugged and lets the open line
82
+ // overflow. Distinct from HugFirstTrailingBreaks, which also fires for a
83
+ // parameterized callback whose empty-array second arg DOES keep the fallback.
84
+ HugFirstForce bool
85
+ // HugLastForce drops the exploded fallback from a HugLast list: the hugged
86
+ // shape is chosen even when its opening line overflows printWidth. The
87
+ // call-argument printer sets it for a test-framework call
88
+ // (`test("very long description", () => { … })`), which Prettier never
89
+ // explodes — the callback always rides the open paren. Requires HugLast.
90
+ HugLastForce bool
38
91
  // ForceBreak commits the list to its broken, one-item-per-line shape
39
92
  // even when it would fit flat. The object-literal printer sets it to
40
93
  // mirror Prettier's objectWrap:"preserve" — an object the source
41
94
  // wrote with a newline after `{` stays expanded.
42
95
  ForceBreak bool
96
+ // Fill packs the items with Prettier's fill layout — as many per line as
97
+ // fit — instead of one item per line when the list breaks. The array
98
+ // printer sets it for a concisely-printed numeric array. Mutually
99
+ // exclusive with HugLast / HugFirst.
100
+ Fill bool
101
+ // Prefix and Suffix are emitted inside the list's Group, before OpEN and
102
+ // after CLOSE respectively. They let a caller fold surrounding tokens into
103
+ // the same fit-or-break decision: an import declaration sets Prefix to
104
+ // `import ` (or `import D, `) and Suffix to ` from "..."` so the named-brace
105
+ // group's flat-fit check counts the whole declaration line — Prettier never
106
+ // breaks a brace that fits but whose `from` tail overflows is measured here,
107
+ // not in a separate group. Only used on the plain (non-hugging) path.
108
+ Prefix Doc
109
+ Suffix Doc
110
+ // BlankBefore[i] reports whether the source had a blank line before item i
111
+ // (i.e. between item i-1 and item i). When any entry is set the list is
112
+ // forced broken and a single blank line is preserved before that item,
113
+ // mirroring Prettier. Length, when non-nil, equals len(Items); index 0 is
114
+ // ignored (no item precedes the first).
115
+ BlankBefore []bool
116
+ }
117
+
118
+ // blankBeforeItems computes BlankBefore for a node list: entry i is true when
119
+ // the source had a blank line (two or more newlines) between item i-1 and
120
+ // item i. Returns nil for a list too short to carry one, so callers pass it
121
+ // through unconditionally.
122
+ func blankBeforeItems(src string, elems []*shimast.Node) []bool {
123
+ if len(elems) < 2 {
124
+ return nil
125
+ }
126
+ out := make([]bool, len(elems))
127
+ for i := 1; i < len(elems); i++ {
128
+ if elems[i-1] == nil || elems[i] == nil {
129
+ continue
130
+ }
131
+ out[i] = blankLineBetweenStatements(src, elems[i-1].End(), elems[i].Pos())
132
+ }
133
+ return out
134
+ }
135
+
136
+ // hasBlankBefore reports whether any item carries a preserved blank line.
137
+ func (s listShape) hasBlankBefore() bool {
138
+ for _, b := range s.BlankBefore {
139
+ if b {
140
+ return true
141
+ }
142
+ }
143
+ return false
43
144
  }
44
145
 
45
146
  // printList renders the list shape as a Doc tree. Empty lists collapse
@@ -50,19 +151,35 @@ func printList(ctx *PrintContext, shape listShape) Doc {
50
151
  return Text(shape.OpenTok + shape.CloseTok)
51
152
  }
52
153
  plain := printListPlain(ctx, shape)
53
- if !shape.HugLast {
154
+ // A source blank line forces the plain broken layout (Prettier never hugs or
155
+ // packs flat across a blank line), so skip the hugging ConditionalGroup.
156
+ if (!shape.HugLast && !shape.HugFirst) || shape.hasBlankBefore() {
54
157
  return plain
55
158
  }
56
- // A HugLast list offers the engine up to three shapes, in preference
159
+ // A hugging list offers the engine up to three shapes, in preference
57
160
  // order:
58
161
  // 1. allFlat — every item on one line, chosen when it fits;
59
- // 2. hugged — leading items inline, the final callback or object
60
- // committed to its multi-line shape, chosen when its opening
61
- // line fits but the all-flat form does not;
162
+ // 2. hugged — the callback (last for HugLast, first for HugFirst)
163
+ // committed to its multi-line shape with the other items inline,
164
+ // chosen when its opening line fits but the all-flat form does not;
62
165
  // 3. plain — every item exploded onto its own indented line.
63
166
  // The all-flat option is dropped when the list cannot render flat
64
167
  // (a block-bodied callback argument carries hard line breaks).
65
168
  hugged := printListHuggingLast(ctx, shape)
169
+ if shape.HugFirst {
170
+ hugged = printListHuggingFirst(ctx, shape)
171
+ }
172
+ // A test-framework call (HugLastForce) and a genuine React-hook deps call
173
+ // (HugFirstForce, `useEffect(() => { … }, [deps])`) hug their callback
174
+ // unconditionally — Prettier never explodes their arguments, it lets the open
175
+ // line overflow — so drop the exploded fallback. The all-flat option still
176
+ // wins when the whole call fits (an empty-body callback).
177
+ if shape.HugLastForce || shape.HugFirstForce {
178
+ if allFlat, ok := flatten(plain); ok {
179
+ return ConditionalGroup(allFlat, hugged)
180
+ }
181
+ return hugged
182
+ }
66
183
  if allFlat, ok := flatten(plain); ok {
67
184
  return ConditionalGroup(allFlat, hugged, plain)
68
185
  }
@@ -75,7 +192,6 @@ func printList(ctx *PrintContext, shape listShape) Doc {
75
192
  // comma — when it does not.
76
193
  func printListPlain(ctx *PrintContext, shape listShape) Doc {
77
194
  sep := Concat(Text(","), Line())
78
- body := Join(sep, shape.Items)
79
195
 
80
196
  flatPad := Doc{Kind: docNil}
81
197
  if shape.Space {
@@ -87,6 +203,50 @@ func printListPlain(ctx *PrintContext, shape listShape) Doc {
87
203
  trailing = IfBreak(Text(","), Doc{Kind: docNil})
88
204
  }
89
205
 
206
+ hasBlank := shape.hasBlankBefore()
207
+
208
+ var body Doc
209
+ if hasBlank {
210
+ // Per-item body that preserves a single source blank line before an item:
211
+ // `,` then a Literalline (the empty line, no indent, mirroring printBlock)
212
+ // then the normal Line. A blank line forces the group broken.
213
+ parts := make([]Doc, 0, len(shape.Items)*3)
214
+ for i, it := range shape.Items {
215
+ if i > 0 {
216
+ parts = append(parts, Text(","))
217
+ if i < len(shape.BlankBefore) && shape.BlankBefore[i] {
218
+ parts = append(parts, Literalline())
219
+ }
220
+ parts = append(parts, Line())
221
+ }
222
+ parts = append(parts, it)
223
+ }
224
+ body = Concat(parts...)
225
+ } else if shape.Fill {
226
+ // Each non-last fill content carries its own comma — `[el, ","]` — and the
227
+ // separator is just a Line. Prettier measures the pack/break decision on
228
+ // `[content_i, line, content_{i+1}]`, so the next element's comma must be
229
+ // part of its content or the line would pack one element too many. The
230
+ // last element carries no comma here; the trailing comma is the shared
231
+ // ifBreak appended after the body so it tracks the GROUP's break mode (a
232
+ // fill-mode ifBreak would drop the comma when the last item packs flat,
233
+ // making the format non-idempotent).
234
+ parts := make([]Doc, 0, len(shape.Items)*2-1)
235
+ for i, it := range shape.Items {
236
+ if i > 0 {
237
+ parts = append(parts, Line())
238
+ }
239
+ if i < len(shape.Items)-1 {
240
+ parts = append(parts, Concat(it, Text(",")))
241
+ } else {
242
+ parts = append(parts, it)
243
+ }
244
+ }
245
+ body = Fill(parts...)
246
+ } else {
247
+ body = Join(sep, shape.Items)
248
+ }
249
+
90
250
  openTok := Text(shape.OpenTok)
91
251
  closeTok := Text(shape.CloseTok)
92
252
 
@@ -96,11 +256,19 @@ func printListPlain(ctx *PrintContext, shape listShape) Doc {
96
256
  trailingSep := IfBreak(Hardline(), flatPad)
97
257
 
98
258
  bodyBlock := Indent(ctx.indentUnit(), leadingSep, body, trailing)
99
- doc := Concat(openTok, bodyBlock, trailingSep, closeTok)
259
+ prefix := shape.Prefix
260
+ if prefix.Kind == 0 {
261
+ prefix = Doc{Kind: docNil}
262
+ }
263
+ suffix := shape.Suffix
264
+ if suffix.Kind == 0 {
265
+ suffix = Doc{Kind: docNil}
266
+ }
267
+ doc := Concat(prefix, openTok, bodyBlock, trailingSep, closeTok, suffix)
100
268
  group := Group(doc)
101
269
  // ForceBreak (object-literal newline preservation) commits the group
102
270
  // to its broken shape regardless of fit.
103
- group.Break = shape.ForceBreak
271
+ group.Break = shape.ForceBreak || hasBlank
104
272
  return group
105
273
  }
106
274
 
@@ -143,3 +311,41 @@ func printListHuggingLast(ctx *PrintContext, shape listShape) Doc {
143
311
  parts = append(parts, last, Text(shape.CloseTok))
144
312
  return Concat(parts...)
145
313
  }
314
+
315
+ // printListHuggingFirst renders the "first-argument hugging" shape
316
+ // Prettier uses for `foo(() => { … }, target)`: the first item stays
317
+ // attached to the open paren and the remaining simple items flow inline
318
+ // after it, so a multi-line callback body does not explode the trailing
319
+ // arguments onto their own lines.
320
+ //
321
+ // hugged: OPEN OPEN-of-first … CLOSE-of-first, b, c CLOSE
322
+ //
323
+ // Like printListHuggingLast it returns a plain Concat (the callback's own
324
+ // printer owns its body's break) and forces the hugged item's first Group
325
+ // broken when the item has no hard line breaks of its own, so the hugged
326
+ // option stays distinct from the all-flat option.
327
+ func printListHuggingFirst(ctx *PrintContext, shape listShape) Doc {
328
+ first := shape.Items[0]
329
+ rest := shape.Items[1:]
330
+ if _, flat := flatten(first); flat {
331
+ first, _ = forceBreakFirstGroup(first)
332
+ }
333
+ parts := []Doc{Text(shape.OpenTok), first}
334
+ for _, item := range rest {
335
+ // The trailing simple arguments ride the close line flat — Prettier keeps
336
+ // them on one line even when that line overflows (a long zero/one-argument
337
+ // trailing call `}, makeAccumulator(single))` is not broken). Force each
338
+ // flat so its own Group does not break against the close-line width. The
339
+ // React-hook deps array is the exception (HugFirstTrailingBreaks): Prettier
340
+ // renders it through a breakable group, so leave it unflattened to break
341
+ // one-element-per-line when the close line overflows.
342
+ if !shape.HugFirstTrailingBreaks {
343
+ if flat, ok := flatten(item); ok {
344
+ item = flat
345
+ }
346
+ }
347
+ parts = append(parts, Text(", "), item)
348
+ }
349
+ parts = append(parts, Text(shape.CloseTok))
350
+ return Concat(parts...)
351
+ }
@@ -35,6 +35,13 @@ func printObjectLiteral(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
35
35
  if obj == nil || obj.Properties == nil {
36
36
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
37
37
  }
38
+ // A comment between properties (or after `{`) would be dropped by the fresh
39
+ // separators; bail to verbatim and report UNCOVERED (hard `false`, not
40
+ // `!nodeSpansMultipleLines`) so an enclosing reflow abstains instead of
41
+ // breaking around this single-line verbatim object and moving it off its line.
42
+ if listHasInterItemComments(ctx, node) {
43
+ return verbatim(ctx, node), false
44
+ }
38
45
  items := make([]Doc, 0, len(obj.Properties.Nodes))
39
46
  covered := true
40
47
  for _, prop := range obj.Properties.Nodes {
@@ -60,12 +67,13 @@ func printObjectLiteral(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
60
67
  // "none" suppresses. Pairs with the call/array branches so the printer
61
68
  // never disagrees with the trailing-comma rule on the same setting.
62
69
  return printList(ctx, listShape{
63
- OpenTok: "{",
64
- CloseTok: "}",
65
- Items: items,
66
- Space: true,
67
- AddComma: ctx.allowsEs5TrailingComma(),
68
- ForceBreak: forceBreak,
70
+ OpenTok: "{",
71
+ CloseTok: "}",
72
+ Items: items,
73
+ Space: true,
74
+ AddComma: ctx.allowsEs5TrailingComma(),
75
+ ForceBreak: forceBreak,
76
+ BlankBefore: blankBeforeItems(ctx.Source, obj.Properties.Nodes),
69
77
  }), covered
70
78
  }
71
79
 
@@ -0,0 +1,147 @@
1
+ package linthost
2
+
3
+ import (
4
+ shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ )
6
+
7
+ // printConditionalExpression renders a (possibly chained) ternary with
8
+ // Prettier 3's "indent nested ternaries" staircase. A conditional whose
9
+ // consequent or alternate is itself a conditional is printed inline
10
+ // under the same break group, but its arms indent one extra level:
11
+ //
12
+ // aaaaaaaaaa
13
+ // ? bbbbbbbbbb
14
+ // : cccccccccc
15
+ // ? dddddddddd
16
+ // : eeeeeeeeee
17
+ //
18
+ // The whole chain shares one fit-or-break decision (a single outer
19
+ // Group), matching Prettier: either the entire chain fits flat
20
+ // (`a ? b : c ? d : e`) or every rung breaks onto its own line. Nesting
21
+ // is expressed by recursing the chain builder without wrapping the
22
+ // nested conditional in its own Group. The outermost chain indents its arms
23
+ // by `tabWidth`. A nested chain in the ALTERNATE (`: `) position indents by a
24
+ // fixed 2 columns; a nested chain in the CONSEQUENT (`? `) position indents by
25
+ // `max(2, tabWidth)` (Prettier's extra `align(tabWidth-2)` on the consequent).
26
+ // So at tabWidth 4 the outer arms sit at column 4, an alternate-nested arm at
27
+ // column 6, and a consequent-nested arm at column 8; they coincide at tabWidth 2.
28
+ //
29
+ // The second return value is the coverage flag (see PrintNode): the AND
30
+ // of the test and both branches, so a multi-line verbatim node anywhere
31
+ // in the chain makes formatPrintWidth abstain rather than emit a
32
+ // half-reflowed shape.
33
+ func printConditionalExpression(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
34
+ body, covered := buildConditionalChain(ctx, node, ctx.indentUnit())
35
+ if body.IsNil() {
36
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
37
+ }
38
+ return Group(body), covered
39
+ }
40
+
41
+ // buildConditionalChain returns the chain body for `node` WITHOUT a
42
+ // surrounding Group, so a nested conditional recursed from here shares
43
+ // the caller's group and its arms indent one level deeper. The top-level
44
+ // printConditionalExpression wraps the outermost body in the single
45
+ // Group that owns the break decision.
46
+ func buildConditionalChain(ctx *PrintContext, node *shimast.Node, indentCols int) (Doc, bool) {
47
+ cond := node.AsConditionalExpression()
48
+ if cond == nil || cond.Condition == nil || cond.WhenTrue == nil || cond.WhenFalse == nil {
49
+ return Doc{}, true
50
+ }
51
+ // A comment around the `?`/`:` markers (`a ? /* c */ b : c`) would be dropped
52
+ // by the minted "? "/": " text — the markers are not AST children, so a
53
+ // nested conditional's gap comment slips past the top-level print-width scan
54
+ // and is lost on reflow. Bail to verbatim and report UNCOVERED (hard `false`,
55
+ // not `!nodeSpansMultipleLines`) so an enclosing reflow abstains instead of
56
+ // breaking around this single-line verbatim conditional and moving it off its
57
+ // line; the bytes survive.
58
+ if listHasInterItemComments(ctx, node) {
59
+ return verbatim(ctx, node), false
60
+ }
61
+ testDoc, c1 := PrintNode(ctx, cond.Condition)
62
+ consDoc, c2 := ternaryArm(ctx, cond.WhenTrue, true)
63
+ altDoc, c3 := ternaryArm(ctx, cond.WhenFalse, false)
64
+ doc := Concat(
65
+ testDoc,
66
+ Indent(indentCols,
67
+ Line(), Text("? "), consDoc,
68
+ Line(), Text(": "), altDoc,
69
+ ),
70
+ )
71
+ return doc, c1 && c2 && c3
72
+ }
73
+
74
+ // ternaryArm prints a ternary consequent/alternate. A nested conditional
75
+ // continues the chain inline (no new Group) so the staircase
76
+ // accumulates; every other arm prints through the normal dispatcher (an
77
+ // arm that is a `??` expression is already parenthesized in valid
78
+ // source, so it round-trips its parens via the ParenthesizedExpression
79
+ // printer).
80
+ //
81
+ // A non-conditional arm is wrapped in Align so that, when it breaks
82
+ // internally, its continuation hangs under the arm expression's own
83
+ // column (one level past the `? `/`: ` marker) rather than under the
84
+ // chain's rung indent. Prettier hangs a broken call/object arm there:
85
+ //
86
+ // cond
87
+ // ? foo(
88
+ // arg, // arm-column + one level, not rung + one level
89
+ // )
90
+ // : bar
91
+ //
92
+ // Align is a no-op for an arm that stays flat, and a nested conditional
93
+ // is deliberately NOT aligned — it keeps the Indent-based staircase.
94
+ //
95
+ // A nested conditional in the CONSEQUENT (`? `) position is wrapped in
96
+ // parentheses, but only when the chain renders flat: Prettier's ternary-old
97
+ // printer emits `consequent.type === node.type ? ifBreak("", "(") : ""` around
98
+ // the consequent (`a ? (b ? c : d) : e` on one line), and drops the parens for
99
+ // the broken staircase. A nested conditional in the ALTERNATE (`: `) position
100
+ // is never wrapped — it chains. `isConsequent` selects between the two.
101
+ func ternaryArm(ctx *PrintContext, node *shimast.Node, isConsequent bool) (Doc, bool) {
102
+ inner := node
103
+ // A nested ternary written with explicit source parentheses — `a ? (b ? c : d)
104
+ // : e` — is the same chain link as a bare nested ternary: Prettier's AST has no
105
+ // ParenthesizedExpression node, so its `printTernary` sees the consequent as a
106
+ // ConditionalExpression and joins the staircase (re-adding the parens only in
107
+ // flat mode via ifBreak). Unwrap a parenthesized conditional so it chains too,
108
+ // instead of printing flat inside kept parens. Only a ConditionalExpression
109
+ // inner is unwrapped; `(a ?? b)` and other parenthesized expressions keep their
110
+ // parens through the normal printer.
111
+ // Do NOT unwrap when a comment sits between the parens and the inner
112
+ // conditional (`(/* keep */ b ? c : d)`): dropping the ParenExpr wrapper would
113
+ // delete that comment. Leaving `inner` as the ParenExpr routes it through the
114
+ // normal printer, whose own self-guard bails to verbatim and preserves it.
115
+ if inner != nil && inner.Kind == shimast.KindParenthesizedExpression &&
116
+ !listHasInterItemComments(ctx, inner) {
117
+ if p := inner.AsParenthesizedExpression(); p != nil && p.Expression != nil &&
118
+ p.Expression.Kind == shimast.KindConditionalExpression {
119
+ inner = p.Expression
120
+ }
121
+ }
122
+ if inner != nil && inner.Kind == shimast.KindConditionalExpression {
123
+ // A nested chain's arms align at 2 past their parent rung. Prettier's
124
+ // ternary-old.js uses that as-is for an ALTERNATE-position nested chain
125
+ // (`align(2)`), but a CONSEQUENT-position one gets an extra
126
+ // `align(Math.max(0, tabWidth - 2))`, for a total of `max(2, tabWidth)`.
127
+ // The two coincide at the default tabWidth 2; they diverge at tabWidth > 2.
128
+ indentCols := 2
129
+ if isConsequent {
130
+ if u := ctx.indentUnit(); u > indentCols {
131
+ indentCols = u
132
+ }
133
+ }
134
+ chain, covered := buildConditionalChain(ctx, inner, indentCols)
135
+ if isConsequent {
136
+ // `ifBreak("", "(")` … `ifBreak("", ")")`: parens in flat mode only.
137
+ chain = Concat(
138
+ IfBreak(Doc{Kind: docNil}, Text("(")),
139
+ chain,
140
+ IfBreak(Doc{Kind: docNil}, Text(")")),
141
+ )
142
+ }
143
+ return chain, covered
144
+ }
145
+ doc, covered := PrintNode(ctx, node)
146
+ return Align(doc), covered
147
+ }