@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
@@ -9,50 +9,59 @@ import (
9
9
  shimscanner "github.com/microsoft/typescript-go/shim/scanner"
10
10
  )
11
11
 
12
- // thirdPartyModulesPlaceholder is the bucket that absorbs any import
13
- // whose specifier doesn't match an explicit `importOrder` regex. Mirrors
14
- // `@trivago/prettier-plugin-sort-imports`.
15
- const thirdPartyModulesPlaceholder = "<THIRD_PARTY_MODULES>"
12
+ // Group-order placeholders accepted inside the `order` option. They mirror
13
+ // `@ianvs/prettier-plugin-sort-imports`: a single declarative array expresses
14
+ // group order, blank-line separation, and special groups all by position.
15
+ const (
16
+ // builtinModulesPlaceholder buckets Node built-in modules (`fs`,
17
+ // `node:path`, `fs/promises`, ...).
18
+ builtinModulesPlaceholder = "<BUILTIN_MODULES>"
19
+ // thirdPartyModulesPlaceholder absorbs any specifier that matched no
20
+ // earlier explicit group.
21
+ thirdPartyModulesPlaceholder = "<THIRD_PARTY_MODULES>"
22
+ // typesPlaceholderPrefix marks the group of `import type` declarations.
23
+ // A regex may follow (`<TYPES>^[.]`) to scope the group to matching
24
+ // specifiers.
25
+ typesPlaceholderPrefix = "<TYPES>"
26
+ // separatorPlaceholder ("") emits one blank line at its position.
27
+ separatorPlaceholder = ""
28
+ )
16
29
 
17
- // formatSortImportsOptions mirrors `TtscLintRuleOptions.SortImports`.
30
+ // formatSortImportsOptions mirrors `ITtscLintFormatSortImports`.
18
31
  type formatSortImportsOptions struct {
19
- ImportOrder []string `json:"importOrder"`
20
- ImportOrderSeparation *bool `json:"importOrderSeparation"`
21
- ImportOrderSortSpecifiers *bool `json:"importOrderSortSpecifiers"`
22
- ImportOrderCaseInsensitive bool `json:"importOrderCaseInsensitive"`
23
- }
24
-
25
- // defaultImportOrder mirrors the two-group MVP scheme used when the user
26
- // supplies no explicit `importOrder`. External modules first
27
- // (`<THIRD_PARTY_MODULES>`), relative imports second (`^\.`). The order
28
- // matches the implicit behavior the rule shipped with before options
29
- // landed, so omitting `importOrder` produces the same output.
30
- var defaultImportOrder = []string{thirdPartyModulesPlaceholder, `^\.`}
31
-
32
- // format/sort-imports orders the file's top-level import declarations into
33
- // canonical groups and alphabetizes each group. Compatible-by-spirit with
34
- // `@trivago/prettier-plugin-sort-imports`. Groups are user-configurable via
35
- // the `importOrder` option; when omitted, the rule falls back to a
36
- // two-group scheme:
37
- //
38
- // 1. External modules (specifier does not start with `.`).
39
- // 2. Relative modules (specifier starts with `.`).
32
+ Order []string `json:"order"`
33
+ CaseSensitive bool `json:"caseSensitive"`
34
+ CombineTypeAndValue bool `json:"combineTypeAndValue"`
35
+ }
36
+
37
+ // defaultImportOrder mirrors `@ianvs/prettier-plugin-sort-imports`' default
38
+ // `importOrder`: Node built-ins, then third-party catch-all, then relative
39
+ // imports. No `""` entries, so the default emits no blank lines between
40
+ // groups (blank lines are opt-in by position).
41
+ var defaultImportOrder = []string{
42
+ builtinModulesPlaceholder,
43
+ thirdPartyModulesPlaceholder,
44
+ `^[.]`,
45
+ }
46
+
47
+ // formatSortImports orders the file's top-level import declarations into
48
+ // canonical groups, alphabetizes each group, merges duplicate imports of the
49
+ // same module, and (when `combineTypeAndValue` is on) folds a type-only
50
+ // import into a value import of the same module. Modeled on
51
+ // `@ianvs/prettier-plugin-sort-imports`. Groups are user-configurable via the
52
+ // `order` option; when omitted, the rule falls back to {@link
53
+ // defaultImportOrder}.
40
54
  //
