@ttsc/lint 0.13.0 → 0.14.0-dev.20260528

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 (467) hide show
  1. package/README.md +948 -69
  2. package/lib/defaultFormat.d.ts +9 -6
  3. package/lib/defaultFormat.js +9 -6
  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/{ITtscLintFormatConfig.d.ts → ITtscLintFormat.d.ts} +2 -2
  10. package/lib/structures/{TtscLintRuleMap.js → ITtscLintFormat.js} +1 -1
  11. package/lib/structures/ITtscLintFormat.js.map +1 -0
  12. package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
  13. package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
  14. package/lib/structures/TtscLintRuleSetting.js.map +1 -0
  15. package/lib/structures/index.d.ts +3 -4
  16. package/lib/structures/index.js +3 -4
  17. package/lib/structures/index.js.map +1 -1
  18. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +104 -0
  19. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +14 -0
  20. package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
  21. package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +79 -0
  22. package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
  23. package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
  24. package/lib/structures/rules/ITtscLintContributorRules.d.ts +17 -0
  25. package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
  26. package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
  27. package/lib/structures/rules/ITtscLintCoreRules.d.ts +1257 -0
  28. package/lib/structures/{ITtscLintFormatConfig.js → rules/ITtscLintCoreRules.js} +1 -1
  29. package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
  30. package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
  31. package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
  32. package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
  33. package/lib/structures/rules/ITtscLintCypressRules.d.ts +129 -0
  34. package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
  35. package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
  36. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +157 -0
  37. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
  38. package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
  39. package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +167 -0
  40. package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
  41. package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
  42. package/lib/structures/rules/ITtscLintJestRules.d.ts +201 -0
  43. package/lib/structures/rules/ITtscLintJestRules.js +3 -0
  44. package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
  45. package/lib/structures/rules/ITtscLintJsdocRules.d.ts +140 -0
  46. package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
  47. package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
  48. package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +302 -0
  49. package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
  50. package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
  51. package/lib/structures/rules/ITtscLintNextjsRules.d.ts +185 -0
  52. package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
  53. package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
  54. package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +330 -0
  55. package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
  56. package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
  57. package/lib/structures/rules/ITtscLintPromiseRules.d.ts +188 -0
  58. package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
  59. package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
  60. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
  61. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
  62. package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
  63. package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +51 -0
  64. package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
  65. package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
  66. package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +39 -0
  67. package/lib/structures/rules/ITtscLintReactRuleOptions.js +12 -0
  68. package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
  69. package/lib/structures/rules/ITtscLintReactRules.d.ts +289 -0
  70. package/lib/structures/rules/ITtscLintReactRules.js +3 -0
  71. package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
  72. package/lib/structures/rules/ITtscLintRegexpRules.d.ts +190 -0
  73. package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
  74. package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
  75. package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +66 -0
  76. package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
  77. package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
  78. package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
  79. package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
  80. package/lib/structures/rules/ITtscLintRules.js.map +1 -0
  81. package/lib/structures/rules/ITtscLintSecurityRules.d.ts +148 -0
  82. package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
  83. package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
  84. package/lib/structures/rules/ITtscLintSolidRules.d.ts +181 -0
  85. package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
  86. package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
  87. package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +21 -0
  88. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
  89. package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
  90. package/lib/structures/rules/ITtscLintStorybookRules.d.ts +174 -0
  91. package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
  92. package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
  93. package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +94 -0
  94. package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
  95. package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
  96. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +22 -0
  97. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
  98. package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
  99. package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +310 -0
  100. package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
  101. package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
  102. package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1132 -0
  103. package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
  104. package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
  105. package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
  106. package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
  107. package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
  108. package/lib/structures/rules/ITtscLintVitestRules.d.ts +141 -0
  109. package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
  110. package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
  111. package/lib/structures/rules/index.d.ts +31 -0
  112. package/lib/structures/rules/index.js +48 -0
  113. package/lib/structures/rules/index.js.map +1 -0
  114. package/linthost/ast_helpers.go +30 -5
  115. package/linthost/compile.go +33 -2
  116. package/linthost/config.go +48 -17
  117. package/linthost/config_format.go +9 -9
  118. package/linthost/directives.go +67 -25
  119. package/linthost/dispatch.go +14 -3
  120. package/linthost/engine.go +160 -40
  121. package/linthost/fix.go +2 -2
  122. package/linthost/flags_gen.go +17 -15
  123. package/linthost/host.go +40 -3
  124. package/linthost/lsp.go +747 -0
  125. package/linthost/print_dispatch.go +2 -2
  126. package/linthost/print_nodes_function.go +2 -2
  127. package/linthost/print_nodes_object.go +1 -1
  128. package/linthost/rule_names.go +14 -0
  129. package/linthost/rules_arrays.go +2 -2
  130. package/linthost/rules_boundaries.go +532 -0
  131. package/linthost/rules_camelcase.go +133 -0
  132. package/linthost/rules_complexity.go +94 -0
  133. package/linthost/rules_consistent_return.go +130 -0
  134. package/linthost/rules_console.go +1 -1
  135. package/linthost/rules_core_extra.go +739 -0
  136. package/linthost/rules_curly.go +70 -0
  137. package/linthost/rules_cypress.go +608 -0
  138. package/linthost/rules_debugger.go +2 -2
  139. package/linthost/rules_default_case.go +39 -0
  140. package/linthost/rules_default_case_last.go +46 -0
  141. package/linthost/rules_dupes.go +3 -3
  142. package/linthost/rules_empty.go +3 -3
  143. package/linthost/rules_escape.go +44 -24
  144. package/linthost/rules_eval.go +2 -2
  145. package/linthost/rules_finally.go +2 -2
  146. package/linthost/rules_format_jsdoc.go +1 -1
  147. package/linthost/rules_format_print_width.go +9 -9
  148. package/linthost/rules_format_quotes.go +1 -1
  149. package/linthost/rules_format_semi.go +1 -1
  150. package/linthost/rules_format_sort_imports.go +1 -1
  151. package/linthost/rules_format_trailing_comma.go +1 -1
  152. package/linthost/rules_functional.go +967 -0
  153. package/linthost/rules_gap.go +24 -45
  154. package/linthost/rules_grouped_accessor_pairs.go +87 -0
  155. package/linthost/rules_guard_for_in.go +198 -0
  156. package/linthost/rules_id_length.go +77 -0
  157. package/linthost/rules_imports.go +70 -2
  158. package/linthost/rules_init_declarations.go +53 -0
  159. package/linthost/rules_jest.go +618 -0
  160. package/linthost/rules_jsdoc.go +502 -0
  161. package/linthost/rules_jsx_a11y.go +1264 -0
  162. package/linthost/rules_logic.go +12 -12
  163. package/linthost/rules_loops.go +1 -1
  164. package/linthost/rules_max_classes_per_file.go +58 -0
  165. package/linthost/rules_max_depth.go +83 -0
  166. package/linthost/rules_max_lines.go +101 -0
  167. package/linthost/rules_max_lines_per_function.go +57 -0
  168. package/linthost/rules_max_nested_callbacks.go +61 -0
  169. package/linthost/rules_max_params.go +49 -0
  170. package/linthost/rules_max_statements.go +59 -0
  171. package/linthost/rules_misc.go +1 -1
  172. package/linthost/rules_nextjs.go +697 -0
  173. package/linthost/rules_no_else_return.go +75 -0
  174. package/linthost/rules_no_empty_named_blocks.go +91 -0
  175. package/linthost/rules_no_extend_native.go +51 -0
  176. package/linthost/rules_no_import_assign.go +140 -0
  177. package/linthost/rules_no_invalid_this.go +52 -0
  178. package/linthost/rules_no_loop_func.go +85 -0
  179. package/linthost/rules_no_magic_numbers.go +123 -0
  180. package/linthost/rules_no_mixed_operators.go +80 -0
  181. package/linthost/rules_no_param_reassign.go +98 -0
  182. package/linthost/rules_no_redeclare.go +270 -0
  183. package/linthost/rules_no_restricted_imports.go +105 -0
  184. package/linthost/rules_no_restricted_syntax.go +49 -0
  185. package/linthost/rules_no_shadow.go +147 -0
  186. package/linthost/rules_no_unreachable.go +77 -0
  187. package/linthost/rules_no_useless_assignment.go +120 -0
  188. package/linthost/rules_no_useless_constructor.go +245 -0
  189. package/linthost/rules_no_useless_return.go +51 -0
  190. package/linthost/rules_params.go +1 -1
  191. package/linthost/rules_playwright.go +788 -0
  192. package/linthost/rules_prefer_arrow_callback.go +124 -0
  193. package/linthost/rules_prefer_destructuring.go +98 -0
  194. package/linthost/rules_prefer_named_capture_group.go +129 -0
  195. package/linthost/rules_prefer_rest_params.go +94 -0
  196. package/linthost/rules_problems.go +41 -30
  197. package/linthost/rules_promise.go +1128 -73
  198. package/linthost/rules_protos.go +2 -2
  199. package/linthost/rules_react.go +773 -0
  200. package/linthost/rules_react_extras.go +227 -0
  201. package/linthost/rules_react_hooks.go +848 -0
  202. package/linthost/rules_react_perf.go +293 -0
  203. package/linthost/rules_react_refresh.go +583 -0
  204. package/linthost/rules_regexp.go +410 -0
  205. package/linthost/rules_security.go +830 -0
  206. package/linthost/rules_self.go +2 -2
  207. package/linthost/rules_solid.go +1073 -0
  208. package/linthost/rules_sort_imports.go +69 -0
  209. package/linthost/rules_sort_keys.go +134 -0
  210. package/linthost/rules_storybook.go +1176 -0
  211. package/linthost/rules_strings.go +4 -4
  212. package/linthost/rules_suggestions.go +38 -38
  213. package/linthost/rules_tanstack_query.go +844 -0
  214. package/linthost/rules_testing_library.go +1204 -0
  215. package/linthost/rules_throw.go +1 -1
  216. package/linthost/rules_ts.go +19 -19
  217. package/linthost/rules_ts_async.go +399 -0
  218. package/linthost/rules_ts_class_literal_property_style.go +115 -0
  219. package/linthost/rules_ts_consistent_type_exports.go +256 -0
  220. package/linthost/rules_ts_explicit_function_return_type.go +67 -0
  221. package/linthost/rules_ts_extra.go +155 -36
  222. package/linthost/rules_ts_no_base_to_string.go +218 -0
  223. package/linthost/rules_ts_no_confusing_void.go +72 -0
  224. package/linthost/rules_ts_no_deprecated.go +195 -0
  225. package/linthost/rules_ts_no_for_in_array.go +84 -0
  226. package/linthost/rules_ts_no_magic_numbers.go +124 -0
  227. package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
  228. package/linthost/rules_ts_no_misused_spread.go +104 -0
  229. package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
  230. package/linthost/rules_ts_no_restricted_types.go +62 -0
  231. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
  232. package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
  233. package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
  234. package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
  235. package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
  236. package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
  237. package/linthost/rules_ts_no_unsafe_argument.go +82 -0
  238. package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
  239. package/linthost/rules_ts_no_unsafe_call.go +82 -0
  240. package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
  241. package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
  242. package/linthost/rules_ts_no_unsafe_return.go +126 -0
  243. package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
  244. package/linthost/rules_ts_no_useless_constructor.go +52 -0
  245. package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
  246. package/linthost/rules_ts_parameter_properties.go +38 -0
  247. package/linthost/rules_ts_prefer_find.go +178 -0
  248. package/linthost/rules_ts_prefer_includes.go +255 -0
  249. package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
  250. package/linthost/rules_ts_prefer_optional_chain.go +228 -0
  251. package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
  252. package/linthost/rules_ts_prefer_readonly.go +73 -0
  253. package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
  254. package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
  255. package/linthost/rules_ts_prefer_return_this_type.go +135 -0
  256. package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
  257. package/linthost/rules_ts_promise_function_async.go +77 -0
  258. package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
  259. package/linthost/rules_ts_require_array_sort_compare.go +96 -0
  260. package/linthost/rules_ts_restrict_plus_operands.go +127 -0
  261. package/linthost/rules_ts_restrict_template_expressions.go +95 -0
  262. package/linthost/rules_ts_simple.go +387 -0
  263. package/linthost/rules_ts_sort_type_constituents.go +144 -0
  264. package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
  265. package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
  266. package/linthost/rules_ts_unbound_method.go +126 -0
  267. package/linthost/rules_tsdoc.go +136 -0
  268. package/linthost/rules_unicorn_better_regex.go +27 -0
  269. package/linthost/rules_unicorn_catch_error_name.go +46 -0
  270. package/linthost/rules_unicorn_consistent_assert.go +47 -0
  271. package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
  272. package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
  273. package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
  274. package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
  275. package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
  276. package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
  277. package/linthost/rules_unicorn_custom_error_definition.go +138 -0
  278. package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
  279. package/linthost/rules_unicorn_error_message.go +64 -0
  280. package/linthost/rules_unicorn_escape_case.go +51 -0
  281. package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
  282. package/linthost/rules_unicorn_explicit_length_check.go +88 -0
  283. package/linthost/rules_unicorn_filename_case.go +29 -0
  284. package/linthost/rules_unicorn_import_style.go +28 -0
  285. package/linthost/rules_unicorn_isolated_functions.go +29 -0
  286. package/linthost/rules_unicorn_new_for_builtins.go +87 -0
  287. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
  288. package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
  289. package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
  290. package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
  291. package/linthost/rules_unicorn_no_array_for_each.go +40 -0
  292. package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
  293. package/linthost/rules_unicorn_no_array_reduce.go +41 -0
  294. package/linthost/rules_unicorn_no_array_reverse.go +44 -0
  295. package/linthost/rules_unicorn_no_array_sort.go +43 -0
  296. package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
  297. package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
  298. package/linthost/rules_unicorn_no_console_spaces.go +61 -0
  299. package/linthost/rules_unicorn_no_document_cookie.go +41 -0
  300. package/linthost/rules_unicorn_no_empty_file.go +44 -0
  301. package/linthost/rules_unicorn_no_for_loop.go +102 -0
  302. package/linthost/rules_unicorn_no_hex_escape.go +42 -0
  303. package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
  304. package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
  305. package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
  306. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
  307. package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
  308. package/linthost/rules_unicorn_no_lonely_if.go +44 -0
  309. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
  310. package/linthost/rules_unicorn_no_named_default.go +54 -0
  311. package/linthost/rules_unicorn_no_negated_condition.go +85 -0
  312. package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
  313. package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
  314. package/linthost/rules_unicorn_no_new_array.go +30 -0
  315. package/linthost/rules_unicorn_no_new_buffer.go +31 -0
  316. package/linthost/rules_unicorn_no_null.go +24 -0
  317. package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
  318. package/linthost/rules_unicorn_no_process_exit.go +43 -0
  319. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
  320. package/linthost/rules_unicorn_no_static_only_class.go +56 -0
  321. package/linthost/rules_unicorn_no_thenable.go +74 -0
  322. package/linthost/rules_unicorn_no_this_assignment.go +35 -0
  323. package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
  324. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
  325. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
  326. package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
  327. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
  328. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
  329. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
  330. package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
  331. package/linthost/rules_unicorn_no_unused_properties.go +30 -0
  332. package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
  333. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
  334. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
  335. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
  336. package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
  337. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
  338. package/linthost/rules_unicorn_no_useless_spread.go +67 -0
  339. package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
  340. package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
  341. package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
  342. package/linthost/rules_unicorn_number_literal_case.go +69 -0
  343. package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
  344. package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
  345. package/linthost/rules_unicorn_prefer_array_find.go +51 -0
  346. package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
  347. package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
  348. package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
  349. package/linthost/rules_unicorn_prefer_array_some.go +67 -0
  350. package/linthost/rules_unicorn_prefer_at.go +77 -0
  351. package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
  352. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
  353. package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
  354. package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
  355. package/linthost/rules_unicorn_prefer_code_point.go +49 -0
  356. package/linthost/rules_unicorn_prefer_date_now.go +78 -0
  357. package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
  358. package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
  359. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
  360. package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
  361. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
  362. package/linthost/rules_unicorn_prefer_event_target.go +36 -0
  363. package/linthost/rules_unicorn_prefer_export_from.go +129 -0
  364. package/linthost/rules_unicorn_prefer_global_this.go +127 -0
  365. package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
  366. package/linthost/rules_unicorn_prefer_includes.go +149 -0
  367. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
  368. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
  369. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
  370. package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
  371. package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
  372. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
  373. package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
  374. package/linthost/rules_unicorn_prefer_module.go +50 -0
  375. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
  376. package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
  377. package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
  378. package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
  379. package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
  380. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
  381. package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
  382. package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
  383. package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
  384. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
  385. package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
  386. package/linthost/rules_unicorn_prefer_set_has.go +42 -0
  387. package/linthost/rules_unicorn_prefer_set_size.go +48 -0
  388. package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
  389. package/linthost/rules_unicorn_prefer_single_call.go +96 -0
  390. package/linthost/rules_unicorn_prefer_spread.go +51 -0
  391. package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
  392. package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
  393. package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
  394. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
  395. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
  396. package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
  397. package/linthost/rules_unicorn_prefer_switch.go +103 -0
  398. package/linthost/rules_unicorn_prefer_ternary.go +71 -0
  399. package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
  400. package/linthost/rules_unicorn_prefer_type_error.go +127 -0
  401. package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
  402. package/linthost/rules_unicorn_relative_url_style.go +49 -0
  403. package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
  404. package/linthost/rules_unicorn_require_module_attributes.go +54 -0
  405. package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
  406. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
  407. package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
  408. package/linthost/rules_unicorn_string_content.go +27 -0
  409. package/linthost/rules_unicorn_switch_case_braces.go +42 -0
  410. package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
  411. package/linthost/rules_unicorn_template_indent.go +28 -0
  412. package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
  413. package/linthost/rules_unicorn_throw_new_error.go +61 -0
  414. package/linthost/rules_var.go +10 -4
  415. package/linthost/rules_vitest.go +568 -0
  416. package/package.json +3 -3
  417. package/plugin/main.go +49 -3
  418. package/rule/astutil/astutil.go +10 -3
  419. package/rule/rule.go +12 -10
  420. package/src/defaultFormat.ts +11 -8
  421. package/src/index.ts +65 -43
  422. package/src/structures/ITtscLintConfig.ts +12 -6
  423. package/src/structures/{ITtscLintFormatConfig.ts → ITtscLintFormat.ts} +2 -2
  424. package/src/structures/TtscLintRuleSetting.ts +55 -0
  425. package/src/structures/index.ts +3 -4
  426. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +128 -0
  427. package/src/structures/rules/ITtscLintBoundariesRules.ts +95 -0
  428. package/src/structures/rules/ITtscLintContributorRules.ts +21 -0
  429. package/src/structures/rules/ITtscLintCoreRules.ts +1406 -0
  430. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
  431. package/src/structures/rules/ITtscLintCypressRules.ts +145 -0
  432. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +213 -0
  433. package/src/structures/rules/ITtscLintFunctionalRules.ts +202 -0
  434. package/src/structures/rules/ITtscLintJestRules.ts +220 -0
  435. package/src/structures/rules/ITtscLintJsdocRules.ts +153 -0
  436. package/src/structures/rules/ITtscLintJsxA11yRules.ts +339 -0
  437. package/src/structures/rules/ITtscLintNextjsRules.ts +206 -0
  438. package/src/structures/rules/ITtscLintPlaywrightRules.ts +361 -0
  439. package/src/structures/rules/ITtscLintPromiseRules.ts +205 -0
  440. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
  441. package/src/structures/rules/ITtscLintReactPerfRules.ts +55 -0
  442. package/src/structures/rules/ITtscLintReactRuleOptions.ts +43 -0
  443. package/src/structures/rules/ITtscLintReactRules.ts +321 -0
  444. package/src/structures/rules/ITtscLintRegexpRules.ts +212 -0
  445. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +89 -0
  446. package/src/structures/rules/ITtscLintRules.ts +77 -0
  447. package/src/structures/rules/ITtscLintSecurityRules.ts +162 -0
  448. package/src/structures/rules/ITtscLintSolidRules.ts +202 -0
  449. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +23 -0
  450. package/src/structures/rules/ITtscLintStorybookRules.ts +193 -0
  451. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +102 -0
  452. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +24 -0
  453. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +342 -0
  454. package/src/structures/rules/ITtscLintTypeScriptRules.ts +1230 -0
  455. package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
  456. package/src/structures/rules/ITtscLintVitestRules.ts +154 -0
  457. package/src/structures/rules/index.ts +31 -0
  458. package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
  459. package/lib/structures/TtscLintRule.d.ts +0 -17
  460. package/lib/structures/TtscLintRule.js.map +0 -1
  461. package/lib/structures/TtscLintRuleMap.d.ts +0 -46
  462. package/lib/structures/TtscLintRuleMap.js.map +0 -1
  463. package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
  464. package/lib/structures/TtscLintRuleOptions.js.map +0 -1
  465. package/src/structures/TtscLintRule.ts +0 -160
  466. package/src/structures/TtscLintRuleMap.ts +0 -55
  467. package/src/structures/TtscLintRuleOptions.ts +0 -151
