@ttsc/lint 0.13.0 → 0.14.0-dev.20250530.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/README.md +955 -85
  2. package/lib/defaultFormat.d.ts +15 -8
  3. package/lib/defaultFormat.js +15 -8
  4. package/lib/defaultFormat.js.map +1 -1
  5. package/lib/index.d.ts +46 -0
  6. package/lib/index.js +60 -37
  7. package/lib/index.js.map +1 -1
  8. package/lib/structures/ITtscLintConfig.d.ts +12 -6
  9. package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
  10. package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
  11. package/lib/structures/TtscLintRuleSetting.js.map +1 -0
  12. package/lib/structures/format/ITtscLintFormat.d.ts +128 -0
  13. package/lib/structures/{TtscLintRuleMap.js → format/ITtscLintFormat.js} +1 -1
  14. package/lib/structures/format/ITtscLintFormat.js.map +1 -0
  15. package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +17 -0
  16. package/lib/structures/{ITtscLintFormatConfig.js → format/ITtscLintFormatJsDoc.js} +1 -1
  17. package/lib/structures/format/ITtscLintFormatJsDoc.js.map +1 -0
  18. package/lib/structures/format/ITtscLintFormatSortImports.d.ts +54 -0
  19. package/lib/structures/format/ITtscLintFormatSortImports.js +3 -0
  20. package/lib/structures/format/ITtscLintFormatSortImports.js.map +1 -0
  21. package/lib/structures/format/index.d.ts +3 -0
  22. package/lib/structures/format/index.js +20 -0
  23. package/lib/structures/format/index.js.map +1 -0
  24. package/lib/structures/index.d.ts +3 -4
  25. package/lib/structures/index.js +3 -4
  26. package/lib/structures/index.js.map +1 -1
  27. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +95 -0
  28. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +13 -0
  29. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
  30. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +75 -0
  31. package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
  32. package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
  33. package/lib/structures/rules/ITtscLintContributorRules.d.ts +16 -0
  34. package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
  35. package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
  36. package/lib/structures/rules/ITtscLintCoreRules.d.ts +1229 -0
  37. package/lib/structures/rules/ITtscLintCoreRules.js +3 -0
  38. package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
  39. package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
  40. package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
  41. package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
  42. package/lib/structures/rules/ITtscLintCypressRules.d.ts +125 -0
  43. package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
  44. package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
  45. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +154 -0
  46. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
  47. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
  48. package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +162 -0
  49. package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
  50. package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
  51. package/lib/structures/rules/ITtscLintJestRules.d.ts +190 -0
  52. package/lib/structures/rules/ITtscLintJestRules.js +3 -0
  53. package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
  54. package/lib/structures/rules/ITtscLintJsdocRules.d.ts +133 -0
  55. package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
  56. package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
  57. package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +294 -0
  58. package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
  59. package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
  60. package/lib/structures/rules/ITtscLintNextjsRules.d.ts +180 -0
  61. package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
  62. package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
  63. package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +312 -0
  64. package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
  65. package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
  66. package/lib/structures/rules/ITtscLintPromiseRules.d.ts +179 -0
  67. package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
  68. package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
  69. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
  70. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
  71. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
  72. package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +49 -0
  73. package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
  74. package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
  75. package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +37 -0
  76. package/lib/structures/rules/ITtscLintReactRuleOptions.js +11 -0
  77. package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
  78. package/lib/structures/rules/ITtscLintReactRules.d.ts +276 -0
  79. package/lib/structures/rules/ITtscLintReactRules.js +3 -0
  80. package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
  81. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +182 -0
  82. package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
  83. package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
  84. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +65 -0
  85. package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
  86. package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
  87. package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
  88. package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
  89. package/lib/structures/rules/ITtscLintRules.js.map +1 -0
  90. package/lib/structures/rules/ITtscLintSecurityRules.d.ts +141 -0
  91. package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
  92. package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
  93. package/lib/structures/rules/ITtscLintSolidRules.d.ts +178 -0
  94. package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
  95. package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
  96. package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +20 -0
  97. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
  98. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
  99. package/lib/structures/rules/ITtscLintStorybookRules.d.ts +165 -0
  100. package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
  101. package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
  102. package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +88 -0
  103. package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
  104. package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
  105. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +21 -0
  106. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
  107. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
  108. package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +293 -0
  109. package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
  110. package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
  111. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1074 -0
  112. package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
  113. package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
  114. package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
  115. package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
  116. package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
  117. package/lib/structures/rules/ITtscLintVitestRules.d.ts +133 -0
  118. package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
  119. package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
  120. package/lib/structures/rules/index.d.ts +31 -0
  121. package/lib/structures/rules/index.js +48 -0
  122. package/lib/structures/rules/index.js.map +1 -0
  123. package/linthost/ast_helpers.go +30 -5
  124. package/linthost/compile.go +33 -2
  125. package/linthost/config.go +118 -71
  126. package/linthost/config_format.go +270 -104
  127. package/linthost/directives.go +67 -25
  128. package/linthost/dispatch.go +14 -3
  129. package/linthost/engine.go +160 -40
  130. package/linthost/fix.go +10 -3
  131. package/linthost/flags_gen.go +17 -15
  132. package/linthost/format.go +222 -25
  133. package/linthost/format_editor_settings.go +262 -0
  134. package/linthost/host.go +40 -3
  135. package/linthost/lsp.go +898 -0
  136. package/linthost/print_dispatch.go +5 -2
  137. package/linthost/print_engine.go +5 -5
  138. package/linthost/print_nodes_call.go +149 -67
  139. package/linthost/print_nodes_function.go +27 -2
  140. package/linthost/print_nodes_list.go +40 -5
  141. package/linthost/print_nodes_object.go +1 -1
  142. package/linthost/print_nodes_ternary.go +86 -0
  143. package/linthost/rule_names.go +14 -0
  144. package/linthost/rules_arrays.go +2 -2
  145. package/linthost/rules_boundaries.go +532 -0
  146. package/linthost/rules_camelcase.go +133 -0
  147. package/linthost/rules_complexity.go +95 -0
  148. package/linthost/rules_consistent_return.go +130 -0
  149. package/linthost/rules_console.go +1 -1
  150. package/linthost/rules_core_extra.go +739 -0
  151. package/linthost/rules_curly.go +70 -0
  152. package/linthost/rules_cypress.go +608 -0
  153. package/linthost/rules_debugger.go +2 -2
  154. package/linthost/rules_default_case.go +39 -0
  155. package/linthost/rules_default_case_last.go +46 -0
  156. package/linthost/rules_dupes.go +3 -3
  157. package/linthost/rules_empty.go +3 -3
  158. package/linthost/rules_escape.go +44 -24
  159. package/linthost/rules_eval.go +2 -2
  160. package/linthost/rules_finally.go +2 -2
  161. package/linthost/rules_format_arrow_parens.go +167 -0
  162. package/linthost/rules_format_bracket_spacing.go +117 -0
  163. package/linthost/rules_format_clause_join.go +172 -0
  164. package/linthost/rules_format_declaration_header.go +595 -0
  165. package/linthost/rules_format_indent.go +372 -0
  166. package/linthost/rules_format_jsdoc.go +5 -5
  167. package/linthost/rules_format_orphan_semi.go +106 -0
  168. package/linthost/rules_format_parameter_properties.go +175 -0
  169. package/linthost/rules_format_print_width.go +66 -55
  170. package/linthost/rules_format_quote_props.go +169 -0
  171. package/linthost/rules_format_quotes.go +76 -30
  172. package/linthost/rules_format_semi.go +186 -11
  173. package/linthost/rules_format_sort_imports.go +437 -118
  174. package/linthost/rules_format_statement_split.go +343 -0
  175. package/linthost/rules_format_ternary_nullish_parens.go +77 -0
  176. package/linthost/rules_format_trailing_comma.go +22 -3
  177. package/linthost/rules_format_whitespace.go +336 -0
  178. package/linthost/rules_functional.go +967 -0
  179. package/linthost/rules_gap.go +24 -45
  180. package/linthost/rules_grouped_accessor_pairs.go +87 -0
  181. package/linthost/rules_guard_for_in.go +198 -0
  182. package/linthost/rules_id_length.go +77 -0
  183. package/linthost/rules_imports.go +70 -2
  184. package/linthost/rules_init_declarations.go +53 -0
  185. package/linthost/rules_jest.go +618 -0
  186. package/linthost/rules_jsdoc.go +502 -0
  187. package/linthost/rules_jsx_a11y.go +1264 -0
  188. package/linthost/rules_logic.go +12 -12
  189. package/linthost/rules_loops.go +1 -1
  190. package/linthost/rules_max_classes_per_file.go +58 -0
  191. package/linthost/rules_max_depth.go +83 -0
  192. package/linthost/rules_max_lines.go +101 -0
  193. package/linthost/rules_max_lines_per_function.go +57 -0
  194. package/linthost/rules_max_nested_callbacks.go +61 -0
  195. package/linthost/rules_max_params.go +49 -0
  196. package/linthost/rules_max_statements.go +59 -0
  197. package/linthost/rules_misc.go +1 -1
  198. package/linthost/rules_nextjs.go +697 -0
  199. package/linthost/rules_no_else_return.go +75 -0
  200. package/linthost/rules_no_empty_named_blocks.go +91 -0
  201. package/linthost/rules_no_extend_native.go +51 -0
  202. package/linthost/rules_no_import_assign.go +140 -0
  203. package/linthost/rules_no_invalid_this.go +52 -0
  204. package/linthost/rules_no_loop_func.go +85 -0
  205. package/linthost/rules_no_magic_numbers.go +123 -0
  206. package/linthost/rules_no_mixed_operators.go +80 -0
  207. package/linthost/rules_no_param_reassign.go +98 -0
  208. package/linthost/rules_no_redeclare.go +270 -0
  209. package/linthost/rules_no_restricted_imports.go +105 -0
  210. package/linthost/rules_no_restricted_syntax.go +49 -0
  211. package/linthost/rules_no_shadow.go +147 -0
  212. package/linthost/rules_no_unreachable.go +77 -0
  213. package/linthost/rules_no_useless_assignment.go +120 -0
  214. package/linthost/rules_no_useless_constructor.go +245 -0
  215. package/linthost/rules_no_useless_return.go +51 -0
  216. package/linthost/rules_params.go +1 -1
  217. package/linthost/rules_playwright.go +788 -0
  218. package/linthost/rules_prefer_arrow_callback.go +124 -0
  219. package/linthost/rules_prefer_destructuring.go +98 -0
  220. package/linthost/rules_prefer_named_capture_group.go +129 -0
  221. package/linthost/rules_prefer_rest_params.go +94 -0
  222. package/linthost/rules_problems.go +41 -30
  223. package/linthost/rules_promise.go +1057 -2
  224. package/linthost/rules_protos.go +2 -2
  225. package/linthost/rules_react.go +773 -0
  226. package/linthost/rules_react_extras.go +227 -0
  227. package/linthost/rules_react_hooks.go +848 -0
  228. package/linthost/rules_react_perf.go +293 -0
  229. package/linthost/rules_react_refresh.go +583 -0
  230. package/linthost/rules_regexp.go +410 -0
  231. package/linthost/rules_security.go +830 -0
  232. package/linthost/rules_self.go +2 -2
  233. package/linthost/rules_solid.go +1073 -0
  234. package/linthost/rules_sort_imports.go +69 -0
  235. package/linthost/rules_sort_keys.go +134 -0
  236. package/linthost/rules_storybook.go +1176 -0
  237. package/linthost/rules_strings.go +4 -4
  238. package/linthost/rules_suggestions.go +38 -38
  239. package/linthost/rules_tanstack_query.go +844 -0
  240. package/linthost/rules_testing_library.go +1204 -0
  241. package/linthost/rules_throw.go +1 -1
  242. package/linthost/rules_ts.go +19 -19
  243. package/linthost/rules_ts_async.go +399 -0
  244. package/linthost/rules_ts_class_literal_property_style.go +115 -0
  245. package/linthost/rules_ts_consistent_type_exports.go +256 -0
  246. package/linthost/rules_ts_explicit_function_return_type.go +67 -0
  247. package/linthost/rules_ts_extra.go +159 -36
  248. package/linthost/rules_ts_no_base_to_string.go +218 -0
  249. package/linthost/rules_ts_no_confusing_void.go +72 -0
  250. package/linthost/rules_ts_no_deprecated.go +195 -0
  251. package/linthost/rules_ts_no_for_in_array.go +84 -0
  252. package/linthost/rules_ts_no_magic_numbers.go +124 -0
  253. package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
  254. package/linthost/rules_ts_no_misused_spread.go +104 -0
  255. package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
  256. package/linthost/rules_ts_no_restricted_types.go +62 -0
  257. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
  258. package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
  259. package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
  260. package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
  261. package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
  262. package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
  263. package/linthost/rules_ts_no_unsafe_argument.go +82 -0
  264. package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
  265. package/linthost/rules_ts_no_unsafe_call.go +82 -0
  266. package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
  267. package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
  268. package/linthost/rules_ts_no_unsafe_return.go +126 -0
  269. package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
  270. package/linthost/rules_ts_no_useless_constructor.go +52 -0
  271. package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
  272. package/linthost/rules_ts_parameter_properties.go +38 -0
  273. package/linthost/rules_ts_prefer_find.go +178 -0
  274. package/linthost/rules_ts_prefer_includes.go +255 -0
  275. package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
  276. package/linthost/rules_ts_prefer_optional_chain.go +228 -0
  277. package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
  278. package/linthost/rules_ts_prefer_readonly.go +73 -0
  279. package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
  280. package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
  281. package/linthost/rules_ts_prefer_return_this_type.go +135 -0
  282. package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
  283. package/linthost/rules_ts_promise_function_async.go +77 -0
  284. package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
  285. package/linthost/rules_ts_require_array_sort_compare.go +96 -0
  286. package/linthost/rules_ts_restrict_plus_operands.go +127 -0
  287. package/linthost/rules_ts_restrict_template_expressions.go +95 -0
  288. package/linthost/rules_ts_simple.go +387 -0
  289. package/linthost/rules_ts_sort_type_constituents.go +144 -0
  290. package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
  291. package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
  292. package/linthost/rules_ts_unbound_method.go +126 -0
  293. package/linthost/rules_tsdoc.go +136 -0
  294. package/linthost/rules_unicorn_better_regex.go +27 -0
  295. package/linthost/rules_unicorn_catch_error_name.go +46 -0
  296. package/linthost/rules_unicorn_consistent_assert.go +47 -0
  297. package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
  298. package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
  299. package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
  300. package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
  301. package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
  302. package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
  303. package/linthost/rules_unicorn_custom_error_definition.go +138 -0
  304. package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
  305. package/linthost/rules_unicorn_error_message.go +64 -0
  306. package/linthost/rules_unicorn_escape_case.go +51 -0
  307. package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
  308. package/linthost/rules_unicorn_explicit_length_check.go +88 -0
  309. package/linthost/rules_unicorn_filename_case.go +29 -0
  310. package/linthost/rules_unicorn_import_style.go +28 -0
  311. package/linthost/rules_unicorn_isolated_functions.go +29 -0
  312. package/linthost/rules_unicorn_new_for_builtins.go +87 -0
  313. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
  314. package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
  315. package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
  316. package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
  317. package/linthost/rules_unicorn_no_array_for_each.go +40 -0
  318. package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
  319. package/linthost/rules_unicorn_no_array_reduce.go +41 -0
  320. package/linthost/rules_unicorn_no_array_reverse.go +44 -0
  321. package/linthost/rules_unicorn_no_array_sort.go +43 -0
  322. package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
  323. package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
  324. package/linthost/rules_unicorn_no_console_spaces.go +61 -0
  325. package/linthost/rules_unicorn_no_document_cookie.go +41 -0
  326. package/linthost/rules_unicorn_no_empty_file.go +44 -0
  327. package/linthost/rules_unicorn_no_for_loop.go +102 -0
  328. package/linthost/rules_unicorn_no_hex_escape.go +42 -0
  329. package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
  330. package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
  331. package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
  332. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
  333. package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
  334. package/linthost/rules_unicorn_no_lonely_if.go +44 -0
  335. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
  336. package/linthost/rules_unicorn_no_named_default.go +54 -0
  337. package/linthost/rules_unicorn_no_negated_condition.go +85 -0
  338. package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
  339. package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
  340. package/linthost/rules_unicorn_no_new_array.go +30 -0
  341. package/linthost/rules_unicorn_no_new_buffer.go +31 -0
  342. package/linthost/rules_unicorn_no_null.go +24 -0
  343. package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
  344. package/linthost/rules_unicorn_no_process_exit.go +43 -0
  345. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
  346. package/linthost/rules_unicorn_no_static_only_class.go +56 -0
  347. package/linthost/rules_unicorn_no_thenable.go +74 -0
  348. package/linthost/rules_unicorn_no_this_assignment.go +35 -0
  349. package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
  350. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
  351. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
  352. package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
  353. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
  354. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
  355. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
  356. package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
  357. package/linthost/rules_unicorn_no_unused_properties.go +30 -0
  358. package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
  359. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
  360. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
  361. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
  362. package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
  363. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
  364. package/linthost/rules_unicorn_no_useless_spread.go +67 -0
  365. package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
  366. package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
  367. package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
  368. package/linthost/rules_unicorn_number_literal_case.go +69 -0
  369. package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
  370. package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
  371. package/linthost/rules_unicorn_prefer_array_find.go +51 -0
  372. package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
  373. package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
  374. package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
  375. package/linthost/rules_unicorn_prefer_array_some.go +67 -0
  376. package/linthost/rules_unicorn_prefer_at.go +77 -0
  377. package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
  378. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
  379. package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
  380. package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
  381. package/linthost/rules_unicorn_prefer_code_point.go +49 -0
  382. package/linthost/rules_unicorn_prefer_date_now.go +78 -0
  383. package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
  384. package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
  385. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
  386. package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
  387. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
  388. package/linthost/rules_unicorn_prefer_event_target.go +36 -0
  389. package/linthost/rules_unicorn_prefer_export_from.go +129 -0
  390. package/linthost/rules_unicorn_prefer_global_this.go +127 -0
  391. package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
  392. package/linthost/rules_unicorn_prefer_includes.go +149 -0
  393. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
  394. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
  395. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
  396. package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
  397. package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
  398. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
  399. package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
  400. package/linthost/rules_unicorn_prefer_module.go +50 -0
  401. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
  402. package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
  403. package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
  404. package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
  405. package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
  406. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
  407. package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
  408. package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
  409. package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
  410. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
  411. package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
  412. package/linthost/rules_unicorn_prefer_set_has.go +42 -0
  413. package/linthost/rules_unicorn_prefer_set_size.go +48 -0
  414. package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
  415. package/linthost/rules_unicorn_prefer_single_call.go +96 -0
  416. package/linthost/rules_unicorn_prefer_spread.go +51 -0
  417. package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
  418. package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
  419. package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
  420. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
  421. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
  422. package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
  423. package/linthost/rules_unicorn_prefer_switch.go +103 -0
  424. package/linthost/rules_unicorn_prefer_ternary.go +71 -0
  425. package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
  426. package/linthost/rules_unicorn_prefer_type_error.go +127 -0
  427. package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
  428. package/linthost/rules_unicorn_relative_url_style.go +49 -0
  429. package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
  430. package/linthost/rules_unicorn_require_module_attributes.go +54 -0
  431. package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
  432. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
  433. package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
  434. package/linthost/rules_unicorn_string_content.go +27 -0
  435. package/linthost/rules_unicorn_switch_case_braces.go +42 -0
  436. package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
  437. package/linthost/rules_unicorn_template_indent.go +28 -0
  438. package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
  439. package/linthost/rules_unicorn_throw_new_error.go +61 -0
  440. package/linthost/rules_var.go +10 -4
  441. package/linthost/rules_vitest.go +568 -0
  442. package/package.json +3 -3
  443. package/plugin/main.go +49 -3
  444. package/rule/astutil/astutil.go +10 -3
  445. package/rule/rule.go +12 -10
  446. package/src/defaultFormat.ts +17 -10
  447. package/src/index.ts +65 -43
  448. package/src/structures/ITtscLintConfig.ts +14 -8
  449. package/src/structures/TtscLintRuleSetting.ts +55 -0
  450. package/src/structures/format/ITtscLintFormat.ts +141 -0
  451. package/src/structures/format/ITtscLintFormatJsDoc.ts +18 -0
  452. package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
  453. package/src/structures/format/index.ts +3 -0
  454. package/src/structures/index.ts +3 -4
  455. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +117 -0
  456. package/src/structures/rules/ITtscLintBoundariesRules.ts +91 -0
  457. package/src/structures/rules/ITtscLintContributorRules.ts +20 -0
  458. package/src/structures/rules/ITtscLintCoreRules.ts +1378 -0
  459. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
  460. package/src/structures/rules/ITtscLintCypressRules.ts +141 -0
  461. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +200 -0
  462. package/src/structures/rules/ITtscLintFunctionalRules.ts +197 -0
  463. package/src/structures/rules/ITtscLintJestRules.ts +209 -0
  464. package/src/structures/rules/ITtscLintJsdocRules.ts +146 -0
  465. package/src/structures/rules/ITtscLintJsxA11yRules.ts +331 -0
  466. package/src/structures/rules/ITtscLintNextjsRules.ts +201 -0
  467. package/src/structures/rules/ITtscLintPlaywrightRules.ts +343 -0
  468. package/src/structures/rules/ITtscLintPromiseRules.ts +196 -0
  469. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
  470. package/src/structures/rules/ITtscLintReactPerfRules.ts +53 -0
  471. package/src/structures/rules/ITtscLintReactRuleOptions.ts +41 -0
  472. package/src/structures/rules/ITtscLintReactRules.ts +308 -0
  473. package/src/structures/rules/ITtscLintRegexpRules.ts +204 -0
  474. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -0
  475. package/src/structures/rules/ITtscLintRules.ts +77 -0
  476. package/src/structures/rules/ITtscLintSecurityRules.ts +155 -0
  477. package/src/structures/rules/ITtscLintSolidRules.ts +199 -0
  478. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +22 -0
  479. package/src/structures/rules/ITtscLintStorybookRules.ts +184 -0
  480. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +96 -0
  481. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +23 -0
  482. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +325 -0
  483. package/src/structures/rules/ITtscLintTypeScriptRules.ts +1172 -0
  484. package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
  485. package/src/structures/rules/ITtscLintVitestRules.ts +146 -0
  486. package/src/structures/rules/index.ts +31 -0
  487. package/lib/structures/ITtscLintFormatConfig.d.ts +0 -153
  488. package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
  489. package/lib/structures/TtscLintRule.d.ts +0 -17
  490. package/lib/structures/TtscLintRule.js.map +0 -1
  491. package/lib/structures/TtscLintRuleMap.d.ts +0 -46
  492. package/lib/structures/TtscLintRuleMap.js.map +0 -1
  493. package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
  494. package/lib/structures/TtscLintRuleOptions.js.map +0 -1
  495. package/src/structures/ITtscLintFormatConfig.ts +0 -168
  496. package/src/structures/TtscLintRule.ts +0 -160
  497. package/src/structures/TtscLintRuleMap.ts +0 -55
  498. package/src/structures/TtscLintRuleOptions.ts +0 -151
