@ttsc/lint 0.14.0-dev.20260529.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +21 -23
  2. package/lib/defaultFormat.d.ts +10 -6
  3. package/lib/defaultFormat.js +10 -6
  4. package/lib/defaultFormat.js.map +1 -1
  5. package/lib/structures/ITtscLintConfig.d.ts +5 -5
  6. package/lib/structures/TtscLintRuleSetting.d.ts +19 -19
  7. package/lib/structures/{ITtscLintFormat.d.ts → format/ITtscLintFormat.d.ts} +45 -70
  8. package/lib/structures/format/ITtscLintFormat.js.map +1 -0
  9. package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +15 -0
  10. package/lib/structures/format/ITtscLintFormatJsDoc.js +3 -0
  11. package/lib/structures/format/ITtscLintFormatJsDoc.js.map +1 -0
  12. package/lib/structures/format/ITtscLintFormatSortImports.d.ts +54 -0
  13. package/lib/structures/format/ITtscLintFormatSortImports.js +3 -0
  14. package/lib/structures/format/ITtscLintFormatSortImports.js.map +1 -0
  15. package/lib/structures/format/index.d.ts +3 -0
  16. package/lib/structures/format/index.js +20 -0
  17. package/lib/structures/format/index.js.map +1 -0
  18. package/lib/structures/index.d.ts +1 -1
  19. package/lib/structures/index.js +1 -1
  20. package/lib/structures/index.js.map +1 -1
  21. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +18 -27
  22. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +4 -5
  23. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -1
  24. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +29 -33
  25. package/lib/structures/rules/ITtscLintContributorRules.d.ts +6 -7
  26. package/lib/structures/rules/ITtscLintCoreRules.d.ts +380 -408
  27. package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +5 -5
  28. package/lib/structures/rules/ITtscLintCypressRuleOptions.js +2 -2
  29. package/lib/structures/rules/ITtscLintCypressRules.d.ts +34 -38
  30. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +21 -24
  31. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +2 -2
  32. package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +51 -56
  33. package/lib/structures/rules/ITtscLintJestRules.d.ts +63 -74
  34. package/lib/structures/rules/ITtscLintJsdocRules.d.ts +47 -54
  35. package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +94 -102
  36. package/lib/structures/rules/ITtscLintNextjsRules.d.ts +50 -55
  37. package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +104 -122
  38. package/lib/structures/rules/ITtscLintPromiseRules.d.ts +62 -71
  39. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +4 -4
  40. package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +17 -19
  41. package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +8 -10
  42. package/lib/structures/rules/ITtscLintReactRuleOptions.js +1 -2
  43. package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -1
  44. package/lib/structures/rules/ITtscLintReactRules.d.ts +91 -104
  45. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +46 -54
  46. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +7 -8
  47. package/lib/structures/rules/ITtscLintSecurityRules.d.ts +45 -52
  48. package/lib/structures/rules/ITtscLintSolidRules.d.ts +53 -56
  49. package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +3 -4
  50. package/lib/structures/rules/ITtscLintStorybookRules.d.ts +60 -69
  51. package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +31 -37
  52. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +2 -3
  53. package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +102 -119
  54. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +460 -518
  55. package/lib/structures/rules/ITtscLintVitestRules.d.ts +43 -51
  56. package/linthost/config.go +89 -72
  57. package/linthost/config_format.go +265 -99
  58. package/linthost/directives.go +5 -1
  59. package/linthost/display_width.go +72 -0
  60. package/linthost/fix.go +26 -1
  61. package/linthost/format.go +222 -25
  62. package/linthost/format_editor_settings.go +262 -0
  63. package/linthost/lsp.go +8 -4
  64. package/linthost/print_dispatch.go +3 -0
  65. package/linthost/print_doc.go +13 -0
  66. package/linthost/print_engine.go +68 -11
  67. package/linthost/print_nodes_array.go +108 -5
  68. package/linthost/print_nodes_call.go +515 -74
  69. package/linthost/print_nodes_function.go +25 -0
  70. package/linthost/print_nodes_imports.go +47 -14
  71. package/linthost/print_nodes_list.go +191 -8
  72. package/linthost/print_nodes_object.go +12 -6
  73. package/linthost/print_nodes_ternary.go +106 -0
  74. package/linthost/rules_boundaries.go +400 -400
  75. package/linthost/rules_camelcase.go +81 -81
  76. package/linthost/rules_complexity.go +52 -51
  77. package/linthost/rules_consistent_return.go +81 -81
  78. package/linthost/rules_core_extra.go +472 -472
  79. package/linthost/rules_curly.go +43 -43
  80. package/linthost/rules_default_case.go +15 -15
  81. package/linthost/rules_default_case_last.go +22 -22
  82. package/linthost/rules_format_arrow_parens.go +167 -0
  83. package/linthost/rules_format_bracket_spacing.go +117 -0
  84. package/linthost/rules_format_clause_join.go +172 -0
  85. package/linthost/rules_format_declaration_header.go +664 -0
  86. package/linthost/rules_format_indent.go +616 -0
  87. package/linthost/rules_format_jsdoc.go +4 -4
  88. package/linthost/rules_format_orphan_semi.go +106 -0
  89. package/linthost/rules_format_parameter_properties.go +175 -0
  90. package/linthost/rules_format_print_width.go +185 -69
  91. package/linthost/rules_format_quote_props.go +169 -0
  92. package/linthost/rules_format_quotes.go +75 -29
  93. package/linthost/rules_format_semi.go +185 -10
  94. package/linthost/rules_format_sort_imports.go +456 -121
  95. package/linthost/rules_format_statement_split.go +368 -0
  96. package/linthost/rules_format_ternary_nullish_parens.go +77 -0
  97. package/linthost/rules_format_trailing_comma.go +21 -2
  98. package/linthost/rules_format_whitespace.go +336 -0
  99. package/linthost/rules_functional.go +688 -688
  100. package/linthost/rules_gap.go +146 -11
  101. package/linthost/rules_grouped_accessor_pairs.go +60 -60
  102. package/linthost/rules_guard_for_in.go +122 -122
  103. package/linthost/rules_id_length.go +44 -44
  104. package/linthost/rules_imports.go +11 -3
  105. package/linthost/rules_init_declarations.go +25 -25
  106. package/linthost/rules_jsx_a11y.go +844 -844
  107. package/linthost/rules_max_classes_per_file.go +24 -24
  108. package/linthost/rules_max_depth.go +43 -43
  109. package/linthost/rules_max_lines.go +53 -53
  110. package/linthost/rules_max_lines_per_function.go +28 -28
  111. package/linthost/rules_max_nested_callbacks.go +26 -26
  112. package/linthost/rules_max_params.go +20 -20
  113. package/linthost/rules_max_statements.go +29 -29
  114. package/linthost/rules_nextjs.go +5 -5
  115. package/linthost/rules_no_else_return.go +38 -38
  116. package/linthost/rules_no_empty_named_blocks.go +45 -45
  117. package/linthost/rules_no_import_assign.go +96 -96
  118. package/linthost/rules_no_invalid_this.go +19 -19
  119. package/linthost/rules_no_loop_func.go +43 -43
  120. package/linthost/rules_no_magic_numbers.go +60 -60
  121. package/linthost/rules_no_mixed_operators.go +44 -44
  122. package/linthost/rules_no_param_reassign.go +64 -64
  123. package/linthost/rules_no_restricted_imports.go +45 -45
  124. package/linthost/rules_no_restricted_syntax.go +17 -17
  125. package/linthost/rules_no_shadow.go +112 -112
  126. package/linthost/rules_no_unreachable.go +36 -36
  127. package/linthost/rules_no_useless_assignment.go +79 -79
  128. package/linthost/rules_no_useless_constructor.go +167 -167
  129. package/linthost/rules_no_useless_return.go +28 -28
  130. package/linthost/rules_prefer_arrow_callback.go +74 -74
  131. package/linthost/rules_prefer_destructuring.go +61 -61
  132. package/linthost/rules_prefer_named_capture_group.go +87 -87
  133. package/linthost/rules_prefer_rest_params.go +61 -61
  134. package/linthost/rules_promise.go +800 -800
  135. package/linthost/rules_react.go +600 -600
  136. package/linthost/rules_react_extras.go +162 -162
  137. package/linthost/rules_react_hooks.go +2 -2
  138. package/linthost/rules_react_perf.go +231 -231
  139. package/linthost/rules_regexp.go +293 -293
  140. package/linthost/rules_security.go +613 -613
  141. package/linthost/rules_sort_imports.go +37 -37
  142. package/linthost/rules_sort_keys.go +88 -88
  143. package/linthost/rules_suggestions.go +13 -0
  144. package/linthost/rules_ts.go +2 -2
  145. package/linthost/rules_ts_async.go +247 -247
  146. package/linthost/rules_ts_class_literal_property_style.go +64 -64
  147. package/linthost/rules_ts_consistent_type_exports.go +209 -209
  148. package/linthost/rules_ts_explicit_function_return_type.go +35 -35
  149. package/linthost/rules_ts_extra.go +6 -2
  150. package/linthost/rules_ts_no_base_to_string.go +140 -140
  151. package/linthost/rules_ts_no_confusing_void.go +28 -28
  152. package/linthost/rules_ts_no_deprecated.go +110 -110
  153. package/linthost/rules_ts_no_for_in_array.go +45 -45
  154. package/linthost/rules_ts_no_magic_numbers.go +67 -67
  155. package/linthost/rules_ts_no_meaningless_void_operator.go +44 -44
  156. package/linthost/rules_ts_no_misused_spread.go +64 -64
  157. package/linthost/rules_ts_no_redundant_type_constituents.go +72 -72
  158. package/linthost/rules_ts_no_restricted_types.go +28 -28
  159. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +87 -87
  160. package/linthost/rules_ts_no_unnecessary_condition.go +184 -184
  161. package/linthost/rules_ts_no_unnecessary_qualifier.go +72 -72
  162. package/linthost/rules_ts_no_unnecessary_template_expression.go +120 -120
  163. package/linthost/rules_ts_no_unnecessary_type_arguments.go +171 -171
  164. package/linthost/rules_ts_no_unnecessary_type_assertion.go +90 -90
  165. package/linthost/rules_ts_no_unsafe_argument.go +50 -50
  166. package/linthost/rules_ts_no_unsafe_assignment.go +60 -60
  167. package/linthost/rules_ts_no_unsafe_call.go +40 -40
  168. package/linthost/rules_ts_no_unsafe_enum_comparison.go +147 -147
  169. package/linthost/rules_ts_no_unsafe_member_access.go +29 -29
  170. package/linthost/rules_ts_no_unsafe_return.go +64 -64
  171. package/linthost/rules_ts_no_unsafe_unary_minus.go +42 -42
  172. package/linthost/rules_ts_no_useless_constructor.go +20 -20
  173. package/linthost/rules_ts_non_nullable_type_assertion_style.go +60 -60
  174. package/linthost/rules_ts_parameter_properties.go +10 -10
  175. package/linthost/rules_ts_prefer_find.go +116 -116
  176. package/linthost/rules_ts_prefer_includes.go +173 -173
  177. package/linthost/rules_ts_prefer_nullish_coalescing.go +54 -54
  178. package/linthost/rules_ts_prefer_optional_chain.go +156 -156
  179. package/linthost/rules_ts_prefer_promise_reject_errors.go +104 -104
  180. package/linthost/rules_ts_prefer_readonly.go +31 -31
  181. package/linthost/rules_ts_prefer_reduce_type_parameter.go +74 -74
  182. package/linthost/rules_ts_prefer_regexp_exec.go +69 -69
  183. package/linthost/rules_ts_prefer_return_this_type.go +79 -79
  184. package/linthost/rules_ts_prefer_string_starts_ends_with.go +237 -237
  185. package/linthost/rules_ts_promise_function_async.go +41 -41
  186. package/linthost/rules_ts_related_getter_setter_pairs.go +61 -61
  187. package/linthost/rules_ts_require_array_sort_compare.go +58 -58
  188. package/linthost/rules_ts_restrict_plus_operands.go +82 -82
  189. package/linthost/rules_ts_restrict_template_expressions.go +50 -50
  190. package/linthost/rules_ts_simple.go +249 -249
  191. package/linthost/rules_ts_sort_type_constituents.go +88 -88
  192. package/linthost/rules_ts_strict_boolean_expressions.go +103 -103
  193. package/linthost/rules_ts_switch_exhaustiveness_check.go +90 -90
  194. package/linthost/rules_ts_unbound_method.go +65 -65
  195. package/linthost/rules_tsdoc.go +88 -88
  196. package/linthost/rules_unicorn_better_regex.go +1 -1
  197. package/linthost/rules_unicorn_catch_error_name.go +19 -19
  198. package/linthost/rules_unicorn_consistent_assert.go +20 -20
  199. package/linthost/rules_unicorn_consistent_date_clone.go +27 -27
  200. package/linthost/rules_unicorn_consistent_destructuring.go +3 -3
  201. package/linthost/rules_unicorn_consistent_empty_array_spread.go +34 -34
  202. package/linthost/rules_unicorn_consistent_existence_index_check.go +42 -42
  203. package/linthost/rules_unicorn_consistent_function_scoping.go +3 -3
  204. package/linthost/rules_unicorn_consistent_template_literal_escape.go +3 -3
  205. package/linthost/rules_unicorn_custom_error_definition.go +92 -92
  206. package/linthost/rules_unicorn_empty_brace_spaces.go +47 -47
  207. package/linthost/rules_unicorn_error_message.go +34 -34
  208. package/linthost/rules_unicorn_escape_case.go +14 -14
  209. package/linthost/rules_unicorn_expiring_todo_comments.go +36 -36
  210. package/linthost/rules_unicorn_explicit_length_check.go +51 -51
  211. package/linthost/rules_unicorn_filename_case.go +1 -1
  212. package/linthost/rules_unicorn_import_style.go +1 -1
  213. package/linthost/rules_unicorn_isolated_functions.go +2 -2
  214. package/linthost/rules_unicorn_new_for_builtins.go +49 -49
  215. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +31 -31
  216. package/linthost/rules_unicorn_no_accessor_recursion.go +69 -69
  217. package/linthost/rules_unicorn_no_anonymous_default_export.go +40 -40
  218. package/linthost/rules_unicorn_no_array_callback_reference.go +33 -33
  219. package/linthost/rules_unicorn_no_array_for_each.go +14 -14
  220. package/linthost/rules_unicorn_no_array_method_this_argument.go +29 -29
  221. package/linthost/rules_unicorn_no_array_reduce.go +15 -15
  222. package/linthost/rules_unicorn_no_array_reverse.go +17 -17
  223. package/linthost/rules_unicorn_no_array_sort.go +17 -17
  224. package/linthost/rules_unicorn_no_await_expression_member.go +12 -12
  225. package/linthost/rules_unicorn_no_await_in_promise_methods.go +38 -38
  226. package/linthost/rules_unicorn_no_console_spaces.go +30 -30
  227. package/linthost/rules_unicorn_no_document_cookie.go +13 -13
  228. package/linthost/rules_unicorn_no_empty_file.go +16 -16
  229. package/linthost/rules_unicorn_no_for_loop.go +58 -58
  230. package/linthost/rules_unicorn_no_hex_escape.go +11 -11
  231. package/linthost/rules_unicorn_no_immediate_mutation.go +55 -55
  232. package/linthost/rules_unicorn_no_instanceof_builtins.go +34 -34
  233. package/linthost/rules_unicorn_no_invalid_fetch_options.go +52 -52
  234. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +25 -25
  235. package/linthost/rules_unicorn_no_keyword_prefix.go +56 -56
  236. package/linthost/rules_unicorn_no_lonely_if.go +21 -21
  237. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +22 -22
  238. package/linthost/rules_unicorn_no_named_default.go +25 -25
  239. package/linthost/rules_unicorn_no_negated_condition.go +54 -54
  240. package/linthost/rules_unicorn_no_negation_in_equality_check.go +23 -23
  241. package/linthost/rules_unicorn_no_nested_ternary.go +15 -15
  242. package/linthost/rules_unicorn_no_new_array.go +5 -5
  243. package/linthost/rules_unicorn_no_new_buffer.go +5 -5
  244. package/linthost/rules_unicorn_no_null.go +2 -2
  245. package/linthost/rules_unicorn_no_object_as_default_parameter.go +19 -19
  246. package/linthost/rules_unicorn_no_process_exit.go +17 -17
  247. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +29 -29
  248. package/linthost/rules_unicorn_no_static_only_class.go +27 -27
  249. package/linthost/rules_unicorn_no_thenable.go +47 -47
  250. package/linthost/rules_unicorn_no_this_assignment.go +10 -10
  251. package/linthost/rules_unicorn_no_typeof_undefined.go +31 -31
  252. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +23 -23
  253. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +37 -37
  254. package/linthost/rules_unicorn_no_unnecessary_await.go +24 -24
  255. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +22 -22
  256. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +67 -67
  257. package/linthost/rules_unicorn_no_unreadable_iife.go +22 -22
  258. package/linthost/rules_unicorn_no_useless_collection_argument.go +34 -34
  259. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +18 -18
  260. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +49 -49
  261. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +41 -41
  262. package/linthost/rules_unicorn_no_useless_length_check.go +82 -82
  263. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +42 -42
  264. package/linthost/rules_unicorn_no_useless_spread.go +38 -38
  265. package/linthost/rules_unicorn_no_useless_switch_case.go +29 -29
  266. package/linthost/rules_unicorn_no_useless_undefined.go +25 -25
  267. package/linthost/rules_unicorn_no_zero_fractions.go +17 -17
  268. package/linthost/rules_unicorn_number_literal_case.go +33 -33
  269. package/linthost/rules_unicorn_numeric_separators_style.go +36 -36
  270. package/linthost/rules_unicorn_prefer_add_event_listener.go +30 -30
  271. package/linthost/rules_unicorn_prefer_array_find.go +27 -27
  272. package/linthost/rules_unicorn_prefer_array_flat.go +25 -25
  273. package/linthost/rules_unicorn_prefer_array_flat_map.go +37 -37
  274. package/linthost/rules_unicorn_prefer_array_index_of.go +108 -108
  275. package/linthost/rules_unicorn_prefer_array_some.go +43 -43
  276. package/linthost/rules_unicorn_prefer_at.go +42 -42
  277. package/linthost/rules_unicorn_prefer_bigint_literals.go +42 -42
  278. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +15 -15
  279. package/linthost/rules_unicorn_prefer_class_fields.go +57 -57
  280. package/linthost/rules_unicorn_prefer_classlist_toggle.go +76 -76
  281. package/linthost/rules_unicorn_prefer_code_point.go +21 -21
  282. package/linthost/rules_unicorn_prefer_date_now.go +47 -47
  283. package/linthost/rules_unicorn_prefer_default_parameters.go +96 -96
  284. package/linthost/rules_unicorn_prefer_dom_node_append.go +14 -14
  285. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +27 -27
  286. package/linthost/rules_unicorn_prefer_dom_node_remove.go +17 -17
  287. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +11 -11
  288. package/linthost/rules_unicorn_prefer_event_target.go +10 -10
  289. package/linthost/rules_unicorn_prefer_export_from.go +99 -99
  290. package/linthost/rules_unicorn_prefer_global_this.go +91 -91
  291. package/linthost/rules_unicorn_prefer_import_meta_properties.go +38 -38
  292. package/linthost/rules_unicorn_prefer_includes.go +108 -108
  293. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +37 -37
  294. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +10 -10
  295. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +20 -20
  296. package/linthost/rules_unicorn_prefer_math_min_max.go +33 -33
  297. package/linthost/rules_unicorn_prefer_math_trunc.go +32 -32
  298. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +18 -18
  299. package/linthost/rules_unicorn_prefer_modern_math_apis.go +38 -38
  300. package/linthost/rules_unicorn_prefer_module.go +22 -22
  301. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +73 -73
  302. package/linthost/rules_unicorn_prefer_negative_index.go +46 -46
  303. package/linthost/rules_unicorn_prefer_node_protocol.go +90 -90
  304. package/linthost/rules_unicorn_prefer_number_properties.go +57 -57
  305. package/linthost/rules_unicorn_prefer_object_from_entries.go +26 -26
  306. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
  307. package/linthost/rules_unicorn_prefer_prototype_methods.go +25 -25
  308. package/linthost/rules_unicorn_prefer_query_selector.go +25 -25
  309. package/linthost/rules_unicorn_prefer_reflect_apply.go +10 -10
  310. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +47 -47
  311. package/linthost/rules_unicorn_prefer_response_static_json.go +26 -26
  312. package/linthost/rules_unicorn_prefer_set_has.go +16 -16
  313. package/linthost/rules_unicorn_prefer_set_size.go +22 -22
  314. package/linthost/rules_unicorn_prefer_simple_condition_first.go +3 -3
  315. package/linthost/rules_unicorn_prefer_single_call.go +59 -59
  316. package/linthost/rules_unicorn_prefer_spread.go +24 -24
  317. package/linthost/rules_unicorn_prefer_string_raw.go +12 -12
  318. package/linthost/rules_unicorn_prefer_string_replace_all.go +46 -46
  319. package/linthost/rules_unicorn_prefer_string_slice.go +17 -17
  320. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +104 -104
  321. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +21 -21
  322. package/linthost/rules_unicorn_prefer_structured_clone.go +36 -36
  323. package/linthost/rules_unicorn_prefer_switch.go +65 -65
  324. package/linthost/rules_unicorn_prefer_ternary.go +35 -35
  325. package/linthost/rules_unicorn_prefer_top_level_await.go +34 -34
  326. package/linthost/rules_unicorn_prefer_type_error.go +84 -84
  327. package/linthost/rules_unicorn_prevent_abbreviations.go +68 -68
  328. package/linthost/rules_unicorn_relative_url_style.go +19 -19
  329. package/linthost/rules_unicorn_require_array_join_separator.go +18 -18
  330. package/linthost/rules_unicorn_require_module_attributes.go +25 -25
  331. package/linthost/rules_unicorn_require_module_specifiers.go +28 -28
  332. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +18 -18
  333. package/linthost/rules_unicorn_require_post_message_target_origin.go +18 -18
  334. package/linthost/rules_unicorn_string_content.go +1 -1
  335. package/linthost/rules_unicorn_switch_case_braces.go +16 -16
  336. package/linthost/rules_unicorn_switch_case_break_position.go +3 -3
  337. package/linthost/rules_unicorn_template_indent.go +1 -1
  338. package/linthost/rules_unicorn_text_encoding_identifier_case.go +36 -36
  339. package/linthost/rules_unicorn_throw_new_error.go +30 -30
  340. package/linthost/rules_var.go +273 -3
  341. package/package.json +3 -3
  342. package/src/defaultFormat.ts +11 -7
  343. package/src/structures/ITtscLintConfig.ts +5 -5
  344. package/src/structures/TtscLintRuleSetting.ts +19 -19
  345. package/src/structures/{ITtscLintFormat.ts → format/ITtscLintFormat.ts} +48 -75
  346. package/src/structures/format/ITtscLintFormatJsDoc.ts +16 -0
  347. package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
  348. package/src/structures/format/index.ts +3 -0
  349. package/src/structures/index.ts +1 -1
  350. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +20 -31
  351. package/src/structures/rules/ITtscLintBoundariesRules.ts +32 -36
  352. package/src/structures/rules/ITtscLintContributorRules.ts +6 -7
  353. package/src/structures/rules/ITtscLintCoreRules.ts +380 -408
  354. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +5 -5
  355. package/src/structures/rules/ITtscLintCypressRules.ts +34 -38
  356. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +28 -41
  357. package/src/structures/rules/ITtscLintFunctionalRules.ts +51 -56
  358. package/src/structures/rules/ITtscLintJestRules.ts +63 -74
  359. package/src/structures/rules/ITtscLintJsdocRules.ts +47 -54
  360. package/src/structures/rules/ITtscLintJsxA11yRules.ts +94 -102
  361. package/src/structures/rules/ITtscLintNextjsRules.ts +50 -55
  362. package/src/structures/rules/ITtscLintPlaywrightRules.ts +104 -122
  363. package/src/structures/rules/ITtscLintPromiseRules.ts +62 -71
  364. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +4 -4
  365. package/src/structures/rules/ITtscLintReactPerfRules.ts +17 -19
  366. package/src/structures/rules/ITtscLintReactRuleOptions.ts +8 -10
  367. package/src/structures/rules/ITtscLintReactRules.ts +91 -104
  368. package/src/structures/rules/ITtscLintRegexpRules.ts +46 -54
  369. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +7 -8
  370. package/src/structures/rules/ITtscLintSecurityRules.ts +45 -52
  371. package/src/structures/rules/ITtscLintSolidRules.ts +53 -56
  372. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +3 -4
  373. package/src/structures/rules/ITtscLintStorybookRules.ts +60 -69
  374. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +31 -37
  375. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +2 -3
  376. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +102 -119
  377. package/src/structures/rules/ITtscLintTypeScriptRules.ts +460 -518
  378. package/src/structures/rules/ITtscLintVitestRules.ts +43 -51
  379. package/lib/structures/ITtscLintFormat.js.map +0 -1
  380. /package/lib/structures/{ITtscLintFormat.js → format/ITtscLintFormat.js} +0 -0
