@ttsc/lint 0.14.0-dev.20260529.1 → 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 +85 -87
  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
@@ -0,0 +1,616 @@
1
+ package linthost
2
+
3
+ import (
4
+ shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
6
+ )
7
+
8
+ // formatIndent normalizes the leading indentation of each statement's
9
+ // first line to `depth * tabWidth` columns (or `depth` tabs under
10
+ // useTabs), mirroring Prettier's block indentation.
11
+ //
12
+ // The rule shares `format/statement-split`'s file-level walk: it
13
+ // registers for KindSourceFile and visits every statement in every
14
+ // statement list (SourceFile body, Block, ModuleBlock, case/default
15
+ // clause) at its nesting depth.
16
+ //
17
+ // Per-statement decision:
18
+ //
19
+ // 1. Find the statement's first non-trivia byte and the start of its
20
+ // physical line.
21
+ // 2. Abstain unless the statement is the first non-whitespace token on
22
+ // that line. A statement sharing a line with a previous statement is
23
+ // `format/statement-split`'s surface; keeping the two rules disjoint
24
+ // means their edits never overlap on one cascade pass.
25
+ // 3. Compare the leading-whitespace run `[lineStart, firstNonWS)` to the
26
+ // desired indent. When they differ, replace the run with the indent.
27
+ //
28
+ // The rule only ever touches a statement's own starting line. Interior
29
+ // and continuation lines belong to `format/print-width`, which owns
30
+ // reflow indentation; rewriting them here would fight that rule.
31
+ //
32
+ // Idempotent: a correctly-indented statement compares equal in step 3
33
+ // and emits nothing.
34
+ type formatIndent struct{}
35
+
36
+ func (formatIndent) Name() string { return "format/indent" }
37
+ func (formatIndent) IsFormat() bool { return true }
38
+ func (formatIndent) Visits() []shimast.Kind {
39
+ return []shimast.Kind{shimast.KindSourceFile}
40
+ }
41
+
42
+ func (formatIndent) Check(ctx *Context, node *shimast.Node) {
43
+ if ctx == nil || ctx.File == nil {
44
+ return
45
+ }
46
+ layout := loadFormatLayout(ctx)
47
+ src := ctx.File.Text()
48
+ var edits []TextEdit
49
+ forEachStatementInList(ctx.File, func(stmt *shimast.Node, depth int) {
50
+ // A statement whose indentation is owned by format/print-width's
51
+ // expression reflow (it sits inside a call/new/array/object that the
52
+ // printer lays out) must not be re-indented here: the printer hangs a
53
+ // callback body under its call-argument column, which is deeper than
54
+ // this rule's block-nesting depth, and reindenting it would oscillate
55
+ // against the printer pass forever (the cascade never converges).
56
+ if indentCededToReflow(stmt) || cededUnderBracelessBody(stmt) ||
57
+ cededUnderWrappedFunctionExpression(src, stmt) {
58
+ return
59
+ }
60
+ start := shimscanner.SkipTrivia(src, stmt.Pos())
61
+ if start < 0 || start > len(src) {
62
+ return
63
+ }
64
+ lineStart := lineStartOffset(src, start)
65
+ // Only the leading run may be whitespace for this to be the first
66
+ // token on its line. A non-whitespace byte in `[lineStart, start)`
67
+ // means a previous statement shares the line, defer to
68
+ // `format/statement-split`.
69
+ for i := lineStart; i < start; i++ {
70
+ if src[i] != ' ' && src[i] != '\t' {
71
+ return
72
+ }
73
+ }
74
+ want := layout.indent(depth)
75
+ // Cede everything inside a chained-arrow body (`a => b => { … }`).
76
+ // Prettier indents such a body one extra level for the chain
77
+ // continuation, so the body block AND every statement nested below it
78
+ // sit one level deeper than the column-0 depth model computes;
79
+ // reindenting any of them to depth*tabWidth de-indents correct source.
80
+ // This is detected structurally (an ancestor block whose owning arrow is
81
+ // itself another arrow's body), so a mangled input cannot fool it,
82
+ // unlike a class method body, a switch case body, a single-head arrow,
83
+ // or a multi-line-condition `if` body, all of which the depth model
84
+ // places correctly.
85
+ if cededByChainedArrowAncestor(stmt) {
86
+ return
87
+ }
88
+ // A decorated declaration statement (`@Dec class B {}`,
89
+ // `@Dec function f() {}`, `@Dec interface I {}` nested in a block or
90
+ // namespace) spans multiple physical lines when its decorators sit on
91
+ // their own lines. `start` is the first decorator's `@`, so the single
92
+ // first-line edit below would align only that first decorator line and
93
+ // leave each further decorator line and the DECLARATION line (`class B`,
94
+ // `function f`, …) at their original column — the very gap FIX C closes.
95
+ // Mirror the decorated-member header pass: re-indent every decorator line
96
+ // and the declaration line to the statement's depth via the shared
97
+ // helpers, which generalize to any decorated node. This is checked before
98
+ // the single-line short-circuit because the first decorator line can
99
+ // already be correct while the declaration line is not (`@Dec\nclass B`
100
+ // with the decorator indented and `class B` left at column 0).
101
+ // reindentHeaderLine is a no-op when a position is not the first
102
+ // non-whitespace byte on its line (a same-line decorator such as `@Dec
103
+ // class B`) or when the run already equals `want`, so this stays
104
+ // idempotent and never double-edits one physical line.
105
+ if decorators := stmt.Decorators(); len(decorators) > 0 {
106
+ for _, dec := range decorators {
107
+ if dec == nil {
108
+ continue
109
+ }
110
+ reindentHeaderLine(src, shimscanner.SkipTrivia(src, dec.Pos()), want, &edits)
111
+ }
112
+ if declPos := memberDeclarationStart(src, stmt); declPos >= 0 {
113
+ reindentHeaderLine(src, declPos, want, &edits)
114
+ }
115
+ return
116
+ }
117
+ if src[lineStart:start] == want {
118
+ return
119
+ }
120
+ edits = append(edits, TextEdit{Pos: lineStart, End: start, Text: want})
121
+ })
122
+ // Second pass: align the block-closing `}` lines and the header lines
123
+ // (class / interface / type-literal member declarations and
124
+ // `case`/`default` labels) that are neither statements nor closing
125
+ // braces. Both surfaces are visited by a single ForEachChild descent
126
+ // (forEachIndentFrame) that tracks depth once and fires a brace callback
127
+ // and a header callback at the right nodes/depths; the two callbacks
128
+ // never target the same node, so this is exactly the two former
129
+ // descents fused.
130
+ //
131
+ // Closing brace: a `}` is not a statement, so the statement walk never
132
+ // touches it; without this a mangled (flat) block body gets its
133
+ // statements re-indented while the closing braces stay at their wrong
134
+ // column, and the cascade "converges" on that malformed result (exit 0
135
+ // on broken output). The brace aligns to the block OWNER's depth, one
136
+ // level shallower than the block's own statements, under the same cede /
137
+ // wrapped-head guards as the opening pass, so an expression-nested or
138
+ // wrapped-head block's `}` is left to the printer / its head.
139
+ //
140
+ // Header: the statement walk never visits a member declaration or a
141
+ // clause label (neither is a statement), so without this a flattened
142
+ // class body or switch leaves member headers and case labels at column 0
143
+ // while their bodies are re-indented, a malformed result the cascade
144
+ // reports as success.
145
+ forEachIndentFrame(
146
+ ctx.File,
147
+ func(block *shimast.Node, ownerDepth int) {
148
+ closeBrace := blockCloseBracePos(src, block)
149
+ if closeBrace < 0 {
150
+ return
151
+ }
152
+ lineStart := lineStartOffset(src, closeBrace)
153
+ // The `}` must be the first non-whitespace byte on its line; a brace
154
+ // sharing a line with content (`} else {`, `{ x }`) is not this
155
+ // rule's to move.
156
+ for i := lineStart; i < closeBrace; i++ {
157
+ if src[i] != ' ' && src[i] != '\t' {
158
+ return
159
+ }
160
+ }
161
+ // indentCededToReflow walks block.Parent upward, the same ancestor
162
+ // chain a body statement would, so a callback / expression-nested
163
+ // block's `}` cedes in lockstep with its body (print-width owns it).
164
+ if indentCededToReflow(block) || cededUnderBracelessBody(block) ||
165
+ typeLiteralIndentCeded(src, block, ownerDepth, layout) ||
166
+ cededUnderWrappedFunctionExpression(src, block) {
167
+ return
168
+ }
169
+ // Chained-arrow body: cede the `}` in lockstep with its body
170
+ // statements. A chained-arrow body's own brace, and any brace nested
171
+ // inside it, sit one extra level deep for the chain continuation, so
172
+ // leave them be.
173
+ if cededByChainedArrowAncestor(block) {
174
+ return
175
+ }
176
+ want := layout.indent(ownerDepth)
177
+ if src[lineStart:closeBrace] == want {
178
+ return
179
+ }
180
+ edits = append(edits, TextEdit{Pos: lineStart, End: closeBrace, Text: want})
181
+ },
182
+ func(header *shimast.Node, depth int) {
183
+ if indentCededToReflow(header) || cededByChainedArrowAncestor(header) ||
184
+ cededUnderBracelessBody(header) ||
185
+ typeLiteralIndentCeded(src, header.Parent, depth-1, layout) {
186
+ return
187
+ }
188
+ want := layout.indent(depth)
189
+ decorators := header.Decorators()
190
+ if len(decorators) == 0 {
191
+ // Undecorated member: header.Pos() is the declaration's first byte,
192
+ // so its line is the only one to align.
193
+ reindentHeaderLine(src, shimscanner.SkipTrivia(src, header.Pos()), want, &edits)
194
+ return
195
+ }
196
+ // A decorated member spans multiple physical lines when its decorators
197
+ // sit on their own lines. header.Pos() is the first decorator's `@`, so
198
+ // the bare header pass would only align the first decorator line and
199
+ // leave each further decorator line and the member's DECLARATION line
200
+ // (`name: type` / method signature past the last decorator) at their
201
+ // original column. Re-indent every decorator line and the declaration
202
+ // line to the member's depth. reindentHeaderLine is a no-op when a
203
+ // position is not the first non-whitespace byte on its line (a
204
+ // same-line decorator such as `@Column() name: string`, or `@A @B` on
205
+ // one line) or when the run already equals `want`, so this stays
206
+ // idempotent and never emits two edits for one physical line.
207
+ for _, dec := range decorators {
208
+ if dec == nil {
209
+ continue
210
+ }
211
+ reindentHeaderLine(src, shimscanner.SkipTrivia(src, dec.Pos()), want, &edits)
212
+ }
213
+ if declPos := memberDeclarationStart(src, header); declPos >= 0 {
214
+ reindentHeaderLine(src, declPos, want, &edits)
215
+ }
216
+ },
217
+ )
218
+ if len(edits) == 0 {
219
+ return
220
+ }
221
+ ctx.ReportRangeFix(
222
+ edits[0].Pos,
223
+ edits[0].End,
224
+ "Statement indentation must match its nesting depth.",
225
+ edits...,
226
+ )
227
+ }
228
+
229
+ // indentCededToReflow reports whether `stmt` lives inside an expression
230
+ // whose layout column format/indent cannot compute from block-nesting
231
+ // depth alone, a call/new argument, an array/object element, a
232
+ // conditional branch, or a parenthesized expression. format/indent's
233
+ // depth counts only Block/clause/declaration nesting, so a statement
234
+ // hung under such an expression (a callback body, a `new (class {…})()`
235
+ // method, a `cond ? () => {…} : …` arm) sits at a deeper column than its
236
+ // block depth, and reindenting it to depth*tabWidth both corrupts
237
+ // correct source and ping-pongs against format/print-width every cascade
238
+ // pass (the cascade never converges).
239
+ //
240
+ // Walking outward from the statement, an enclosing expression of those
241
+ // kinds means the indentation is owned by the printer (when print-width
242
+ // is active) or by the already-correct source (when it is off), so
243
+ // format/indent cedes. Reaching the source file or a module block first
244
+ // means the statement is in ordinary block/declaration position,
245
+ // format/indent owns it and indents to its nesting depth.
246
+ func indentCededToReflow(stmt *shimast.Node) bool {
247
+ for n := stmt.Parent; n != nil; n = n.Parent {
248
+ switch n.Kind {
249
+ case shimast.KindCallExpression,
250
+ shimast.KindNewExpression,
251
+ shimast.KindArrayLiteralExpression,
252
+ shimast.KindObjectLiteralExpression,
253
+ shimast.KindConditionalExpression,
254
+ shimast.KindParenthesizedExpression:
255
+ return true
256
+ case shimast.KindSourceFile,
257
+ shimast.KindModuleBlock:
258
+ return false
259
+ }
260
+ }
261
+ return false
262
+ }
263
+
264
+ // cededUnderWrappedFunctionExpression reports whether `node` sits inside a
265
+ // function or arrow EXPRESSION whose header was pushed onto a continuation line,
266
+ // the initializer or value broke after `=` (or a `return`), indenting the
267
+ // `function`/arrow one or more levels past the column its statement starts at:
268
+ //
269
+ // export const f: Sig =
270
+ // function f(
271
+ // a,
272
+ // ): R {
273
+ // body; // sits at the continuation header's depth + 1, not the
274
+ // }; // statement-base depth + 1 the block model computes
275
+ //
276
+ // The block-depth model counts only Block/clause/declaration nesting, so it
277
+ // places the body relative to the statement base and would de-indent the
278
+ // already-correct (printer-/source-owned) body. Detected structurally and
279
+ // positionally, the enclosing function expression begins on a different
280
+ // physical line than its own parent (the declaration / assignment / return it
281
+ // is the value of), so a same-line `const f = () => { … }` is unaffected.
282
+ func cededUnderWrappedFunctionExpression(src string, node *shimast.Node) bool {
283
+ for n := node.Parent; n != nil; n = n.Parent {
284
+ switch n.Kind {
285
+ case shimast.KindSourceFile, shimast.KindModuleBlock:
286
+ return false
287
+ case shimast.KindFunctionExpression, shimast.KindArrowFunction:
288
+ if n.Parent == nil {
289
+ continue
290
+ }
291
+ fnStart := shimscanner.SkipTrivia(src, n.Pos())
292
+ parentStart := shimscanner.SkipTrivia(src, n.Parent.Pos())
293
+ if fnStart < 0 || parentStart < 0 || fnStart > len(src) || parentStart > len(src) {
294
+ continue
295
+ }
296
+ if lineStartOffset(src, fnStart) != lineStartOffset(src, parentStart) {
297
+ return true
298
+ }
299
+ }
300
+ }
301
+ return false
302
+ }
303
+
304
+ // typeLiteralIndentCeded reports whether a type-literal member or its closing
305
+ // brace sits in a layout-dependent position the block-depth model misplaces.
306
+ // A type literal opens on its own indented continuation line when it is a
307
+ // parameter's type (`f(input: Payload & { … })`), an intersection/union
308
+ // operand (`A &\n { … }`), or a multi-line generic argument (`Record<\n
309
+ // string,\n { … }>`); Prettier then indents the members relative to that
310
+ // opening line, not the block depth. The structural walk expects the opening
311
+ // line to be indented to layout.indent(ownerDepth); when that disagrees with
312
+ // the line's actual leading whitespace, the literal is in such a layout-
313
+ // dependent spot and reindenting its members or brace to depth*tabWidth would
314
+ // de-indent already-correct source, so cede. When the opening line IS at its
315
+ // depth (a type literal annotating a variable, or a single-line generic
316
+ // argument), the model is correct and the rule owns it.
317
+ func typeLiteralIndentCeded(src string, owner *shimast.Node, ownerDepth int, layout formatLayout) bool {
318
+ if owner == nil || owner.Kind != shimast.KindTypeLiteral {
319
+ return false
320
+ }
321
+ open := shimscanner.SkipTrivia(src, owner.Pos())
322
+ if open < 0 || open > len(src) {
323
+ return false
324
+ }
325
+ lineStart := lineStartOffset(src, open)
326
+ i := lineStart
327
+ for i < len(src) && (src[i] == ' ' || src[i] == '\t') {
328
+ i++
329
+ }
330
+ return src[lineStart:i] != layout.indent(ownerDepth)
331
+ }
332
+
333
+ // cededUnderBracelessBody reports whether `node` sits inside the braceless
334
+ // body of a control-flow statement (`for (...) stmt;`, `if (c) stmt;`).
335
+ // Prettier indents a braceless body one level past the header line, layout
336
+ // the block-depth model has no frame for, so it would de-indent the body and
337
+ // any block nested inside it (a `for (...) try { … }` loses a level on the
338
+ // `try` body and `catch`). Cede to keep it byte-identical. The walk stops at
339
+ // the enclosing function or source boundary so an ordinary block body, which
340
+ // the depth model places correctly, is unaffected.
341
+ func cededUnderBracelessBody(node *shimast.Node) bool {
342
+ for n := node; n != nil; n = n.Parent {
343
+ p := n.Parent
344
+ if p == nil {
345
+ return false
346
+ }
347
+ switch p.Kind {
348
+ case shimast.KindSourceFile,
349
+ shimast.KindModuleBlock,
350
+ shimast.KindFunctionDeclaration,
351
+ shimast.KindFunctionExpression,
352
+ shimast.KindArrowFunction,
353
+ shimast.KindMethodDeclaration,
354
+ shimast.KindConstructor,
355
+ shimast.KindGetAccessor,
356
+ shimast.KindSetAccessor:
357
+ return false
358
+ case shimast.KindIfStatement,
359
+ shimast.KindForStatement,
360
+ shimast.KindForInStatement,
361
+ shimast.KindForOfStatement,
362
+ shimast.KindWhileStatement:
363
+ if bracelessControlBody(p, n) {
364
+ return true
365
+ }
366
+ }
367
+ }
368
+ return false
369
+ }
370
+
371
+ // bracelessControlBody reports whether `child` is the controlled body of
372
+ // control-flow `parent` and is not itself a Block, the `then`/`else` branch
373
+ // of an `if` (an `else if` chain's nested `if` is excluded) or the loop body
374
+ // of an iteration statement.
375
+ func bracelessControlBody(parent, child *shimast.Node) bool {
376
+ switch parent.Kind {
377
+ case shimast.KindIfStatement:
378
+ ifs := parent.AsIfStatement()
379
+ if ifs.ThenStatement == child && child.Kind != shimast.KindBlock {
380
+ return true
381
+ }
382
+ if ifs.ElseStatement == child && child.Kind != shimast.KindBlock &&
383
+ child.Kind != shimast.KindIfStatement {
384
+ return true
385
+ }
386
+ case shimast.KindWhileStatement:
387
+ if parent.AsWhileStatement().Statement == child &&
388
+ child.Kind != shimast.KindBlock {
389
+ return true
390
+ }
391
+ case shimast.KindForStatement:
392
+ if parent.AsForStatement().Statement == child &&
393
+ child.Kind != shimast.KindBlock {
394
+ return true
395
+ }
396
+ case shimast.KindForInStatement, shimast.KindForOfStatement:
397
+ if parent.AsForInOrOfStatement().Statement == child &&
398
+ child.Kind != shimast.KindBlock {
399
+ return true
400
+ }
401
+ }
402
+ return false
403
+ }
404
+
405
+ // cededByChainedArrowAncestor reports whether `node` sits inside the body of
406
+ // a chained arrow (`a => b => { … }`). Prettier indents a chained arrow's
407
+ // body one extra level for the chain continuation, so the body block AND
408
+ // every statement / brace nested below it sit one level deeper than the
409
+ // column-0 depth model computes; format/indent must cede all of them or it
410
+ // de-indents correct source. Walking ancestor blocks (not just the nearest)
411
+ // catches a statement deep inside the body, e.g. an `if` body two levels
412
+ // down. Purely structural (AST kinds), so a mangled input cannot fool it,
413
+ // and it does NOT match a class method body, a switch case body, a
414
+ // single-head arrow body, or a multi-line-condition `if` body, those the
415
+ // depth model places correctly.
416
+ //
417
+ // `node` may be a statement (statement pass) or a Block (closing-brace
418
+ // pass). The walk starts at `node` itself so a Block argument's own
419
+ // chained-arrow-body-ness is tested (its `}` must cede too), then climbs
420
+ // ancestors for a statement nested deeper inside the body.
421
+ func cededByChainedArrowAncestor(node *shimast.Node) bool {
422
+ for n := node; n != nil; n = n.Parent {
423
+ if n.Kind == shimast.KindSourceFile {
424
+ return false
425
+ }
426
+ if n.Kind == shimast.KindBlock {
427
+ arrow := n.Parent
428
+ if arrow != nil && arrow.Kind == shimast.KindArrowFunction &&
429
+ arrow.Parent != nil && arrow.Parent.Kind == shimast.KindArrowFunction {
430
+ return true
431
+ }
432
+ }
433
+ }
434
+ return false
435
+ }
436
+
437
+ // forEachIndentFrame walks the file's AST once and fires two callbacks at
438
+ // the nodes/depths the former forEachBlockClose and forEachIndentHeader
439
+ // descents fired, fused into a single ForEachChild descent that tracks
440
+ // depth once. The two former walks computed an identical childDepth for
441
+ // every child kind and only differed in which callback they fired on which
442
+ // node (the brace surface and the header surface never overlap on one
443
+ // node), so one descent firing both is exactly the two passes combined.
444
+ //
445
+ // - brace(owner, ownerDepth): fired for every Block / ModuleBlock /
446
+ // CaseBlock / class / interface / type-literal owner with the depth
447
+ // its closing `}` aligns to (one level shallower than the owner's own
448
+ // statements). A case-body Block fires at depth-1 so its `}` aligns
449
+ // with the `case` label one level up. It mirrors walkStatementLists's
450
+ // depth model so all indent passes agree.
451
+ // - header(node, depth): fired for every class/interface/type-literal
452
+ // member declaration and every case/default label with the depth its
453
+ // header line should align to. A class/interface/type-literal body is
454
+ // a +1 frame and its member headers sit at that body depth; a switch's
455
+ // CaseBlock is a +1 frame and each case/default label sits at that
456
+ // CaseBlock depth (its body statements nest one deeper, handled by the
457
+ // statement pass).
458
+ func forEachIndentFrame(
459
+ file *shimast.SourceFile,
460
+ brace func(block *shimast.Node, ownerDepth int),
461
+ header func(node *shimast.Node, depth int),
462
+ ) {
463
+ if file == nil {
464
+ return
465
+ }
466
+ walkIndentFrames(file.AsNode(), file.Text(), 0, brace, header)
467
+ }
468
+
469
+ func walkIndentFrames(
470
+ node *shimast.Node,
471
+ src string,
472
+ depth int,
473
+ brace func(block *shimast.Node, ownerDepth int),
474
+ header func(node *shimast.Node, depth int),
475
+ ) {
476
+ if node == nil {
477
+ return
478
+ }
479
+ node.ForEachChild(func(child *shimast.Node) bool {
480
+ if child == nil {
481
+ return false
482
+ }
483
+ childDepth := depth
484
+ switch child.Kind {
485
+ case shimast.KindBlock, shimast.KindModuleBlock:
486
+ // Only a SAME-LINE case-body block (`case X: { … }`) adds no extra
487
+ // level; a block on its own line under the clause is an ordinary
488
+ // nested block (see blockStartsOwnLine).
489
+ isCaseBody := child.Kind == shimast.KindBlock && child.Parent != nil &&
490
+ (child.Parent.Kind == shimast.KindCaseClause ||
491
+ child.Parent.Kind == shimast.KindDefaultClause) &&
492
+ !blockStartsOwnLine(src, child)
493
+ if isCaseBody {
494
+ // A same-line case-body block (`case X: { … }`) adds no extra level
495
+ // for its statements (they stay at the clause body depth, this
496
+ // `depth`), but its own closing `}` aligns with the `case` label one
497
+ // level up.
498
+ brace(child, depth-1)
499
+ childDepth = depth
500
+ } else {
501
+ // The block's `}` aligns to the owner depth (this `depth`); its
502
+ // statements nest one deeper.
503
+ brace(child, depth)
504
+ childDepth = depth + 1
505
+ }
506
+ case shimast.KindCaseClause, shimast.KindDefaultClause:
507
+ // The label (`case X:` / `default:`) sits at the current (CaseBlock)
508
+ // depth; its body statements nest one deeper.
509
+ header(child, depth)
510
+ childDepth = depth + 1
511
+ case shimast.KindCaseBlock,
512
+ shimast.KindClassDeclaration,
513
+ shimast.KindClassExpression,
514
+ shimast.KindInterfaceDeclaration,
515
+ shimast.KindTypeLiteral:
516
+ // These carry a real closing `}` that is not a Block node: a switch's
517
+ // `}` (CaseBlock), a class/interface/type-literal body `}`. Align it to
518
+ // the depth the owner sits at (this `depth`); the members/clauses nest
519
+ // one deeper.
520
+ brace(child, depth)
521
+ childDepth = depth + 1
522
+ case shimast.KindObjectLiteralExpression:
523
+ // Object-literal braces live in expression position (assignments,
524
+ // arguments); their layout is the printer's / source's, so leave the
525
+ // `}` alone and only descend one level for any nested statement lists.
526
+ childDepth = depth + 1
527
+ case shimast.KindMethodDeclaration,
528
+ shimast.KindPropertyDeclaration,
529
+ shimast.KindGetAccessor,
530
+ shimast.KindSetAccessor,
531
+ shimast.KindConstructor,
532
+ shimast.KindMethodSignature,
533
+ shimast.KindPropertySignature,
534
+ shimast.KindIndexSignature:
535
+ // A class/interface/type-literal member: its header line sits at the
536
+ // current body depth (the enclosing frame already bumped it).
537
+ header(child, depth)
538
+ }
539
+ walkIndentFrames(child, src, childDepth, brace, header)
540
+ return false
541
+ })
542
+ }
543
+
544
+ // reindentHeaderLine normalizes the leading whitespace of the physical line
545
+ // containing `pos` to `want`, appending an edit only when the line begins
546
+ // with `pos` as its first non-whitespace byte and the run differs. Shared by
547
+ // the decorator line and the declaration line of a decorated member so both
548
+ // land at the member's nesting depth. A no-op when the run already equals
549
+ // `want`, which keeps the rule idempotent.
550
+ func reindentHeaderLine(src string, pos int, want string, edits *[]TextEdit) {
551
+ if pos < 0 || pos > len(src) {
552
+ return
553
+ }
554
+ lineStart := lineStartOffset(src, pos)
555
+ for i := lineStart; i < pos; i++ {
556
+ if src[i] != ' ' && src[i] != '\t' {
557
+ return
558
+ }
559
+ }
560
+ if src[lineStart:pos] == want {
561
+ return
562
+ }
563
+ *edits = append(*edits, TextEdit{Pos: lineStart, End: pos, Text: want})
564
+ }
565
+
566
+ // memberDeclarationStart returns the byte offset where a decorated member's
567
+ // actual declaration begins (just past its last leading decorator), or -1
568
+ // when the member has no decorators. The declaration start is the first
569
+ // non-trivia byte after the final decorator's End(). When the last decorator
570
+ // and the declaration share a physical line (`@Column() name: string`), the
571
+ // returned offset lands on the same line the decorator pass already handled,
572
+ // so the caller's reindentHeaderLine emits nothing the second time; only a
573
+ // decorator alone on its line followed by the declaration on a later line
574
+ // produces a distinct line to re-indent.
575
+ func memberDeclarationStart(src string, member *shimast.Node) int {
576
+ if member == nil {
577
+ return -1
578
+ }
579
+ decorators := member.Decorators()
580
+ if len(decorators) == 0 {
581
+ return -1
582
+ }
583
+ last := decorators[len(decorators)-1]
584
+ if last == nil {
585
+ return -1
586
+ }
587
+ start := shimscanner.SkipTrivia(src, last.End())
588
+ if start < 0 || start > len(src) {
589
+ return -1
590
+ }
591
+ return start
592
+ }
593
+
594
+ // blockCloseBracePos returns the byte offset of a block's closing `}`.
595
+ // A block's End() points just past the `}`, so the brace is the last
596
+ // non-whitespace byte before End(); returns -1 if it is not a `}`.
597
+ func blockCloseBracePos(src string, block *shimast.Node) int {
598
+ end := block.End()
599
+ if end <= 0 || end > len(src) {
600
+ return -1
601
+ }
602
+ for i := end - 1; i >= 0; i-- {
603
+ c := src[i]
604
+ if c == '}' {
605
+ return i
606
+ }
607
+ if c != ' ' && c != '\t' && c != '\n' && c != '\r' {
608
+ return -1
609
+ }
610
+ }
611
+ return -1
612
+ }
613
+
614
+ func init() {
615
+ Register(formatIndent{})
616
+ }
@@ -19,7 +19,7 @@ import (
19
19
  // - @virtual → @abstract
20
20
  // - @func, @method → @function
21
21
  //
22
- // JSDoc tags only fire when the `@` is at the start of a JSDoc line
22
+ // JSDoc tags only fire when the `@` is at the start of a JSDoc line,
23
23
  // preceded by `*`, whitespace, or a newline. Inline `@foo` references in
24
24
  // prose text are left alone. The rule scans source bytes directly inside
25
25
  // `/** ... */` blocks; it deliberately avoids relying on the JSDoc AST
@@ -65,7 +65,7 @@ func (formatJSDoc) Check(ctx *Context, node *shimast.Node) {
65
65
  }
66
66
  for k, v := range opts.TagSynonyms {
67
67
  // Reject empty canonicals or canonicals carrying non-identifier
68
- // bytes the fixer would otherwise emit malformed JSDoc like
68
+ // bytes, the fixer would otherwise emit malformed JSDoc like
69
69
  // `@` or `@my tag`. Silently dropping the bad entry is the right
70
70
  // failure mode: the rule already runs on every file in a project
71
71
  // and a single typo'd entry should not poison every block.
@@ -82,7 +82,7 @@ func (formatJSDoc) Check(ctx *Context, node *shimast.Node) {
82
82
  // `sortTags` is reserved for a follow-up that pulls in the
83
83
  // prettier-plugin-jsdoc canonical order. The flag is parsed today so
84
84
  // the type surface freezes, but the implementation lands in a future
85
- // pass projects that opt in get a no-op until then.
85
+ // pass, projects that opt in get a no-op until then.
86
86
  _ = opts.SortTags
87
87
  }
88
88
 
@@ -122,7 +122,7 @@ func findJSDocBlocks(src string) []jsdocBlock {
122
122
  if !(src[start] == '/' && src[start+1] == '*' && src[start+2] == '*') {
123
123
  continue
124
124
  }
125
- // Skip `/**/` an empty doc block has no tags to rewrite.
125
+ // Skip `/**/`, an empty doc block has no tags to rewrite.
126
126
  if end-start == 4 && src[start+3] == '/' {
127
127
  continue
128
128
  }