@@ -3,13 +3,14 @@ package linthost
3
3
  import (
4
4
  "encoding/json"
5
5
  "fmt"
6
+ "strings"
6
7
  )
7
8
 
8
9
  // expandFormatBlock translates a Prettier-style `format` block into the
9
10
  // per-rule severity + options shape the existing rule parsers consume.
10
11
  // The result is a `map[string]any` that mirrors what a user would
11
- // have written under `rules` directly — entries like
12
- // `"format/semi": ["off", {"prefer": "always"}]` — so the caller
12
+ // have written under `rules` directly, entries like
13
+ // `"format/semi": ["off", {"prefer": "always"}]`, so the caller
13
14
  // can route it through either `ParseRulesWithOptions` or
14
15
  // `parseExternalRuleMapInto` without duplicating option-decoding logic.
15
16
  //
@@ -20,16 +21,27 @@ import (
20
21
  //
21
22
  // The block's enablement matrix:
22
23
  //
23
- // - `format/semi` — always on. `semi: false` flips to `prefer: "never"`.
24
- // - `format/quotes` — always on. `singleQuote: true` flips to `prefer: "single"`.
25
- // - `format/trailing-comma` — always on with the requested mode.
26
- // - `format/print-width` — always on, driven by printWidth/tabWidth/useTabs/endOfLine.
27
- // - `format/sort-imports` — opt-in by setting `importOrder`.
28
- // - `format/jsdoc` — opt-in by setting `jsdoc` truthy.
24
+ // - `format/semi`, always on. `semi: false` flips to `prefer: "never"`.
25
+ // - `format/quotes`, always on. `singleQuote: true` flips to `prefer: "single"`.
26
+ // - `format/arrow-parens`, always on. `arrowParens: "avoid"` strips a single bare-identifier arrow parameter's parens; default "always" adds them.
27
+ // - `format/bracket-spacing`, always on. `bracketSpacing: false` removes the inner space of single-line object/destructure/import/export/type braces; default true keeps it.
28
+ // - `format/quote-props`, always on. `quoteProps: "as-needed"` (default) unquotes identifier object keys; "consistent" keeps all keys quoted when any needs it; "preserve" leaves quoting alone.
29
+ // - `format/trailing-comma`, always on with the requested mode.
30
+ // - `format/print-width`, always on, driven by printWidth/tabWidth/useTabs/endOfLine.
31
+ // - `format/clause-join`, always on, joins a single-statement clause body that fits printWidth.
32
+ // - `format/declaration-header`, always on, reflows a class/interface header's type params and heritage clauses.
33
+ // - `format/ternary-nullish-parens`, always on, parenthesizes a `??` operand of a conditional expression.
34
+ // - `format/orphan-semi`, always on; under semi:false, merges a leading-semicolon ASI guard onto its statement.
35
+ // - `format/parameter-properties`, always on, breaks a constructor's parameter list when it has parameter properties.
36
+ // - `format/statement-split`, always on, driven by tabWidth/useTabs/endOfLine.
37
+ // - `format/indent`, always on, driven by tabWidth/useTabs/endOfLine.
38
+ // - `format/whitespace`, always on, driven by endOfLine.
39
+ // - `format/sort-imports`, opt-in by setting `sortImports`.
40
+ // - `format/jsdoc`, always-on; `jsDoc: false` opts out, an object customizes.
29
41
  //
30
42
  // The returned map is the raw form rules parsers expect. Callers MUST
31
43
  // merge any user-supplied `rules` map on top of this one (rules-wins
32
- // semantics) before invoking the existing parsers — `mergeRuleMaps`
44
+ // semantics) before invoking the existing parsers, `mergeRuleMaps`
33
45
  // below performs the merge with the right precedence.
34
46
  func expandFormatBlock(raw map[string]any) (map[string]any, error) {
35
47
  if raw == nil {
@@ -47,7 +59,7 @@ func expandFormatBlock(raw map[string]any) (map[string]any, error) {
47
59
  }
48
60
  out := map[string]any{}
49
61
 
50
- // format/semi
62
+ // formatSemi
51
63
  semiPrefer := "always"
52
64
  if v, ok := raw["semi"]; ok {
53
65
  b, err := asBool("format.semi", v)
@@ -60,7 +72,11 @@ func expandFormatBlock(raw map[string]any) (map[string]any, error) {
60
72
  }
61
73
  out["format/semi"] = ruleEntry(map[string]any{"prefer": semiPrefer})
62
74
 
63
- // format/quotes
75
+ // formatOrphanSemi, always on; mirrors the effective semi setting so
76
+ // it only merges the leading-semicolon ASI guard under semi:false.
77
+ out["format/orphan-semi"] = ruleEntry(map[string]any{"semi": semiPrefer == "always"})
78
+
79
+ // formatQuotes
64
80
  quotesPrefer := "double"
65
81
  if v, ok := raw["singleQuote"]; ok {
66
82
  b, err := asBool("format.singleQuote", v)
@@ -73,7 +89,57 @@ func expandFormatBlock(raw map[string]any) (map[string]any, error) {
73
89
  }
74
90
  out["format/quotes"] = ruleEntry(map[string]any{"prefer": quotesPrefer})
75
91
 
76
- // format/trailing-comma
92
+ // formatArrowParens, always on. Mirrors Prettier's arrowParens; the
93
+ // default "always" parenthesizes a single bare-identifier arrow parameter,
94
+ // "avoid" strips the parens.
95
+ arrowPrefer := "always"
96
+ if v, ok := raw["arrowParens"]; ok {
97
+ s, err := asString("format.arrowParens", v)
98
+ if err != nil {
99
+ return nil, err
100
+ }
101
+ switch s {
102
+ case "always", "avoid":
103
+ arrowPrefer = s
104
+ default:
105
+ return nil, fmt.Errorf("@ttsc/lint: format.arrowParens must be \"always\" or \"avoid\"; got %q", s)
106
+ }
107
+ }
108
+ out["format/arrow-parens"] = ruleEntry(map[string]any{"prefer": arrowPrefer})
109
+
110
+ // formatBracketSpacing, always on. Mirrors Prettier's bracketSpacing;
111
+ // the default true pads single-line object/destructure/import/export/type
112
+ // braces with one inner space, false removes it.
113
+ bracketSpacing := true
114
+ if v, ok := raw["bracketSpacing"]; ok {
115
+ b, err := asBool("format.bracketSpacing", v)
116
+ if err != nil {
117
+ return nil, err
118
+ }
119
+ bracketSpacing = b
120
+ }
121
+ out["format/bracket-spacing"] = ruleEntry(map[string]any{"spacing": bracketSpacing})
122
+
123
+ // formatQuoteProps, always on. Mirrors Prettier's quoteProps; the default
124
+ // "as-needed" unquotes object keys that are valid identifiers, "consistent"
125
+ // keeps every key quoted when any one needs it, "preserve" leaves quoting
126
+ // untouched.
127
+ quoteProps := "as-needed"
128
+ if v, ok := raw["quoteProps"]; ok {
129
+ s, err := asString("format.quoteProps", v)
130
+ if err != nil {
131
+ return nil, err
132
+ }
133
+ switch s {
134
+ case "as-needed", "consistent", "preserve":
135
+ quoteProps = s
136
+ default:
137
+ return nil, fmt.Errorf("@ttsc/lint: format.quoteProps must be \"as-needed\", \"consistent\", or \"preserve\"; got %q", s)
138
+ }
139
+ }
140
+ out["format/quote-props"] = ruleEntry(map[string]any{"mode": quoteProps})
141
+
142
+ // formatTrailingComma
77
143
  tcMode := "all"
78
144
  if v, ok := raw["trailingComma"]; ok {
79
145
  s, err := asString("format.trailingComma", v)
@@ -89,15 +155,20 @@ func expandFormatBlock(raw map[string]any) (map[string]any, error) {
89
155
  }
90
156
  out["format/trailing-comma"] = ruleEntry(map[string]any{"mode": tcMode})
91
157
 
92
- // format/print-width
158
+ // formatPrintWidth
93
159
  //
94
160
  // `trailingComma` is mirrored into the print-width rule's options so
95
161
  // the printer's broken-list reflow emits the same trailing-comma
96
162
  // shape `format/trailing-comma` does. Without the mirror the two
97
163
  // rules disagree on `es5` / `none` projects and oscillate on every
98
- // cascade pass — the trailing-comma rule says "no comma" while the
164
+ // cascade pass, the trailing-comma rule says "no comma" while the
99
165
  // printer adds one back. See `printArgList` in print_nodes_call.go.
100
- pwOpts := map[string]any{"trailingComma": tcMode}
166
+ layoutOpts, err := collectLayoutOpts(raw)
167
+ if err != nil {
168
+ return nil, err
169
+ }
170
+ pwOpts := cloneStringAnyMap(layoutOpts)
171
+ pwOpts["trailingComma"] = tcMode
101
172
  if v, ok := raw["printWidth"]; ok {
102
173
  n, err := asInt("format.printWidth", v)
103
174
  if err != nil {
@@ -108,116 +179,153 @@ func expandFormatBlock(raw map[string]any) (map[string]any, error) {
108
179
  }
109
180
  pwOpts["printWidth"] = n
110
181
  }
111
- if v, ok := raw["tabWidth"]; ok {
112
- n, err := asInt("format.tabWidth", v)
113
- if err != nil {
114
- return nil, err
115
- }
116
- if n < 1 {
117
- return nil, fmt.Errorf("@ttsc/lint: format.tabWidth must be a positive integer; got %d", n)
118
- }
119
- pwOpts["tabWidth"] = n
120
- }
121
- if v, ok := raw["useTabs"]; ok {
122
- b, err := asBool("format.useTabs", v)
123
- if err != nil {
124
- return nil, err
125
- }
126
- pwOpts["useTabs"] = b
127
- }
128
- if v, ok := raw["endOfLine"]; ok {
129
- s, err := asString("format.endOfLine", v)
130
- if err != nil {
131
- return nil, err
132
- }
133
- if s != "lf" && s != "crlf" {
134
- return nil, fmt.Errorf("@ttsc/lint: format.endOfLine must be \"lf\" or \"crlf\"; got %q", s)
135
- }
136
- pwOpts["endOfLine"] = s
137
- }
138
182
  out["format/print-width"] = ruleEntry(pwOpts)
139
183
 
140
- // format/sort-imports — opt-in by `importOrder`.
141
- if v, ok := raw["importOrder"]; ok {
142
- siOpts := map[string]any{}
143
- order, err := asStringSlice("format.importOrder", v)
184
+ // formatClauseJoin, always on. Reuses the printWidth/tabWidth/useTabs
185
+ // budget so it only joins a single-statement clause body back onto its
186
+ // header when the joined line fits. A distinct map instance so it does
187
+ // not alias the print-width blob.
188
+ out["format/clause-join"] = ruleEntry(cloneStringAnyMap(pwOpts))
189
+
190
+ // formatDeclarationHeader, always on. Reflows a class/interface header
191
+ // (type parameters + heritage clauses) to Prettier's break shapes;
192
+ // needs the same printWidth/tabWidth/useTabs budget.
193
+ out["format/declaration-header"] = ruleEntry(cloneStringAnyMap(pwOpts))
194
+
195
+ // formatTernaryNullishParens, always on, no options: wraps a `??`
196
+ // operand of a conditional expression in parentheses (Prettier 3).
197
+ out["format/ternary-nullish-parens"] = ruleEntry(map[string]any{})
198
+
199
+ // formatStatementSplit + formatIndent, always on, Prettier-style.
200
+ // Both reuse the indentation/EOL settings to synthesize line breaks
201
+ // and indent strings. Only the keys the user actually set are mirrored
202
+ // in (same conditional shape as print-width's pwOpts), so defaults are
203
+ // applied rule-side. The two rules share the same option surface
204
+ // collected once into layoutOpts above.
205
+ //
206
+ // Distinct map instances so the two rule entries don't alias one blob.
207
+ out["format/statement-split"] = ruleEntry(cloneStringAnyMap(layoutOpts))
208
+ out["format/indent"] = ruleEntry(cloneStringAnyMap(layoutOpts))
209
+
210
+ // formatParameterProperties, always on. Force-breaks a constructor's
211
+ // parameter list when it declares parameter properties; needs only the
212
+ // indentation settings.
213
+ out["format/parameter-properties"] = ruleEntry(cloneStringAnyMap(layoutOpts))
214
+
215
+ // formatWhitespace, always on. Needs only endOfLine for the final
216
+ // newline; indentation is irrelevant to text hygiene.
217
+ wsOpts := map[string]any{}
218
+ if v, ok := layoutOpts["endOfLine"]; ok {
219
+ wsOpts["endOfLine"] = v
220
+ }
221
+ out["format/whitespace"] = ruleEntry(wsOpts)
222
+
223
+ // formatSortImports, opt-in by `sortImports` (a boolean or options object).
224
+ if v, ok := raw["sortImports"]; ok && v != nil {
225
+ siOpts, enabled, err := expandSortImportsBlock(v)
144
226
  if err != nil {
145
227
  return nil, err
146
228
  }
147
- if len(order) == 0 {
148
- return nil, fmt.Errorf("@ttsc/lint: format.importOrder must contain at least one entry; omit the field to keep format/sort-imports off")
149
- }
150
- siOpts["importOrder"] = order
151
- if x, ok := raw["importOrderSeparation"]; ok {
152
- b, err := asBool("format.importOrderSeparation", x)
153
- if err != nil {
154
- return nil, err
155
- }
156
- siOpts["importOrderSeparation"] = b
157
- }
158
- if x, ok := raw["importOrderSortSpecifiers"]; ok {
159
- b, err := asBool("format.importOrderSortSpecifiers", x)
160
- if err != nil {
161
- return nil, err
162
- }
163
- siOpts["importOrderSortSpecifiers"] = b
164
- }
165
- if x, ok := raw["importOrderCaseInsensitive"]; ok {
166
- b, err := asBool("format.importOrderCaseInsensitive", x)
167
- if err != nil {
168
- return nil, err
169
- }
170
- siOpts["importOrderCaseInsensitive"] = b
229
+ if enabled {
230
+ out["format/sort-imports"] = ruleEntry(siOpts)
171
231
  }
172
- out["format/sort-imports"] = ruleEntry(siOpts)
173
232
  }
174
233
 
175
- // format/jsdoc — opt-in by `jsdoc` truthy (boolean or object).
176
- if v, ok := raw["jsdoc"]; ok && v != nil {
177
- jdOpts := map[string]any{}
178
- enabled := false
234
+ // formatJsdoc, always-on. `jsDoc: false` opts out; a
235
+ // `{ tagSynonyms, sortTags }` object customizes it. The config key is
236
+ // camelCased (jsDoc) to match the other multi-word keys; the emitted rule id
237
+ // stays `format/jsdoc`.
238
+ //
239
+ // Today the rule only rewrites tag synonyms (@return → @returns, ...); tag
240
+ // sorting, column alignment, and wrapping are on the roadmap. It is on by
241
+ // default so JSDoc tag names normalize without opt-in, matching the rest of
242
+ // the always-on format set.
243
+ jdOpts := map[string]any{}
244
+ jdEnabled := true
245
+ if v, ok := raw["jsDoc"]; ok && v != nil {
179
246
  switch j := v.(type) {
180
247
  case bool:
181
- enabled = j
248
+ jdEnabled = j
182
249
  case map[string]any:
183
- enabled = true
184
250
  for key, val := range j {
185
251
  switch key {
186
252
  case "tagSynonyms":
187
253
  ts, ok := val.(map[string]any)
188
254
  if !ok {
189
- return nil, fmt.Errorf("@ttsc/lint: format.jsdoc.tagSynonyms must be an object, got %T", val)
255
+ return nil, fmt.Errorf("@ttsc/lint: format.jsDoc.tagSynonyms must be an object, got %T", val)
190
256
  }
191
257
  // Element values must be strings; surface
192
258
  // typos early instead of after a downstream
193
259
  // JSON-decode failure.
194
260
  for k, v := range ts {
195
261
  if _, ok := v.(string); !ok {
196
- return nil, fmt.Errorf("@ttsc/lint: format.jsdoc.tagSynonyms[%q] must be a string, got %T", k, v)
262
+ return nil, fmt.Errorf("@ttsc/lint: format.jsDoc.tagSynonyms[%q] must be a string, got %T", k, v)
197
263
  }
198
264
  }
199
265
  jdOpts["tagSynonyms"] = ts
200
266
  case "sortTags":
201
- b, err := asBool("format.jsdoc.sortTags", val)
267
+ b, err := asBool("format.jsDoc.sortTags", val)
202
268
  if err != nil {
203
269
  return nil, err
204
270
  }
205
271
  jdOpts["sortTags"] = b
206
272
  default:
207
- return nil, fmt.Errorf("@ttsc/lint: format.jsdoc unknown key %q (allowed: tagSynonyms, sortTags)", key)
273
+ return nil, fmt.Errorf("@ttsc/lint: format.jsDoc unknown key %q (allowed: tagSynonyms, sortTags)", key)
208
274
  }
209
275
  }
210
276
  default:
211
- return nil, fmt.Errorf("@ttsc/lint: format.jsdoc must be a boolean or object, got %T", v)
212
- }
213
- if enabled {
214
- out["format/jsdoc"] = ruleEntry(jdOpts)
277
+ return nil, fmt.Errorf("@ttsc/lint: format.jsDoc must be a boolean or object, got %T", v)
215
278
  }
216
279
  }
280
+ if jdEnabled {
281
+ out["format/jsdoc"] = ruleEntry(jdOpts)
282
+ }
217
283
 
218
284
  return out, nil
219
285
  }
220
286
 
287
+ // expandSortImportsBlock translates the `format.sortImports` value (a boolean
288
+ // or an options object) into the rule's option map. The bool reports whether
289
+ // the rule is enabled: `true` and any object enable it, `false` keeps it off.
290
+ func expandSortImportsBlock(v any) (map[string]any, bool, error) {
291
+ switch sv := v.(type) {
292
+ case bool:
293
+ return map[string]any{}, sv, nil
294
+ case map[string]any:
295
+ opts := map[string]any{}
296
+ for key, val := range sv {
297
+ switch key {
298
+ case "order":
299
+ order, err := asStringSlice("format.sortImports.order", val)
300
+ if err != nil {
301
+ return nil, false, err
302
+ }
303
+ if len(order) == 0 {
304
+ return nil, false, fmt.Errorf("@ttsc/lint: format.sortImports.order must contain at least one entry; omit it to use the default order")
305
+ }
306
+ opts["order"] = order
307
+ case "caseSensitive":
308
+ b, err := asBool("format.sortImports.caseSensitive", val)
309
+ if err != nil {
310
+ return nil, false, err
311
+ }
312
+ opts["caseSensitive"] = b
313
+ case "combineTypeAndValue":
314
+ b, err := asBool("format.sortImports.combineTypeAndValue", val)
315
+ if err != nil {
316
+ return nil, false, err
317
+ }
318
+ opts["combineTypeAndValue"] = b
319
+ default:
320
+ return nil, false, fmt.Errorf("@ttsc/lint: format.sortImports unknown key %q (allowed: order, caseSensitive, combineTypeAndValue)", key)
321
+ }
322
+ }
323
+ return opts, true, nil
324
+ default:
325
+ return nil, false, fmt.Errorf("@ttsc/lint: format.sortImports must be a boolean or object, got %T", v)
326
+ }
327
+ }
328
+
221
329
  // formatBlockSeverity extracts the optional `severity` field from a format
222
330
  // block. Defaults to SeverityOff so that format rules never produce check/build
223
331
  // diagnostics unless the user explicitly opts in. SeverityOff still allows
@@ -234,12 +342,20 @@ func formatBlockSeverity(raw map[string]any) (Severity, error) {
234
342
  return severity, nil
235
343
  }
236
344
 
345
+ // isFormatRuleName reports whether `name` is a formatter rule id. Such
346
+ // rules are configured exclusively through the `format` block; a `format/*`
347
+ // key in the user's `rules` map is dropped before the merge, so the config
348
+ // layer never carries a formatter setting in two places.
349
+ func isFormatRuleName(name string) bool {
350
+ return strings.HasPrefix(name, "format/")
351
+ }
352
+
237
353
  // mergeRuleMaps overlays `overrides` on `base` and returns the merged
238
354
  // map. Identical keys in `overrides` replace the entire entry from
239
- // `base`; option objects are NOT deep-merged, which matches the
240
- // conflict-resolution policy spec: a `rules` entry that names a
241
- // `format/*` rule fully replaces the corresponding entry expanded
242
- // from the `format` block.
355
+ // `base`; option objects are NOT deep-merged. `overrides` (the user's
356
+ // `rules` map) never contains a `format/*` key, those are dropped before
357
+ // the merge, so the merge only ever layers lint-rule severities on top of
358
+ // the format block's expanded entries.
243
359
  func mergeRuleMaps(base, overrides map[string]any) map[string]any {
244
360
  out := make(map[string]any, len(base)+len(overrides))
245
361
  for k, v := range base {
@@ -251,6 +367,56 @@ func mergeRuleMaps(base, overrides map[string]any) map[string]any {
251
367
  return out
252
368
  }
253
369
 
370
+ // cloneStringAnyMap returns a shallow copy of `m`. expandFormatBlock uses
371
+ // it so `format/statement-split` and `format/indent` each receive their
372
+ // own options blob rather than aliasing one shared map, a later mutation
373
+ // or marshal of one entry then cannot leak into the other.
374
+ func cloneStringAnyMap(m map[string]any) map[string]any {
375
+ out := make(map[string]any, len(m))
376
+ for k, v := range m {
377
+ out[k] = v
378
+ }
379
+ return out
380
+ }
381
+
382
+ // collectLayoutOpts parses the shared tabWidth/useTabs/endOfLine layout
383
+ // fields from a format block once. Only keys the user actually set are
384
+ // emitted, so rule-side defaults still apply for absent fields. Both the
385
+ // print-width blob (which adds trailingComma + printWidth) and the
386
+ // statement-split/indent/parameter-properties entries derive from this
387
+ // single source so their parse-and-validate logic stays in one place.
388
+ func collectLayoutOpts(raw map[string]any) (map[string]any, error) {
389
+ layoutOpts := map[string]any{}
390
+ if v, ok := raw["tabWidth"]; ok {
391
+ n, err := asInt("format.tabWidth", v)
392
+ if err != nil {
393
+ return nil, err
394
+ }
395
+ if n < 1 {
396
+ return nil, fmt.Errorf("@ttsc/lint: format.tabWidth must be a positive integer; got %d", n)
397
+ }
398
+ layoutOpts["tabWidth"] = n
399
+ }
400
+ if v, ok := raw["useTabs"]; ok {
401
+ b, err := asBool("format.useTabs", v)
402
+ if err != nil {
403
+ return nil, err
404
+ }
405
+ layoutOpts["useTabs"] = b
406
+ }
407
+ if v, ok := raw["endOfLine"]; ok {
408
+ s, err := asString("format.endOfLine", v)
409
+ if err != nil {
410
+ return nil, err
411
+ }
412
+ if s != "lf" && s != "crlf" {
413
+ return nil, fmt.Errorf("@ttsc/lint: format.endOfLine must be \"lf\" or \"crlf\"; got %q", s)
414
+ }
415
+ layoutOpts["endOfLine"] = s
416
+ }
417
+ return layoutOpts, nil
418
+ }
419
+
254
420
  // asBool coerces a raw config value to a bool, returning a typed error on
255
421
  // failure. Used by expandFormatBlock to validate boolean format fields.
256
422
  func asBool(field string, v any) (bool, error) {
@@ -314,26 +480,26 @@ func asStringSlice(field string, v any) ([]string, error) {
314
480
 
315
481
  // rejectUnknownFormatKeys surfaces typos in top-level format-block
316
482
  // keys at the boundary rather than silently ignoring them. The
317
- // key set mirrors `ITtscLintFormatConfig` exactly.
483
+ // key set mirrors `ITtscLintFormat` exactly.
318
484
  func rejectUnknownFormatKeys(raw map[string]any) error {
319
485
  allowed := map[string]struct{}{
320
- "severity": {},
321
- "semi": {},
322
- "singleQuote": {},
323
- "trailingComma": {},
324
- "printWidth": {},
325
- "tabWidth": {},
326
- "useTabs": {},
327
- "endOfLine": {},
328
- "importOrder": {},
329
- "importOrderSeparation": {},
330
- "importOrderSortSpecifiers": {},
331
- "importOrderCaseInsensitive": {},
332
- "jsdoc": {},
486
+ "severity": {},
487
+ "semi": {},
488
+ "singleQuote": {},
489
+ "arrowParens": {},
490
+ "bracketSpacing": {},
491
+ "quoteProps": {},
492
+ "trailingComma": {},
493
+ "printWidth": {},
494
+ "tabWidth": {},
495
+ "useTabs": {},
496
+ "endOfLine": {},
497
+ "sortImports": {},
498
+ "jsDoc": {},
333
499
  }
334
500
  for key := range raw {
335
501
  if _, ok := allowed[key]; !ok {
336
- return fmt.Errorf("@ttsc/lint: format unknown key %q; see ITtscLintFormatConfig for the allowed surface", key)
502
+ return fmt.Errorf("@ttsc/lint: format unknown key %q; see ITtscLintFormat for the allowed surface", key)
337
503
  }
338
504
  }
339
505
  return nil