@@ -180,6 +180,16 @@ func printBlock(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
180
180
  bodyParts := make([]Doc, 0, len(items)*2)
181
181
  for i, item := range items {
182
182
  if i > 0 {
183
+ // A lone leading-semicolon ASI guard stays glued to the statement
184
+ // it protects (`;(expr)`): when the previous statement is an empty
185
+ // `;` sitting directly against this one in the source, emit no line
186
+ // break, so the reflow agrees with format/orphan-semi instead of
187
+ // ping-ponging it back onto two lines every cascade pass.
188
+ if stmts[i-1].Kind == shimast.KindEmptyStatement &&
189
+ !rangeHasNewline(ctx.Source, stmts[i-1].End(), shimscanner.SkipTrivia(ctx.Source, stmts[i].Pos())) {
190
+ bodyParts = append(bodyParts, item)
191
+ continue
192
+ }
183
193
  if blankLineBetweenStatements(ctx.Source, stmts[i-1].End(), stmts[i].Pos()) {
184
194
  bodyParts = append(bodyParts, Literalline())
185
195
  }
@@ -196,6 +206,21 @@ func printBlock(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
196
206
  return doc, covered
197
207
  }
198
208
 
209
+ // rangeHasNewline reports whether src[start:end) contains a newline. Used
210
+ // to detect whether a leading-semicolon guard sits on the same line as
211
+ // the statement it protects.
212
+ func rangeHasNewline(src string, start, end int) bool {
213
+ if start < 0 || end > len(src) || end <= start {
214
+ return false
215
+ }
216
+ for i := start; i < end; i++ {
217
+ if src[i] == '\n' {
218
+ return true
219
+ }
220
+ }
221
+ return false
222
+ }
223
+
199
224
  // blankLineBetweenStatements reports whether the source gap between the
200
225
  // end of one block statement and the start of the next contains a blank
201
226
  // line — two or more newlines. printBlock uses it to keep a single
@@ -115,39 +115,72 @@ func printImportDeclaration(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
115
115
  if clauseData == nil || clauseData.NamedBindings == nil {
116
116
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
117
117
  }
118
- if clauseData.NamedBindings.Kind != shimast.KindNamedImports {
118
+ nb := clauseData.NamedBindings
119
+ if nb.Kind != shimast.KindNamedImports {
119
120
  // Namespace imports (`import * as ns from "x"`) have no
120
121
  // reflow surface; leave them alone.
121
122
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
122
123
  }
123
- if clause.Name() != nil {
124
- // `import Default, { … } from "x"` — keep verbatim for v1.
125
- return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
126
- }
127
124
  // AttributeClause (`with { ... }` / `assert { ... }`) lives after
128
125
  // the module specifier. Fall back to verbatim when present so we
129
126
  // don't drop attributes silently.
130
127
  if imp.Attributes != nil {
131
128
  return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
132
129
  }
130
+ // A comment between specifiers would be dropped by the fresh separators.
131
+ if listHasInterItemComments(ctx, nb) {
132
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
133
+ }
134
+ ni := nb.AsNamedImports()
135
+ if ni == nil || ni.Elements == nil {
136
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
137
+ }
133
138
 
134
- // Bracketed clause prefix: `import ` (and optional `type `).
139
+ // Bracketed clause prefix: `import `, `import type `, or — when a default
140
+ // binding precedes the named clause — `import Default, `. A default never
141
+ // combines with `type` (TS forbids `import type D, { … }`), so the two
142
+ // prefix forms are exclusive.
135
143
  prefix := "import "
136
144
  if clause.IsTypeOnly() {
137
145
  prefix = "import type "
138
146
  }
139
- named, covered := PrintNode(ctx, clauseData.NamedBindings)
147
+ if name := clause.Name(); name != nil {
148
+ prefix = "import " + identifierText(name) + ", "
149
+ }
150
+
151
+ items := make([]Doc, 0, len(ni.Elements.Nodes))
152
+ covered := true
153
+ for _, spec := range ni.Elements.Nodes {
154
+ if spec == nil {
155
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
156
+ }
157
+ doc, childCovered := PrintNode(ctx, spec)
158
+ covered = covered && childCovered
159
+ items = append(items, doc)
160
+ }
161
+
140
162
  moduleSpec := verbatim(ctx, imp.ModuleSpecifier)
141
163
  covered = covered && !nodeSpansMultipleLines(ctx, imp.ModuleSpecifier)
142
- parts := []Doc{Text(prefix), named, Text(" from "), moduleSpec}
164
+ suffixParts := []Doc{Text(" from "), moduleSpec}
143
165
  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(";"))
166
+ // Preserve the user's terminator decision. Emitting `;` unconditionally
167
+ // would collide with `format/semi`'s zero-width insert and produce `;;`.
168
+ suffixParts = append(suffixParts, Text(";"))
149
169
  }
150
- return Concat(parts...), covered
170
+
171
+ // The whole declaration renders as one Group via Prefix/Suffix, so the
172
+ // named-brace break decision counts the ` from "..."` tail: a brace that
173
+ // fits but whose declaration line overflows still breaks, and a
174
+ // default-combined import breaks its brace too.
175
+ return printList(ctx, listShape{
176
+ Prefix: Text(prefix),
177
+ OpenTok: "{",
178
+ CloseTok: "}",
179
+ Items: items,
180
+ Space: true,
181
+ AddComma: ctx.allowsEs5TrailingComma(),
182
+ Suffix: Concat(suffixParts...),
183
+ }), covered
151
184
  }
152
185
 
153
186
  // 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,71 @@ 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
+ // HugLastForce drops the exploded fallback from a HugLast list: the hugged
70
+ // shape is chosen even when its opening line overflows printWidth. The
71
+ // call-argument printer sets it for a test-framework call
72
+ // (`test("very long description", () => { … })`), which Prettier never
73
+ // explodes — the callback always rides the open paren. Requires HugLast.
74
+ HugLastForce bool
38
75
  // ForceBreak commits the list to its broken, one-item-per-line shape
39
76
  // even when it would fit flat. The object-literal printer sets it to
40
77
  // mirror Prettier's objectWrap:"preserve" — an object the source
41
78
  // wrote with a newline after `{` stays expanded.
42
79
  ForceBreak bool
80
+ // Fill packs the items with Prettier's fill layout — as many per line as
81
+ // fit — instead of one item per line when the list breaks. The array
82
+ // printer sets it for a concisely-printed numeric array. Mutually
83
+ // exclusive with HugLast / HugFirst.
84
+ Fill bool
85
+ // Prefix and Suffix are emitted inside the list's Group, before OpEN and
86
+ // after CLOSE respectively. They let a caller fold surrounding tokens into
87
+ // the same fit-or-break decision: an import declaration sets Prefix to
88
+ // `import ` (or `import D, `) and Suffix to ` from "..."` so the named-brace
89
+ // group's flat-fit check counts the whole declaration line — Prettier never
90
+ // breaks a brace that fits but whose `from` tail overflows is measured here,
91
+ // not in a separate group. Only used on the plain (non-hugging) path.
92
+ Prefix Doc
93
+ Suffix Doc
94
+ // BlankBefore[i] reports whether the source had a blank line before item i
95
+ // (i.e. between item i-1 and item i). When any entry is set the list is
96
+ // forced broken and a single blank line is preserved before that item,
97
+ // mirroring Prettier. Length, when non-nil, equals len(Items); index 0 is
98
+ // ignored (no item precedes the first).
99
+ BlankBefore []bool
100
+ }
101
+
102
+ // blankBeforeItems computes BlankBefore for a node list: entry i is true when
103
+ // the source had a blank line (two or more newlines) between item i-1 and
104
+ // item i. Returns nil for a list too short to carry one, so callers pass it
105
+ // through unconditionally.
106
+ func blankBeforeItems(src string, elems []*shimast.Node) []bool {
107
+ if len(elems) < 2 {
108
+ return nil
109
+ }
110
+ out := make([]bool, len(elems))
111
+ for i := 1; i < len(elems); i++ {
112
+ if elems[i-1] == nil || elems[i] == nil {
113
+ continue
114
+ }
115
+ out[i] = blankLineBetweenStatements(src, elems[i-1].End(), elems[i].Pos())
116
+ }
117
+ return out
118
+ }
119
+
120
+ // hasBlankBefore reports whether any item carries a preserved blank line.
121
+ func (s listShape) hasBlankBefore() bool {
122
+ for _, b := range s.BlankBefore {
123
+ if b {
124
+ return true
125
+ }
126
+ }
127
+ return false
43
128
  }
44
129
 
45
130
  // printList renders the list shape as a Doc tree. Empty lists collapse
@@ -50,19 +135,33 @@ func printList(ctx *PrintContext, shape listShape) Doc {
50
135
  return Text(shape.OpenTok + shape.CloseTok)
51
136
  }
52
137
  plain := printListPlain(ctx, shape)
53
- if !shape.HugLast {
138
+ // A source blank line forces the plain broken layout (Prettier never hugs or
139
+ // packs flat across a blank line), so skip the hugging ConditionalGroup.
140
+ if (!shape.HugLast && !shape.HugFirst) || shape.hasBlankBefore() {
54
141
  return plain
55
142
  }
56
- // A HugLast list offers the engine up to three shapes, in preference
143
+ // A hugging list offers the engine up to three shapes, in preference
57
144
  // order:
58
145
  // 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;
146
+ // 2. hugged — the callback (last for HugLast, first for HugFirst)
147
+ // committed to its multi-line shape with the other items inline,
148
+ // chosen when its opening line fits but the all-flat form does not;
62
149
  // 3. plain — every item exploded onto its own indented line.
63
150
  // The all-flat option is dropped when the list cannot render flat
64
151
  // (a block-bodied callback argument carries hard line breaks).
65
152
  hugged := printListHuggingLast(ctx, shape)
153
+ if shape.HugFirst {
154
+ hugged = printListHuggingFirst(ctx, shape)
155
+ }
156
+ // A test-framework call hugs its callback unconditionally — Prettier never
157
+ // explodes its arguments — so drop the exploded fallback. The all-flat
158
+ // option still wins when the whole call fits (an empty-body callback).
159
+ if shape.HugLastForce {
160
+ if allFlat, ok := flatten(plain); ok {
161
+ return ConditionalGroup(allFlat, hugged)
162
+ }
163
+ return hugged
164
+ }
66
165
  if allFlat, ok := flatten(plain); ok {
67
166
  return ConditionalGroup(allFlat, hugged, plain)
68
167
  }
@@ -75,7 +174,6 @@ func printList(ctx *PrintContext, shape listShape) Doc {
75
174
  // comma — when it does not.
76
175
  func printListPlain(ctx *PrintContext, shape listShape) Doc {
77
176
  sep := Concat(Text(","), Line())
78
- body := Join(sep, shape.Items)
79
177
 
80
178
  flatPad := Doc{Kind: docNil}
81
179
  if shape.Space {
@@ -87,6 +185,50 @@ func printListPlain(ctx *PrintContext, shape listShape) Doc {
87
185
  trailing = IfBreak(Text(","), Doc{Kind: docNil})
88
186
  }
89
187
 
188
+ hasBlank := shape.hasBlankBefore()
189
+
190
+ var body Doc
191
+ if hasBlank {
192
+ // Per-item body that preserves a single source blank line before an item:
193
+ // `,` then a Literalline (the empty line, no indent, mirroring printBlock)
194
+ // then the normal Line. A blank line forces the group broken.
195
+ parts := make([]Doc, 0, len(shape.Items)*3)
196
+ for i, it := range shape.Items {
197
+ if i > 0 {
198
+ parts = append(parts, Text(","))
199
+ if i < len(shape.BlankBefore) && shape.BlankBefore[i] {
200
+ parts = append(parts, Literalline())
201
+ }
202
+ parts = append(parts, Line())
203
+ }
204
+ parts = append(parts, it)
205
+ }
206
+ body = Concat(parts...)
207
+ } else if shape.Fill {
208
+ // Each non-last fill content carries its own comma — `[el, ","]` — and the
209
+ // separator is just a Line. Prettier measures the pack/break decision on
210
+ // `[content_i, line, content_{i+1}]`, so the next element's comma must be
211
+ // part of its content or the line would pack one element too many. The
212
+ // last element carries no comma here; the trailing comma is the shared
213
+ // ifBreak appended after the body so it tracks the GROUP's break mode (a
214
+ // fill-mode ifBreak would drop the comma when the last item packs flat,
215
+ // making the format non-idempotent).
216
+ parts := make([]Doc, 0, len(shape.Items)*2-1)
217
+ for i, it := range shape.Items {
218
+ if i > 0 {
219
+ parts = append(parts, Line())
220
+ }
221
+ if i < len(shape.Items)-1 {
222
+ parts = append(parts, Concat(it, Text(",")))
223
+ } else {
224
+ parts = append(parts, it)
225
+ }
226
+ }
227
+ body = Fill(parts...)
228
+ } else {
229
+ body = Join(sep, shape.Items)
230
+ }
231
+
90
232
  openTok := Text(shape.OpenTok)
91
233
  closeTok := Text(shape.CloseTok)
92
234
 
@@ -96,11 +238,19 @@ func printListPlain(ctx *PrintContext, shape listShape) Doc {
96
238
  trailingSep := IfBreak(Hardline(), flatPad)
97
239
 
98
240
  bodyBlock := Indent(ctx.indentUnit(), leadingSep, body, trailing)
99
- doc := Concat(openTok, bodyBlock, trailingSep, closeTok)
241
+ prefix := shape.Prefix
242
+ if prefix.Kind == 0 {
243
+ prefix = Doc{Kind: docNil}
244
+ }
245
+ suffix := shape.Suffix
246
+ if suffix.Kind == 0 {
247
+ suffix = Doc{Kind: docNil}
248
+ }
249
+ doc := Concat(prefix, openTok, bodyBlock, trailingSep, closeTok, suffix)
100
250
  group := Group(doc)
101
251
  // ForceBreak (object-literal newline preservation) commits the group
102
252
  // to its broken shape regardless of fit.
103
- group.Break = shape.ForceBreak
253
+ group.Break = shape.ForceBreak || hasBlank
104
254
  return group
105
255
  }
106
256
 
@@ -143,3 +293,36 @@ func printListHuggingLast(ctx *PrintContext, shape listShape) Doc {
143
293
  parts = append(parts, last, Text(shape.CloseTok))
144
294
  return Concat(parts...)
145
295
  }
296
+
297
+ // printListHuggingFirst renders the "first-argument hugging" shape
298
+ // Prettier uses for `foo(() => { … }, target)`: the first item stays
299
+ // attached to the open paren and the remaining simple items flow inline
300
+ // after it, so a multi-line callback body does not explode the trailing
301
+ // arguments onto their own lines.
302
+ //
303
+ // hugged: OPEN OPEN-of-first … CLOSE-of-first, b, c CLOSE
304
+ //
305
+ // Like printListHuggingLast it returns a plain Concat (the callback's own
306
+ // printer owns its body's break) and forces the hugged item's first Group
307
+ // broken when the item has no hard line breaks of its own, so the hugged
308
+ // option stays distinct from the all-flat option.
309
+ func printListHuggingFirst(ctx *PrintContext, shape listShape) Doc {
310
+ first := shape.Items[0]
311
+ rest := shape.Items[1:]
312
+ if _, flat := flatten(first); flat {
313
+ first, _ = forceBreakFirstGroup(first)
314
+ }
315
+ parts := []Doc{Text(shape.OpenTok), first}
316
+ for _, item := range rest {
317
+ // The trailing simple arguments ride the close line flat — Prettier keeps
318
+ // them on one line even when that line overflows (a long zero/one-argument
319
+ // trailing call `}, makeAccumulator(single))` is not broken). Force each
320
+ // flat so its own Group does not break against the close-line width.
321
+ if flat, ok := flatten(item); ok {
322
+ item = flat
323
+ }
324
+ parts = append(parts, Text(", "), item)
325
+ }
326
+ parts = append(parts, Text(shape.CloseTok))
327
+ return Concat(parts...)
328
+ }
@@ -35,6 +35,11 @@ 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 so the enclosing reflow abstains.
40
+ if listHasInterItemComments(ctx, node) {
41
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
42
+ }
38
43
  items := make([]Doc, 0, len(obj.Properties.Nodes))
39
44
  covered := true
40
45
  for _, prop := range obj.Properties.Nodes {
@@ -60,12 +65,13 @@ func printObjectLiteral(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
60
65
  // "none" suppresses. Pairs with the call/array branches so the printer
61
66
  // never disagrees with the trailing-comma rule on the same setting.
62
67
  return printList(ctx, listShape{
63
- OpenTok: "{",
64
- CloseTok: "}",
65
- Items: items,
66
- Space: true,
67
- AddComma: ctx.allowsEs5TrailingComma(),
68
- ForceBreak: forceBreak,
68
+ OpenTok: "{",
69
+ CloseTok: "}",
70
+ Items: items,
71
+ Space: true,
72
+ AddComma: ctx.allowsEs5TrailingComma(),
73
+ ForceBreak: forceBreak,
74
+ BlankBefore: blankBeforeItems(ctx.Source, obj.Properties.Nodes),
69
75
  }), covered
70
76
  }
71
77
 
@@ -0,0 +1,106 @@
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`, but each nested chain indents by a FIXED 2 columns (not
24
+ // `tabWidth`), matching Prettier 3: at tabWidth 4 the outer arms sit at
25
+ // column 4 and the nested arms at column 6, not 8.
26
+ //
27
+ // The second return value is the coverage flag (see PrintNode): the AND
28
+ // of the test and both branches, so a multi-line verbatim node anywhere
29
+ // in the chain makes formatPrintWidth abstain rather than emit a
30
+ // half-reflowed shape.
31
+ func printConditionalExpression(ctx *PrintContext, node *shimast.Node) (Doc, bool) {
32
+ body, covered := buildConditionalChain(ctx, node, ctx.indentUnit())
33
+ if body.IsNil() {
34
+ return verbatim(ctx, node), !nodeSpansMultipleLines(ctx, node)
35
+ }
36
+ return Group(body), covered
37
+ }
38
+
39
+ // buildConditionalChain returns the chain body for `node` WITHOUT a
40
+ // surrounding Group, so a nested conditional recursed from here shares
41
+ // the caller's group and its arms indent one level deeper. The top-level
42
+ // printConditionalExpression wraps the outermost body in the single
43
+ // Group that owns the break decision.
44
+ func buildConditionalChain(ctx *PrintContext, node *shimast.Node, indentCols int) (Doc, bool) {
45
+ cond := node.AsConditionalExpression()
46
+ if cond == nil || cond.Condition == nil || cond.WhenTrue == nil || cond.WhenFalse == nil {
47
+ return Doc{}, true
48
+ }
49
+ testDoc, c1 := PrintNode(ctx, cond.Condition)
50
+ consDoc, c2 := ternaryArm(ctx, cond.WhenTrue, true)
51
+ altDoc, c3 := ternaryArm(ctx, cond.WhenFalse, false)
52
+ doc := Concat(
53
+ testDoc,
54
+ Indent(indentCols,
55
+ Line(), Text("? "), consDoc,
56
+ Line(), Text(": "), altDoc,
57
+ ),
58
+ )
59
+ return doc, c1 && c2 && c3
60
+ }
61
+
62
+ // ternaryArm prints a ternary consequent/alternate. A nested conditional
63
+ // continues the chain inline (no new Group) so the staircase
64
+ // accumulates; every other arm prints through the normal dispatcher (an
65
+ // arm that is a `??` expression is already parenthesized in valid
66
+ // source, so it round-trips its parens via the ParenthesizedExpression
67
+ // printer).
68
+ //
69
+ // A non-conditional arm is wrapped in Align so that, when it breaks
70
+ // internally, its continuation hangs under the arm expression's own
71
+ // column (one level past the `? `/`: ` marker) rather than under the
72
+ // chain's rung indent. Prettier hangs a broken call/object arm there:
73
+ //
74
+ // cond
75
+ // ? foo(
76
+ // arg, // arm-column + one level, not rung + one level
77
+ // )
78
+ // : bar
79
+ //
80
+ // Align is a no-op for an arm that stays flat, and a nested conditional
81
+ // is deliberately NOT aligned — it keeps the Indent-based staircase.
82
+ //
83
+ // A nested conditional in the CONSEQUENT (`? `) position is wrapped in
84
+ // parentheses, but only when the chain renders flat: Prettier's ternary-old
85
+ // printer emits `consequent.type === node.type ? ifBreak("", "(") : ""` around
86
+ // the consequent (`a ? (b ? c : d) : e` on one line), and drops the parens for
87
+ // the broken staircase. A nested conditional in the ALTERNATE (`: `) position
88
+ // is never wrapped — it chains. `isConsequent` selects between the two.
89
+ func ternaryArm(ctx *PrintContext, node *shimast.Node, isConsequent bool) (Doc, bool) {
90
+ if node != nil && node.Kind == shimast.KindConditionalExpression {
91
+ // A nested chain indents a fixed 2 columns past its parent rung,
92
+ // independent of tabWidth (Prettier 3's nested-ternary rule).
93
+ chain, covered := buildConditionalChain(ctx, node, 2)
94
+ if isConsequent {
95
+ // `ifBreak("", "(")` … `ifBreak("", ")")`: parens in flat mode only.
96
+ chain = Concat(
97
+ IfBreak(Doc{Kind: docNil}, Text("(")),
98
+ chain,
99
+ IfBreak(Doc{Kind: docNil}, Text(")")),
100
+ )
101
+ }
102
+ return chain, covered
103
+ }
104
+ doc, covered := PrintNode(ctx, node)
105
+ return Align(doc), covered
106
+ }