41
- // Groups are separated by exactly one blank line (or omitted when
42
- // `importOrderSeparation: false`). Within each group, declarations are
43
- // sorted by their module-specifier text (ASCII order, or case-insensitive
44
- // when `importOrderCaseInsensitive: true`). Named specifiers inside each
45
- // declaration are alphabetized when `importOrderSortSpecifiers` is true
46
- // (the default).
55
+ // Within each group, declarations are sorted by their module-specifier text
56
+ // (ASCII order, or case-insensitive unless `caseSensitive: true`). Named
57
+ // specifiers inside each declaration are always sorted.
47
58
  //
48
59
  // Safety policy: if any byte between the contiguous imports is not
49
60
  // whitespace, the rule bails. Comments anchored to specific imports would
50
61
  // otherwise move with the wrong declaration, which is a strictly worse
51
- // outcome than declining to sort.
52
- //
53
- // Side-effect imports (`import "foo"`), namespace imports
54
- // (`import * as ns from`), default imports, and `import type` all
55
- // participate in the same group based on their module specifier.
62
+ // outcome than declining to sort. Side-effect imports (`import "foo"`) also
63
+ // bail the whole block because their evaluation order can carry meaning the
64
+ // rule cannot reason about.
56
65
  type formatSortImports struct{}
57
66
 
58
67
  func (formatSortImports) Name() string { return "format/sort-imports" }
@@ -80,7 +89,7 @@ func (formatSortImports) Check(ctx *Context, node *shimast.Node) {
80
89
  // its module's top-level code for its observable effect; sorting it
81
90
  // across a sibling import that depends on the polyfill being
82
91
  // initialized first would silently change runtime behavior. The
83
- // rule conservatively refuses to sort the entire block in that
92
+ // rule conservatively refuses to reorder the entire block in that
84
93
  // case.
85
94
  if len(imports) >= 2 &&
86
95
  leadingTriviaIsAllWhitespace(src, imports) &&
@@ -101,11 +110,8 @@ func (formatSortImports) Check(ctx *Context, node *shimast.Node) {
101
110
  return
102
111
  }
103
112
  }
104
- if !opts.sortSpecifiers {
105
- return
106
- }
107
- for _, decl := range collectLeadingImports(statements.Nodes) {
108
- reportNamedSpecifierSort(ctx, decl, opts.caseInsensitive)
113
+ for _, decl := range imports {
114
+ reportNamedSpecifierSort(ctx, decl, opts.caseSensitive)
109
115
  }
110
116
  }
111
117
 