@@ -34,8 +34,9 @@ const (
34
34
 
35
35
  // lintDirective is the parsed representation of one directive comment.
36
36
  type lintDirective struct {
37
- kind lintDirectiveKind
38
- rules lintDirectiveRules
37
+ kind lintDirectiveKind
38
+ rules lintDirectiveRules
39
+ ruleList []string
39
40
  }
40
41
 
41
42
  // lintDirectiveRules holds the rule scope of a directive. When `all` is
@@ -55,13 +56,22 @@ type lintDirectiveEvent struct {
55
56
  on bool
56
57
  }
57
58
 
59
+ // lintDirectiveRecord preserves the per-directive rule list so the engine
60
+ // can surface unknown rule names referenced by `// eslint-disable*` comments
61
+ // (see `Engine.collectUnknownDirectiveRules`). The suppression path itself
62
+ // only needs `rules`; this record exists purely for that diagnostic pass.
63
+ type lintDirectiveRecord struct {
64
+ ruleList []string
65
+ }
66
+
58
67
  // lintInlineDirectives accumulates the per-file directive information
59
68
  // extracted by parseLintInlineDirectives. `lines` maps a zero-based line
60
69
  // number to any disable-line / disable-next-line directives on that line.
61
70
  // `events` is the ordered list of range-style disable/enable transitions.
62
71
  type lintInlineDirectives struct {
63
- lines map[int][]lintDirectiveRules
64
- events []lintDirectiveEvent
72
+ lines map[int][]lintDirectiveRules
73
+ events []lintDirectiveEvent
74
+ records []lintDirectiveRecord
65
75
  }
66
76
 
67
77
  // lintDisableState tracks the cumulative suppress/allow state as
@@ -75,11 +85,14 @@ type lintDisableState struct {
75
85
  enabledInAll map[string]struct{}
76
86
  }
77
87
 
78
- func filterInlineDisabledFindings(file *shimast.SourceFile, findings []*Finding) []*Finding {
88
+ // filterInlineDisabledFindingsWithDirectives drops findings suppressed by
89
+ // the directive set passed in. The caller is expected to have parsed
90
+ // directives once via parseLintInlineDirectives and reuse them for any
91
+ // other passes (e.g., Engine.collectUnknownDirectiveRules).
92
+ func filterInlineDisabledFindingsWithDirectives(file *shimast.SourceFile, findings []*Finding, directives *lintInlineDirectives) []*Finding {
79
93
  if len(findings) == 0 || file == nil {
80
94
  return findings
81
95
  }
82
- directives := parseLintInlineDirectives(file)
83
96
  if directives.empty() {
84
97
  return findings
85
98
  }
@@ -111,8 +124,19 @@ func parseLintInlineDirectives(file *shimast.SourceFile) *lintInlineDirectives {
111
124
  directives := &lintInlineDirectives{
112
125
  lines: make(map[int][]lintDirectiveRules),
113
126
  }
127
+ // Cheap byte-scan short-circuit: a file with no `-disable` or
128
+ // `-enable` substring cannot contain any recognized directive
129
+ // (`eslint-disable`, `lint-disable`, `eslint-enable`, `lint-enable`
130
+ // all match one of these). Running the full TypeScript scanner over
131
+ // every file just to find comments accounts for ~15 % of the lint
132
+ // engine's CPU on directive-free codebases; the substring check
133
+ // turns that into ~50 ns regardless of file size.
134
+ text := file.Text()
135
+ if !strings.Contains(text, "-disable") && !strings.Contains(text, "-enable") {
136
+ return directives
137
+ }
114
138
  scanner := shimscanner.NewScanner()
115
- scanner.SetText(file.Text())
139
+ scanner.SetText(text)
116
140
  scanner.SetSkipTrivia(false)
117
141
 
118
142
  // templateBraceDepth tracks `{` nesting inside each open template
@@ -186,6 +210,9 @@ scan:
186
210
  on: false,
187
211
  })
188
212
  }
213
+ directives.records = append(directives.records, lintDirectiveRecord{
214
+ ruleList: append([]string(nil), directive.ruleList...),
215
+ })
189
216
  }