@@ -113,63 +119,93 @@ func (formatSortImports) Check(ctx *Context, node *shimast.Node) {
113
119
  // during one Check call. All option defaults are applied here so the
114
120
  // rest of the rule code does not branch on nil-ness.
115
121
  type resolvedSortImportsOptions struct {
116
- groups []sortImportsGroup
117
- separation bool
118
- sortSpecifiers bool
119
- caseInsensitive bool
122
+ groups []sortImportsGroup
123
+ caseSensitive bool
124
+ combineTypeAndValue bool
120
125
  }
121
126
 
127
+ // sortImportsGroup is one resolved entry of the `order` array. A separator
128
+ // entry ("") is not a real group; it is folded into the following group's
129
+ // sepBefore flag during parsing and never appears in the resolved list.
122
130
  type sortImportsGroup struct {
123
131
  raw string
124
- pattern *regexp.Regexp
132
+ pattern *regexp.Regexp // nil for placeholder groups without a regex
125
133
  thirdParty bool
134
+ builtin bool
135
+ typesOnly bool // <TYPES> group: matches `import type` declarations
136
+ sepBefore bool // a "" preceded this group in the `order` array
126
137
  }
127
138
 
128
139
  func loadSortImportsOptions(ctx *Context) resolvedSortImportsOptions {
129
140
  var raw formatSortImportsOptions
130
141
  _ = ctx.DecodeOptions(&raw)
131
- order := raw.ImportOrder
142
+ order := raw.Order
132
143
  if len(order) == 0 {
133
144
  order = defaultImportOrder
134
145
  }
146
+ groups := parseImportOrder(order)
147
+ return resolvedSortImportsOptions{
148
+ groups: groups,
149
+ caseSensitive: raw.CaseSensitive,
150
+ combineTypeAndValue: raw.CombineTypeAndValue,
151
+ }
152
+ }
153
+
154
+ // parseImportOrder turns the raw `order` array into resolved groups. "" entries
155
+ // fold into the next group's sepBefore flag. A third-party catch-all is
156
+ // appended when the user omitted one so unmatched specifiers always land in a
157
+ // real group.
158
+ func parseImportOrder(order []string) []sortImportsGroup {
135
159
  groups := make([]sortImportsGroup, 0, len(order))
160
+ pendingSep := false
136
161
  for _, pat := range order {
137
- if pat == thirdPartyModulesPlaceholder {
138
- groups = append(groups, sortImportsGroup{raw: pat, thirdParty: true})
162
+ if pat == separatorPlaceholder {
163
+ pendingSep = true
139
164
  continue
140
165
  }
141
- compiled, err := regexp.Compile(pat)
142
- if err != nil {
166
+ group, ok := compileImportOrderGroup(pat)
167
+ if !ok {
143
168
  // Bad regex: skip the group rather than failing the entire run.
144
- // The diagnostic surface for misconfigured rules is intentionally
145
- // limited; users see "no match" behavior instead of a crash.
169
+ // Users see "no match" behavior instead of a crash; a stray
170
+ // separator in front of the skipped group carries to the next.
146
171
  continue
147
172
  }
148
- groups = append(groups, sortImportsGroup{raw: pat, pattern: compiled})
149
- }
150
- if len(groups) == 0 {
151
- // Empty `importOrder` collapses to the third-party catchall so the
152
- // rule still produces a deterministic ordering.
153
- groups = []sortImportsGroup{{raw: thirdPartyModulesPlaceholder, thirdParty: true}}
173
+ group.sepBefore = pendingSep
174
+ pendingSep = false
175
+ groups = append(groups, group)
154
176
  }
155
- // Ensure a third-party catchall exists; if the user omitted it,
156
- // append one so unmatched specifiers don't land in a phantom group.
157
177
  if !hasThirdPartyGroup(groups) {
158
178
  groups = append(groups, sortImportsGroup{raw: thirdPartyModulesPlaceholder, thirdParty: true})
159
179
  }
160
- separation := true
161
- if raw.ImportOrderSeparation != nil {
162
- separation = *raw.ImportOrderSeparation
163
- }
164
- sortSpecifiers := true
165
- if raw.ImportOrderSortSpecifiers != nil {
166
- sortSpecifiers = *raw.ImportOrderSortSpecifiers
167
- }
168
- return resolvedSortImportsOptions{
169
- groups: groups,
170
- separation: separation,
171
- sortSpecifiers: sortSpecifiers,
172
- caseInsensitive: raw.ImportOrderCaseInsensitive,
180
+ return groups
181
+ }
182
+
183
+ // compileImportOrderGroup resolves one non-separator `order` entry into a
184
+ // group. The bool is false only when a regex entry fails to compile.
185
+ func compileImportOrderGroup(pat string) (sortImportsGroup, bool) {
186
+ switch {
187
+ case pat == builtinModulesPlaceholder:
188
+ return sortImportsGroup{raw: pat, builtin: true}, true
189
+ case pat == thirdPartyModulesPlaceholder:
190
+ return sortImportsGroup{raw: pat, thirdParty: true}, true
191
+ case strings.HasPrefix(pat, typesPlaceholderPrefix):
192
+ rest := strings.TrimPrefix(pat, typesPlaceholderPrefix)
193
+ group := sortImportsGroup{raw: pat, typesOnly: true}
194
+ if rest == "" {
195
+ return group, true
196
+ }
197
+ compiled, err := regexp.Compile(rest)
198
+ if err != nil {
199
+ return sortImportsGroup{}, false
200
+ }
201
+ group.pattern = compiled
202
+ return group, true
203
+ default:
204
+ compiled, err := regexp.Compile(pat)
205
+ if err != nil {
206
+ return sortImportsGroup{}, false
207
+ }
208
+ return sortImportsGroup{raw: pat, pattern: compiled}, true
173
209
  }
174
210
  }
175
211
 
@@ -184,32 +220,64 @@ func hasThirdPartyGroup(groups []sortImportsGroup) bool {
184
220
  return false
185
221
  }
186
222
 
187
- // matchGroup returns the index of the first group that claims `specifier`.
188
- // Non-third-party groups are checked in order; the third-party catchall
189
- // absorbs anything that did not match an earlier explicit pattern.
190
- func matchGroup(groups []sortImportsGroup, specifier string) int {
223
+ // matchGroup returns the index of the first group that claims a declaration
224
+ // with the given specifier and type-only flag. Groups are checked in order, so
225
+ // a `<TYPES>` group placed ahead of a regex group claims type-only imports
226
+ // first; the third-party catchall absorbs anything left over.
227
+ func matchGroup(groups []sortImportsGroup, specifier string, typeOnly bool) int {
191
228
  thirdPartyIdx := -1
192
229
  for i, g := range groups {
193
- if g.thirdParty {
230
+ switch {
231
+ case g.thirdParty:
194
232
  if thirdPartyIdx < 0 {
195
233
  thirdPartyIdx = i
196
234
  }
197
- continue
198
- }
199
- if g.pattern != nil && g.pattern.MatchString(specifier) {
235
+ case g.builtin:
236
+ if isBuiltinModule(specifier) {
237
+ return i
238
+ }
239
+ case g.typesOnly:
240
+ if typeOnly && (g.pattern == nil || g.pattern.MatchString(specifier)) {
241
+ return i
242
+ }
243
+ case g.pattern != nil && g.pattern.MatchString(specifier):
200
244
  return i
201
245
  }
202
246
  }
203
- if thirdPartyIdx >= 0 {
204
- return thirdPartyIdx
205
- }
206
- return len(groups) // sentinel "no match"
247
+ return thirdPartyIdx
248
+ }
249
+
250
+ // nodeBuiltinModules is the set of Node.js built-in module names recognized by
251
+ // the `<BUILTIN_MODULES>` group. A `node:` prefix and a `pkg/subpath` suffix
252
+ // are both stripped before the lookup.
253
+ var nodeBuiltinModules = map[string]struct{}{
254
+ "assert": {}, "async_hooks": {}, "buffer": {}, "child_process": {},
255
+ "cluster": {}, "console": {}, "constants": {}, "crypto": {},
256
+ "dgram": {}, "diagnostics_channel": {}, "dns": {}, "domain": {},
257
+ "events": {}, "fs": {}, "http": {}, "http2": {}, "https": {},
258
+ "inspector": {}, "module": {}, "net": {}, "os": {}, "path": {},
259
+ "perf_hooks": {}, "process": {}, "punycode": {}, "querystring": {},
260
+ "readline": {}, "repl": {}, "stream": {}, "string_decoder": {},
261
+ "sys": {}, "test": {}, "timers": {}, "tls": {}, "trace_events": {},
262
+ "tty": {}, "url": {}, "util": {}, "v8": {}, "vm": {}, "wasi": {},
263
+ "worker_threads": {}, "zlib": {},
264
+ }
265
+
266
+ // isBuiltinModule reports whether the specifier names a Node.js built-in
267
+ // module. `node:fs`, `fs`, and `fs/promises` all qualify.
268
+ func isBuiltinModule(specifier string) bool {
269
+ name := strings.TrimPrefix(specifier, "node:")
270
+ if slash := strings.IndexByte(name, '/'); slash >= 0 {
271
+ name = name[:slash]
272
+ }
273
+ _, ok := nodeBuiltinModules[name]
274
+ return ok
207
275
  }
208
276
 
209
277
  // specifierListHasCommentTrivia returns true when the source between
210
278
  // adjacent specifiers contains any byte that is not whitespace,
211
279
  // comma, or whitespace-equivalent. A `/* x */` or `// x` inside the
212
- // list anchors the rule into a no-op — sorting would re-emit the
280
+ // list anchors the rule into a no-op, sorting would re-emit the
213
281
  // specifier texts joined with `", "` and silently drop the comment.
214
282
  func specifierListHasCommentTrivia(src string, specifiers []*shimast.Node) bool {
215
283
  for i := 1; i < len(specifiers); i++ {
@@ -284,29 +352,52 @@ func leadingTriviaIsAllWhitespace(src string, imports []*shimast.Node) bool {
284
352
  return true
285
353
  }
286
354
 
287
- // buildSortedImportBlock returns the canonical group/sort representation of
288
- // the contiguous import declarations.
355
+ // siSpec is one named import specifier captured for merge/sort: its local
356
+ // name drives the sort key, its source text is re-emitted verbatim, and the
357
+ // type-only flag of the owning declaration drives `combineTypeAndValue`
358
+ // promotion.
359
+ type siSpec struct {
360
+ sortKey string
361
+ text string
362
+ fromTypeOnlyDecl bool
363
+ }
364
+
365
+ // siDecl is the parsed shape of one import declaration used by the block
366
+ // rebuilder. Declarations with a namespace binding are not mergeable and are
367
+ // re-emitted from their original text.
368
+ type siDecl struct {
369
+ specifier string
370
+ specRaw string
371
+ typeOnly bool
372
+ defaultName string
373
+ named []siSpec
374
+ hasNamed bool
375
+ namespace bool
376
+ original string
377
+ semicolon bool
378
+ }
379
+
380
+ // siEntry is a (possibly merged) import declaration ready to be grouped,
381
+ // sorted, and emitted.
382
+ type siEntry struct {
383
+ group int
384
+ specifier string
385
+ sortKey string
386
+ typeOnly bool
387
+ text string
388
+ }
389
+
390
+ // buildSortedImportBlock returns the canonical group/sort/merge representation
391
+ // of the contiguous import declarations.
289
392
  func buildSortedImportBlock(src string, imports []*shimast.Node, opts resolvedSortImportsOptions) string {
290
- type entry struct {
291
- group int
292
- specifier string
293
- sortKey string
294
- text string
295
- }
296
- entries := make([]entry, 0, len(imports))
393
+ decls := make([]siDecl, 0, len(imports))
297
394
  for _, decl := range imports {
298
- spec := moduleSpecifierText(decl)
299
- text := importStatementText(src, decl)
300
- sortKey := spec
301
- if opts.caseInsensitive {
302
- sortKey = strings.ToLower(spec)
303
- }
304
- entries = append(entries, entry{
305
- group: matchGroup(opts.groups, spec),
306
- specifier: spec,
307
- sortKey: sortKey,
308
- text: text,
309
- })
395
+ decls = append(decls, parseImportDecl(src, decl))
396
+ }
397
+ entries := mergeImportDecls(decls, opts)
398
+ for i := range entries {
399
+ entries[i].group = matchGroup(opts.groups, entries[i].specifier, entries[i].typeOnly)
400
+ entries[i].sortKey = foldCase(entries[i].specifier, opts.caseSensitive)
310
401
  }
311
402
  sort.SliceStable(entries, func(i, j int) bool {
312
403
  if entries[i].group != entries[j].group {
@@ -319,7 +410,7 @@ func buildSortedImportBlock(src string, imports []*shimast.Node, opts resolvedSo
319
410
  prevGroup := entries[0].group
320
411
  for i, e := range entries {
321
412
  if i > 0 {
322
- if e.group != prevGroup && opts.separation {
413
+ if e.group != prevGroup && separatorBetween(opts.groups, prevGroup, e.group) {
323
414
  b.WriteString("\n\n")
324
415
  } else {
325
416
  b.WriteString("\n")
@@ -331,6 +422,214 @@ func buildSortedImportBlock(src string, imports []*shimast.Node, opts resolvedSo
331
422
  return b.String()
332
423
  }
333
424
 
425
+ // separatorBetween reports whether a blank line should sit between two
426
+ // consecutive (in sorted output) groups. Groups skipped because they had no
427
+ // imports still contribute their sepBefore flag, so `[A, "", B]` with an empty
428
+ // A and B that both fall through to a later group collapses to one blank line.
429
+ func separatorBetween(groups []sortImportsGroup, prev, curr int) bool {
430
+ for k := prev + 1; k <= curr && k < len(groups); k++ {
431
+ if groups[k].sepBefore {
432
+ return true
433
+ }
434
+ }
435
+ return false
436
+ }
437
+
438
+ // parseImportDecl captures the merge-relevant shape of one import declaration.
439
+ func parseImportDecl(src string, decl *shimast.Node) siDecl {
440
+ out := siDecl{
441
+ specifier: moduleSpecifierText(decl),
442
+ specRaw: moduleSpecifierRaw(src, decl),
443
+ original: importStatementText(src, decl),
444
+ }
445
+ out.semicolon = strings.HasSuffix(out.original, ";")
446
+ imp := decl.AsImportDeclaration()
447
+ if imp == nil || imp.ImportClause == nil {
448
+ return out
449
+ }
450
+ clause := imp.ImportClause.AsImportClause()
451
+ if clause == nil {
452
+ return out
453
+ }
454
+ out.typeOnly = clause.PhaseModifier == shimast.KindTypeKeyword
455
+ out.defaultName = identifierText(clause.Name())
456
+ if clause.NamedBindings == nil {
457
+ return out
458
+ }
459
+ if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
460
+ out.namespace = true
461
+ return out
462
+ }
463
+ if clause.NamedBindings.Kind != shimast.KindNamedImports {
464
+ return out
465
+ }
466
+ named := clause.NamedBindings.AsNamedImports()
467
+ if named == nil || named.Elements == nil {
468
+ return out
469
+ }
470
+ out.hasNamed = true
471
+ for _, spec := range named.Elements.Nodes {
472
+ s := spec.AsImportSpecifier()
473
+ if s == nil {
474
+ continue
475
+ }
476
+ start := shimscanner.SkipTrivia(src, spec.Pos())
477
+ out.named = append(out.named, siSpec{
478
+ sortKey: identifierText(s.Name()),
479
+ text: src[start:spec.End()],
480
+ fromTypeOnlyDecl: out.typeOnly,
481
+ })
482
+ }
483
+ return out
484
+ }
485
+
486
+ // mergeImportDecls collapses declarations of the same module into one entry.
487
+ // Same-module value imports always merge; same-module type-only imports always
488
+ // merge; a value import and a type-only import of the same module merge only
489
+ // when `combineTypeAndValue` is set. Namespace imports and merge conflicts fall
490
+ // back to one entry per original declaration.
491
+ func mergeImportDecls(decls []siDecl, opts resolvedSortImportsOptions) []siEntry {
492
+ order := make([]string, 0, len(decls))
493
+ buckets := make(map[string][]siDecl)
494
+ for _, d := range decls {
495
+ key := mergeKey(d, opts.combineTypeAndValue)
496
+ if _, seen := buckets[key]; !seen {
497
+ order = append(order, key)
498
+ }
499
+ buckets[key] = append(buckets[key], d)
500
+ }
501
+ entries := make([]siEntry, 0, len(decls))
502
+ for _, key := range order {
503
+ entries = append(entries, mergeBucket(buckets[key], opts)...)
504
+ }
505
+ return entries
506
+ }
507
+
508
+ // mergeKey groups declarations that are eligible to merge. Namespace imports
509
+ // get a unique key (a per-declaration counter via the original text plus a
510
+ // sentinel) so they never merge.
511
+ func mergeKey(d siDecl, combine bool) string {
512
+ if d.namespace {
513
+ return "\x00ns\x00" + d.original
514
+ }
515
+ if combine || !d.typeOnly {
516
+ return "v\x00" + d.specifier
517
+ }
518
+ return "t\x00" + d.specifier
519
+ }
520
+
521
+ // mergeBucket renders a bucket of same-key declarations. A single declaration
522
+ // (or an unmergeable conflict) is emitted from its original text; otherwise the
523
+ // declarations fold into one rebuilt statement.
524
+ func mergeBucket(group []siDecl, opts resolvedSortImportsOptions) []siEntry {
525
+ if len(group) == 1 {
526
+ return []siEntry{originalEntry(group[0])}
527
+ }
528
+ text, typeOnly, ok := renderMergedDecl(group, opts)
529
+ if !ok {
530
+ out := make([]siEntry, 0, len(group))
531
+ for _, d := range group {
532
+ out = append(out, originalEntry(d))
533
+ }
534
+ return out
535
+ }
536
+ return []siEntry{{specifier: group[0].specifier, typeOnly: typeOnly, text: text}}
537
+ }
538
+
539
+ // originalEntry wraps one declaration's untouched source text as an entry.
540
+ func originalEntry(d siDecl) siEntry {
541
+ return siEntry{specifier: d.specifier, typeOnly: d.typeOnly, text: d.original}
542
+ }
543
+
544
+ // renderMergedDecl folds a bucket of mergeable declarations into one statement.
545
+ // The bool is false when the declarations cannot be merged (conflicting default
546
+ // names, or a type-only default that cannot survive in a mixed value import).
547
+ func renderMergedDecl(group []siDecl, opts resolvedSortImportsOptions) (string, bool, bool) {
548
+ mergedTypeOnly := true
549
+ for _, d := range group {
550
+ if !d.typeOnly {
551
+ mergedTypeOnly = false
552
+ }
553
+ }
554
+ defaultName := ""
555
+ defaultTypeOnly := false
556
+ for _, d := range group {
557
+ if d.defaultName == "" {
558
+ continue
559
+ }
560
+ if defaultName != "" && defaultName != d.defaultName {
561
+ return "", false, false
562
+ }
563
+ defaultName = d.defaultName
564
+ defaultTypeOnly = d.typeOnly
565
+ }
566
+ // A type-only default cannot be expressed inside a mixed value import
567
+ // (`import type Foo, { value }` would retype `value`).
568
+ if defaultName != "" && defaultTypeOnly && !mergedTypeOnly {
569
+ return "", false, false
570
+ }
571
+ specs := collectMergedSpecs(group, mergedTypeOnly, opts.caseSensitive)
572
+ if defaultName == "" && len(specs) == 0 {
573
+ return "", false, false
574
+ }
575
+
576
+ var b strings.Builder
577
+ b.WriteString("import ")
578
+ if mergedTypeOnly {
579
+ b.WriteString("type ")
580
+ }
581
+ if defaultName != "" {
582
+ b.WriteString(defaultName)
583
+ if len(specs) > 0 {
584
+ b.WriteString(", ")
585
+ }
586
+ }
587
+ if len(specs) > 0 {
588
+ b.WriteString("{ ")
589
+ b.WriteString(strings.Join(specs, ", "))
590
+ b.WriteString(" }")
591
+ }
592
+ b.WriteString(" from ")
593
+ b.WriteString(group[0].specRaw)
594
+ if group[0].semicolon {
595
+ b.WriteString(";")
596
+ }
597
+ return b.String(), mergedTypeOnly, true
598
+ }
599
+
600
+ // collectMergedSpecs gathers, de-duplicates, and sorts the named specifiers of
601
+ // a merged declaration. A specifier from a type-only declaration folded into a
602
+ // mixed value import gains an inline `type ` prefix.
603
+ func collectMergedSpecs(group []siDecl, mergedTypeOnly, caseSensitive bool) []string {
604
+ type keyed struct {
605
+ sortKey string
606
+ text string
607
+ }
608
+ seen := make(map[string]struct{})
609
+ items := make([]keyed, 0)
610
+ for _, d := range group {
611
+ for _, s := range d.named {
612
+ text := s.text
613
+ if s.fromTypeOnlyDecl && !mergedTypeOnly && !strings.HasPrefix(text, "type ") {
614
+ text = "type " + text
615
+ }
616
+ if _, dup := seen[text]; dup {
617
+ continue
618
+ }
619
+ seen[text] = struct{}{}
620
+ items = append(items, keyed{sortKey: foldCase(s.sortKey, caseSensitive), text: text})
621
+ }
622
+ }
623
+ sort.SliceStable(items, func(i, j int) bool {
624
+ return items[i].sortKey < items[j].sortKey
625
+ })
626
+ texts := make([]string, len(items))
627
+ for i, it := range items {
628
+ texts[i] = it.text
629
+ }
630
+ return texts
631
+ }
632
+
334
633
  // importStatementText returns the import declaration's source text,
335
634
  // stripped of any leading whitespace/comment trivia.
336
635
  func importStatementText(src string, decl *shimast.Node) string {
@@ -355,6 +654,30 @@ func moduleSpecifierText(decl *shimast.Node) string {
355
654
  return stringLiteralText(imp.ModuleSpecifier)
356
655
  }
357
656
 
657
+ // moduleSpecifierRaw returns the module specifier's source text including its
658
+ // surrounding quotes, so a merged declaration preserves the original quote
659
+ // style.
660
+ func moduleSpecifierRaw(src string, decl *shimast.Node) string {
661
+ imp := decl.AsImportDeclaration()
662
+ if imp == nil || imp.ModuleSpecifier == nil {
663
+ return ""
664
+ }
665
+ start := shimscanner.SkipTrivia(src, imp.ModuleSpecifier.Pos())
666
+ end := imp.ModuleSpecifier.End()
667
+ if start < 0 || start > end || end > len(src) {
668
+ return ""
669
+ }
670
+ return src[start:end]
671
+ }
672
+
673
+ // foldCase lowercases `s` unless case-sensitive ordering was requested.
674
+ func foldCase(s string, caseSensitive bool) string {
675
+ if caseSensitive {
676
+ return s
677
+ }
678
+ return strings.ToLower(s)
679
+ }
680
+
358
681
  // specifierEntry is the sortable pair captured for each named import
359
682
  // specifier: the identifier used as the sort key and the literal source
360
683
  // text that gets re-emitted in canonical order.
@@ -365,7 +688,7 @@ type specifierEntry struct {
365
688
 
366
689
  // reportNamedSpecifierSort reports a fix when an import's `{ a, b }` list
367
690
  // is out of alphabetical order. Type-only specifiers participate in the
368
- // same sort key as value specifiers — prettier's plugin-sort-imports
691
+ // same sort key as value specifiers, prettier's plugin-sort-imports
369
692
  // matches this behavior.
370
693
  //
371
694
  // Safety policy: comment trivia between specifiers anchors the rule
@@ -373,7 +696,7 @@ type specifierEntry struct {
373
696
  // would discard any `/* x */` comment carried inside the list. The
374
697
  // block-level sort applies the same policy via
375
698
  // `leadingTriviaIsAllWhitespace`; this is the per-specifier analog.
376
- func reportNamedSpecifierSort(ctx *Context, decl *shimast.Node, caseInsensitive bool) {
699
+ func reportNamedSpecifierSort(ctx *Context, decl *shimast.Node, caseSensitive bool) {
377
700
  imp := decl.AsImportDeclaration()
378
701
  if imp == nil || imp.ImportClause == nil {
379
702
  return
@@ -410,12 +733,8 @@ func reportNamedSpecifierSort(ctx *Context, decl *shimast.Node, caseInsensitive
410
733
  }
411
734
  start := shimscanner.SkipTrivia(src, spec.Pos())
412
735
  end := spec.End()
413
- key := name
414
- if caseInsensitive {
415
- key = strings.ToLower(name)
416
- }
417
736
  entries = append(entries, specifierEntry{
418
- key: key,
737
+ key: foldCase(name, caseSensitive),
419
738
  text: src[start:end],
420
739
  })
421
740
  }