190
217
  return directives
191
218
  }
@@ -193,7 +220,7 @@ scan:
193
220
  // empty reports whether no directives were found in the file, allowing the
194
221
  // caller to skip the filtering step entirely.
195
222
  func (d *lintInlineDirectives) empty() bool {
196
- return d == nil || (len(d.lines) == 0 && len(d.events) == 0)
223
+ return d == nil || (len(d.lines) == 0 && len(d.events) == 0 && len(d.records) == 0)
197
224
  }
198
225
 
199
226
  // suppresses reports whether the directive set causes `finding` to be
@@ -259,8 +286,11 @@ func (s *lintDisableState) apply(event lintDirectiveEvent) {
259
286
  }
260
287
 
261
288
  // matches reports whether `rule` is currently suppressed given this state.
262
- // The name is normalized before lookup so that `@typescript-eslint/` prefixes
263
- // do not prevent a match.
289
+ // The name is normalized before lookup so an optional `eslint/` prefix in
290
+ // the directive does not prevent a match against the canonical rule id.
291
+ // Legacy `@typescript-eslint/` / `react-hooks/` / `tsdoc/syntax` prefixes
292
+ // are NOT normalized — they fall through to the unknown-rule path and
293
+ // surface as `Engine.UnknownRules()` so users see the migration loudly.
264
294
  func (s lintDisableState) matches(rule string) bool {
265
295
  normalized := normalizeDirectiveRuleName(rule)
266
296
  if _, ok := s.rules[normalized]; ok {
@@ -336,9 +366,11 @@ func parseLintDirectiveLine(text string) (lintDirective, bool) {
336
366
  if !ok {
337
367
  continue
338
368
  }
369
+ parsed := parseDirectivePayload(payload)
339
370
  return lintDirective{
340
- kind: form.kind,
341
- rules: parseDirectiveRules(payload),
371
+ kind: form.kind,
372
+ rules: parsed.rules,
373
+ ruleList: parsed.ruleList,
342
374
  }, true
343
375
  }
344
376
  }
@@ -363,10 +395,20 @@ func directivePayload(text, marker string) (string, bool) {
363
395
  // marker) into a lintDirectiveRules value. The `--` separator strips an
364
396
  // optional human-readable description. An empty rule list means "all rules".
365
397
  func parseDirectiveRules(payload string) lintDirectiveRules {
366
- payload = stripDirectiveDescription(payload)
367
- payload = strings.ReplaceAll(payload, ",", " ")
368
- fields := strings.Fields(payload)
398
+ return parseDirectivePayload(payload).rules
399
+ }
400
+
401
+ type parsedDirectivePayload struct {
402
+ rules lintDirectiveRules
403
+ ruleList []string
404
+ }
405
+
406
+ func parseDirectivePayload(payload string) parsedDirectivePayload {
407
+ ruleText := stripDirectiveDescription(payload)
408
+ ruleText = strings.ReplaceAll(ruleText, ",", " ")
409
+ fields := strings.Fields(ruleText)
369
410
  rules := make(map[string]struct{}, len(fields))
411
+ ruleList := make([]string, 0, len(fields))
370
412
  for _, field := range fields {
371
413
  if strings.HasPrefix(field, "--") {
372
414
  break
@@ -374,12 +416,16 @@ func parseDirectiveRules(payload string) lintDirectiveRules {
374
416
  name := normalizeDirectiveRuleName(field)
375
417
  if name != "" {
376
418
  rules[name] = struct{}{}
419
+ ruleList = append(ruleList, name)
377
420
  }
378
421
  }
379
422
  if len(rules) == 0 {
380
- return lintDirectiveRules{all: true}
423
+ return parsedDirectivePayload{rules: lintDirectiveRules{all: true}}
424
+ }
425
+ return parsedDirectivePayload{
426
+ rules: lintDirectiveRules{rules: rules},
427
+ ruleList: ruleList,
381
428
  }
382
- return lintDirectiveRules{rules: rules}
383
429
  }
384
430
 
385
431
  // stripDirectiveDescription returns the portion of `payload` before the
@@ -398,15 +444,11 @@ func stripDirectiveDescription(payload string) string {
398
444
  return strings.TrimSpace(payload[:i])
399
445
  }
400
446
  }
401
- return payload
447
+ return strings.TrimSpace(payload)
402
448
  }
403
449
 
404
- // normalizeDirectiveRuleName strips the common ESLint namespace prefixes so
405
- // both `@typescript-eslint/no-var` and `no-var` resolve to the same key.
450
+ // normalizeDirectiveRuleName strips common ESLint namespace prefixes while
451
+ // preserving the kebab/slash rule IDs diagnostics use.
406
452
  func normalizeDirectiveRuleName(name string) string {
407
- name = strings.TrimSpace(name)
408
- name = strings.TrimPrefix(name, "@typescript-eslint/")
409
- name = strings.TrimPrefix(name, "typescript-eslint/")
410
- name = strings.TrimPrefix(name, "eslint/")
411
- return name
453
+ return normalizeBuiltinRuleName(name)
412
454
  }
@@ -23,7 +23,8 @@ var Version = "dev"
23
23
  // caller should propagate to the OS (`os.Exit`) or the host (`Plugin.Run`).
24
24
  //
25
25
  // Recognized verbs: `version` / `-v` / `--version`, `check`, `fix`, `format`,
26
- // `build`, `transform`. Anything else is a usage error (exit code 2).
26
+ // `build`, `transform`, and the `lsp-*` protocol commands consumed by
27
+ // ttscserver. Anything else is a usage error (exit code 2).
27
28
  func Main(args []string) int {
28
29
  return run(args)
29
30
  }
@@ -33,7 +34,7 @@ func Main(args []string) int {
33
34
  // the same entry point the CLI uses.
34
35
  func run(args []string) int {
35
36
  if len(args) == 0 {
36
- fmt.Fprintln(os.Stderr, "@ttsc/lint: command required (expected check|fix|format|build|transform|version)")
37
+ fmt.Fprintln(os.Stderr, "@ttsc/lint: command required (expected check|fix|format|build|transform|lsp-*|version)")
37
38
  return 2
38
39
  }
39
40
  switch args[0] {
@@ -41,7 +42,7 @@ func run(args []string) int {
41
42
  // Don't pay contributor-registration cost for the version banner.
42
43
  fmt.Fprintf(os.Stdout, "@ttsc/lint %s\n", Version)
43
44
  return 0
44
- case "check", "fix", "format", "build", "transform":
45
+ case "check", "fix", "format", "build", "transform", "lsp-command-ids", "lsp-code-action-kinds", "lsp-diagnostics", "lsp-code-actions", "lsp-execute-command":
45
46
  default:
46
47
  fmt.Fprintf(os.Stderr, "@ttsc/lint: unknown command %q\n", args[0])
47
48
  return 2
@@ -60,6 +61,16 @@ func run(args []string) int {
60
61
  return RunBuild(args[1:])
61
62
  case "transform":
62
63
  return RunTransform(args[1:])
64
+ case "lsp-command-ids":
65
+ return RunLSPCommandIDs(args[1:])
66
+ case "lsp-code-action-kinds":
67
+ return RunLSPCodeActionKinds(args[1:])
68
+ case "lsp-diagnostics":
69
+ return RunLSPDiagnostics(args[1:])
70
+ case "lsp-code-actions":
71
+ return RunLSPCodeActions(args[1:])
72
+ case "lsp-execute-command":
73
+ return RunLSPExecuteCommand(args[1:])
63
74
  }
64
75
  return 2
65
76
  }
@@ -246,13 +246,22 @@ func AllRuleNames() []string {
246
246
 
247
247
  // Engine binds a rule configuration to a Program and walks the AST once
248
248
  // per source file, dispatching each visited node to its interested rules.
249
+ //
250
+ // `rules` is a fixed-size slice indexed by `shimast.Kind` value rather
251
+ // than a map. `KindCount` (~350) is small and bounded, and the slice
252
+ // removes a per-node map hash from the hot path — a `walk(node)` over a
253
+ // 50k-node file performs 50k dispatch lookups. The conversion is
254
+ // equivalent in semantics; entries for unused kinds are nil and the
255
+ // per-rule slice still grows by append.
249
256
  type Engine struct {
250
- config RuleResolver
251
- rules map[shimast.Kind][]Rule
252
- enabled map[string]Severity
253
- unknown []string
254
- needsTypeChecker bool
255
- serial bool
257
+ config RuleResolver
258
+ rules [][]Rule
259
+ enabled map[string]Severity
260
+ unknown []string
261
+ unknownDirectives map[string]struct{}
262
+ unknownDirectiveMu sync.Mutex
263
+ needsTypeChecker bool
264
+ serial bool
256
265
  }
257
266
 
258
267
  // SetSerial forces Engine.Run to walk files one at a time. The host calls
@@ -290,9 +299,10 @@ func NewEngineWithResolver(config RuleResolver) *Engine {
290
299
  config = RuleConfig{}
291
300
  }
292
301
  eng := &Engine{
293
- config: config,
294
- rules: make(map[shimast.Kind][]Rule),
295
- enabled: make(map[string]Severity),
302
+ config: config,
303
+ rules: make([][]Rule, int(shimast.KindCount)),
304
+ enabled: make(map[string]Severity),
305
+ unknownDirectives: make(map[string]struct{}),
296
306
  }
297
307
  displaySeverities := config.EnabledRuleConfig()
298
308
  for _, name := range config.ActiveRuleNames() {
@@ -313,16 +323,91 @@ func NewEngineWithResolver(config RuleResolver) *Engine {
313
323
  continue
314
324
  }
315
325
  seen[kind] = struct{}{}
316
- eng.rules[kind] = append(eng.rules[kind], rule)
326
+ idx := int(kind)
327
+ if idx < 0 || idx >= len(eng.rules) {
328
+ // Defensive: a contributor returning a Kind beyond the
329
+ // shim's KindCount would otherwise panic on dispatch.
330
+ continue
331
+ }
332
+ eng.rules[idx] = append(eng.rules[idx], rule)
317
333
  }
318
334
  }
319
335
  sort.Strings(eng.unknown)
320
336
  return eng
321
337
  }
322
338
 
323
- // UnknownRules returns the names of rules that appeared in the config but
324
- // have no registered implementation.
325
- func (e *Engine) UnknownRules() []string { return e.unknown }
339
+ // UnknownRules returns the names of rules that appeared either in the
340
+ // config or in an inline `eslint-disable*` directive but have no
341
+ // registered implementation. Directive-side unknowns are deduped so the
342
+ // same misspelling on every page doesn't flood the warning channel.
343
+ func (e *Engine) UnknownRules() []string {
344
+ if e == nil {
345
+ return nil
346
+ }
347
+ e.unknownDirectiveMu.Lock()
348
+ extras := make([]string, 0, len(e.unknownDirectives))
349
+ for name := range e.unknownDirectives {
350
+ extras = append(extras, name)
351
+ }
352
+ e.unknownDirectiveMu.Unlock()
353
+ if len(extras) == 0 {
354
+ return e.unknown
355
+ }
356
+ seen := make(map[string]struct{}, len(e.unknown)+len(extras))
357
+ out := make([]string, 0, len(e.unknown)+len(extras))
358
+ for _, name := range e.unknown {
359
+ if _, dup := seen[name]; dup {
360
+ continue
361
+ }
362
+ seen[name] = struct{}{}
363
+ out = append(out, name)
364
+ }
365
+ for _, name := range extras {
366
+ if _, dup := seen[name]; dup {
367
+ continue
368
+ }
369
+ seen[name] = struct{}{}
370
+ out = append(out, name)
371
+ }
372
+ sort.Strings(out)
373
+ return out
374
+ }
375
+
376
+ // collectUnknownDirectiveRules walks every directive's rule list and
377
+ // records names that don't resolve to a registered rule. Called once
378
+ // per file after `parseLintInlineDirectives`.
379
+ func (e *Engine) collectUnknownDirectiveRules(directives *lintInlineDirectives) {
380
+ if e == nil || directives == nil {
381
+ return
382
+ }
383
+ for _, rec := range directives.records {
384
+ for _, raw := range rec.ruleList {
385
+ name := normalizeDirectiveRuleName(raw)
386
+ if name == "" {
387
+ continue
388
+ }
389
+ if _, ok := registered.rules[name]; ok {
390
+ continue
391
+ }
392
+ e.recordUnknownDirectiveRule(name)
393
+ }
394
+ }
395
+ }
396
+
397
+ // recordUnknownDirectiveRule remembers a rule name referenced by an
398
+ // `// eslint-disable*` directive that does not resolve to a registered
399
+ // rule. Each unique name is recorded once across the engine run.
400
+ func (e *Engine) recordUnknownDirectiveRule(name string) {
401
+ if e == nil || name == "" {
402
+ return
403
+ }
404
+ e.unknownDirectiveMu.Lock()
405
+ defer e.unknownDirectiveMu.Unlock()
406
+ if e.unknownDirectives == nil {
407
+ e.unknownDirectives = make(map[string]struct{})
408
+ }
409
+ e.unknownDirectives[name] = struct{}{}
410
+ }
326
411
 
327
412
  // NeedsTypeChecker reports whether any active rule requires Context.Checker.
328
413
  func (e *Engine) NeedsTypeChecker() bool {
@@ -333,18 +418,17 @@ func (e *Engine) NeedsTypeChecker() bool {
333
418
  // tests + introspection.
334
419
  func (e *Engine) EnabledRules() map[string]Severity { return e.enabled }
335
420
 
336
- // Run walks every non-declaration source file in the program and returns
337
- // the collected findings. By default files are processed in parallel,
338
- // bounded by `runtime.NumCPU()`; the engine falls back to a serial walk
339
- // when SetSerial(true) was called or when a type-aware rule is active.
340
- // Findings are merged in source-file order so the diagnostic stream is
341
- // deterministic across runs even when the per-file work happens out of
342
- // order.
421
+ // Run walks the source files supplied by the caller and returns the collected
422
+ // findings. By default files are processed in parallel, bounded by
423
+ // `runtime.NumCPU()`; the engine falls back to a serial walk when
424
+ // SetSerial(true) was called or when a type-aware rule is active. Findings are
425
+ // merged in source-file order so the diagnostic stream is deterministic across
426
+ // runs even when the per-file work happens out of order.
343
427
  func (e *Engine) Run(files []*shimast.SourceFile, checker *shimchecker.Checker) []*Finding {
344
428
  if e.runsSerial() {
345
429
  var findings []*Finding
346
430
  for _, file := range files {
347
- if file == nil || file.IsDeclarationFile {
431
+ if file == nil {
348
432
  continue
349
433
  }
350
434
  findings = append(findings, e.runFile(file, checker)...)
@@ -360,7 +444,7 @@ func (e *Engine) Run(files []*shimast.SourceFile, checker *shimchecker.Checker)
360
444
  }
361
445
  sem := make(chan struct{}, workers)
362
446
  for i, file := range files {
363
- if file == nil || file.IsDeclarationFile {
447
+ if file == nil {
364
448
  continue
365
449
  }
366
450
  wg.Add(1)
@@ -394,6 +478,40 @@ type boundRule struct {
394
478
  ctx *Context
395
479
  }
396
480
 
481
+ // lintFileWalker drives the per-file AST traversal. The struct exists so
482
+ // the `ForEachChild` callback can be a method value cached in
483
+ // `childCB`. A naive nested-closure walker re-allocates one callback
484
+ // per recursive call (it captures the walking function variable),
485
+ // which on a 50 k-node file is 50 k throwaway closure allocations.
486
+ // Caching the method value reduces that to one allocation per file.
487
+ type lintFileWalker struct {
488
+ byKind [][]boundRule
489
+ collect func(*Finding)
490
+ childCB func(*shimast.Node) bool
491
+ }
492
+
493
+ // walk dispatches a single node to every rule that registered for the
494
+ // node's Kind, then recurses into children via the cached childCB.
495
+ func (w *lintFileWalker) walk(node *shimast.Node) {
496
+ if node == nil {
497
+ return
498
+ }
499
+ if k := int(node.Kind); k >= 0 && k < len(w.byKind) {
500
+ for _, bound := range w.byKind[k] {
501
+ runRuleCheck(bound.rule, bound.ctx, node, w.collect)
502
+ }
503
+ }
504
+ node.ForEachChild(w.childCB)
505
+ }
506
+
507
+ // visitChild is the cached ForEachChild callback. It is the method
508
+ // value stored in `childCB` so per-recursion closure allocation is
509
+ // avoided.
510
+ func (w *lintFileWalker) visitChild(child *shimast.Node) bool {
511
+ w.walk(child)
512
+ return false
513
+ }
514
+
397
515
  // runFile is the per-file driver. The visitor is allocated once per file
398
516
  // to keep the per-node hot path branch-free; it visits children
399
517
  // post-order so parents see their already-checked subtrees.
@@ -416,9 +534,12 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
416
534
  // Context for every (node, rule) pair, which on a large program meant
417
535
  // millions of short-lived heap allocations and the GC pressure they
418
536
  // carry. Rules never mutate their Context, so reuse is safe.
419
- byKind := make(map[shimast.Kind][]boundRule, len(e.rules))
537
+ byKind := make([][]boundRule, len(e.rules))
420
538
  ctxByRule := make(map[string]*Context, len(e.enabled))
421
539
  for kind, rules := range e.rules {
540
+ if len(rules) == 0 {
541
+ continue
542
+ }
422
543
  for _, rule := range rules {
423
544
  name := rule.Name()
424
545
  ctx, built := ctxByRule[name]
@@ -445,40 +566,39 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
445
566
  }
446
567
  }
447
568
 
448
- var walk func(node *shimast.Node)
449
- walk = func(node *shimast.Node) {
450
- if node == nil {
451
- return
452
- }
453
- for _, bound := range byKind[node.Kind] {
454
- runRuleCheck(bound.rule, bound.ctx, node, collect)
455
- }
456
- node.ForEachChild(func(child *shimast.Node) bool {
457
- walk(child)
458
- return false // visit every child
459
- })
460
- }
569
+ // Use a struct-based walker so the per-node ForEachChild callback is
570
+ // allocated once (stored as `w.childCB`) instead of once per Walk
571
+ // call. Closures that capture a recursive local function escape to
572
+ // the heap on every invocation; converting to a method value with a
573
+ // cached function field removes that allocation from the hot path —
574
+ // ~38 % of pre-Opt-4 CPU was in the inner ForEachChild closure.
575
+ w := &lintFileWalker{byKind: byKind, collect: collect}
576
+ w.childCB = w.visitChild
461
577
 
462
578
  // SourceFile dispatches into its statement list directly; we walk
463
579
  // statements explicitly so the file node itself can be inspected by
464
580
  // rules (e.g., `ban-ts-comment` reads CommentDirectives off the
465
581
  // SourceFile).
466
- for _, bound := range byKind[shimast.KindSourceFile] {
467
- runRuleCheck(bound.rule, bound.ctx, file.AsNode(), collect)
582
+ if k := int(shimast.KindSourceFile); k >= 0 && k < len(byKind) {
583
+ for _, bound := range byKind[k] {
584
+ runRuleCheck(bound.rule, bound.ctx, file.AsNode(), collect)
585
+ }
468
586
  }
469
587
 
470
588
  statements := file.Statements
471
589
  if statements != nil {
472
590
  for _, stmt := range statements.Nodes {
473
- walk(stmt)
591
+ w.walk(stmt)
474
592
  }
475
593
  }
594
+ directives := parseLintInlineDirectives(file)
595
+ e.collectUnknownDirectiveRules(directives)
476
596
  // Apply inline-disable filtering even for files with no statement
477
597
  // list. A SourceFile-level rule that fires on a `// ttsc-lint-disable`
478
598
  // comment must still honor the directive; early-returning before
479
599
  // the filter would silently leak those findings into the diagnostic
480
600
  // stream.
481
- return filterInlineDisabledFindings(file, collected)
601
+ return filterInlineDisabledFindingsWithDirectives(file, collected, directives)
482
602
  }
483
603
 
484
604
  func hasEnabledFileRules(rules RuleConfig) bool {
package/linthost/fix.go CHANGED
@@ -16,8 +16,8 @@ import (
16
16
  shimdw "github.com/microsoft/typescript-go/shim/diagnosticwriter"
17
17
  )
18
18
 
19
- // maxFixPasses bounds the native fix cascade. Real-world cascades (no-var
20
- // prefer-const → eqeqeq …) settle in a handful of passes; the cap exists so
19
+ // maxFixPasses bounds the native fix cascade. Real-world cascades (noVar
20
+ // preferConst → eqeqeq …) settle in a handful of passes; the cap exists so
21
21
  // a buggy rule that re-reports its own edit cannot loop forever.
22
22
  const maxFixPasses = 10
23
23
 
@@ -13,19 +13,21 @@ package linthost
13
13
  // Generated from packages/ttsc/src/flags/schema.ts. Edit the schema, not
14
14
  // this file.
15
15
  var LintFlagAllowList = map[string]bool{
16
- "checkers": true,
17
- "cwd": true,
18
- "emit": false,
19
- "file": true,
20
- "noEmit": false,
21
- "out": true,
22
- "outDir": true,
23
- "p": true,
24
- "plugins-json": true,
25
- "project": true,
26
- "quiet": false,
27
- "singleThreaded": false,
28
- "tsconfig": true,
29
- "tsgo-args": true,
30
- "verbose": false,
16
+ "checkers": true,
17
+ "cwd": true,
18
+ "diagnostics": false,
19
+ "emit": false,
20
+ "extendedDiagnostics": false,
21
+ "file": true,
22
+ "noEmit": false,
23
+ "out": true,
24
+ "outDir": true,
25
+ "p": true,
26
+ "plugins-json": true,
27
+ "project": true,
28
+ "quiet": false,
29
+ "singleThreaded": false,
30
+ "tsconfig": true,
31
+ "tsgo-args": true,
32
+ "verbose": false,
31
33
  }
package/linthost/host.go CHANGED
@@ -14,6 +14,7 @@ import (
14
14
  "errors"
15
15
  "fmt"
16
16
  "path/filepath"
17
+ "strings"
17
18
 
18
19
  shimast "github.com/microsoft/typescript-go/shim/ast"
19
20
  "github.com/microsoft/typescript-go/shim/bundled"
@@ -157,12 +158,19 @@ func (p *program) close() {
157
158
  }
158
159
  }
159
160
 
160
- // userSourceFiles returns the program's user-authored source files
161
- // (declaration files filtered out those belong to library typings).
161
+ // userSourceFiles returns the tsconfig-selected source files the lint engine
162
+ // owns. The tsconfig file list is the boundary: imported libraries, generated
163
+ // output, and JSON modules may still appear in Program.SourceFiles(), but lint
164
+ // and format should not walk them unless the project selected them as TS/JS
165
+ // source roots.
162
166
  func (p *program) userSourceFiles() []*shimast.SourceFile {
167
+ roots := p.userSourceFileNames()
163
168
  out := make([]*shimast.SourceFile, 0)
164
169
  for _, f := range p.tsProgram.SourceFiles() {
165
- if f == nil || f.IsDeclarationFile {
170
+ if f == nil {
171
+ continue
172
+ }
173
+ if _, ok := roots[canonicalProjectPath(p.cwd, f.FileName())]; !ok {
166
174
  continue
167
175
  }
168
176
  out = append(out, f)
@@ -170,6 +178,35 @@ func (p *program) userSourceFiles() []*shimast.SourceFile {
170
178
  return out
171
179
  }
172
180
 
181
+ func (p *program) userSourceFileNames() map[string]struct{} {
182
+ out := make(map[string]struct{})
183
+ if p == nil || p.parsed == nil || p.parsed.ParsedConfig == nil {
184
+ return out
185
+ }
186
+ for _, fileName := range p.parsed.ParsedConfig.FileNames {
187
+ if isLintSourceFileName(fileName) {
188
+ out[canonicalProjectPath(p.cwd, fileName)] = struct{}{}
189
+ }
190
+ }
191
+ return out
192
+ }
193
+
194
+ func canonicalProjectPath(cwd, fileName string) string {
195
+ if !filepath.IsAbs(fileName) {
196
+ fileName = filepath.Join(cwd, fileName)
197
+ }
198
+ return filepath.ToSlash(filepath.Clean(fileName))
199
+ }
200
+
201
+ func isLintSourceFileName(fileName string) bool {
202
+ switch strings.ToLower(filepath.Ext(fileName)) {
203
+ case ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs":
204
+ return true
205
+ default:
206
+ return false
207
+ }
208
+ }
209
+
173
210
  // programDiagnostics returns the bind + semantic diagnostics for the
174
211
  // loaded program. Same surface tsgo's CLI prints when you run a regular
175
212
  // `tsgo --noEmit`.