@ttsc/lint 0.13.1 → 0.14.0-dev.20260528.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 (464) 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 +35 -10
  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/config.go +48 -17
  116. package/linthost/config_format.go +9 -9
  117. package/linthost/directives.go +67 -25
  118. package/linthost/dispatch.go +14 -3
  119. package/linthost/engine.go +152 -31
  120. package/linthost/fix.go +2 -2
  121. package/linthost/lsp.go +747 -0
  122. package/linthost/print_dispatch.go +2 -2
  123. package/linthost/print_nodes_function.go +2 -2
  124. package/linthost/print_nodes_object.go +1 -1
  125. package/linthost/rule_names.go +14 -0
  126. package/linthost/rules_arrays.go +2 -2
  127. package/linthost/rules_boundaries.go +532 -0
  128. package/linthost/rules_camelcase.go +133 -0
  129. package/linthost/rules_complexity.go +94 -0
  130. package/linthost/rules_consistent_return.go +130 -0
  131. package/linthost/rules_console.go +1 -1
  132. package/linthost/rules_core_extra.go +739 -0
  133. package/linthost/rules_curly.go +70 -0
  134. package/linthost/rules_cypress.go +608 -0
  135. package/linthost/rules_debugger.go +2 -2
  136. package/linthost/rules_default_case.go +39 -0
  137. package/linthost/rules_default_case_last.go +46 -0
  138. package/linthost/rules_dupes.go +3 -3
  139. package/linthost/rules_empty.go +3 -3
  140. package/linthost/rules_escape.go +44 -24
  141. package/linthost/rules_eval.go +2 -2
  142. package/linthost/rules_finally.go +2 -2
  143. package/linthost/rules_format_jsdoc.go +1 -1
  144. package/linthost/rules_format_print_width.go +9 -9
  145. package/linthost/rules_format_quotes.go +1 -1
  146. package/linthost/rules_format_semi.go +1 -1
  147. package/linthost/rules_format_sort_imports.go +1 -1
  148. package/linthost/rules_format_trailing_comma.go +1 -1
  149. package/linthost/rules_functional.go +967 -0
  150. package/linthost/rules_gap.go +24 -45
  151. package/linthost/rules_grouped_accessor_pairs.go +87 -0
  152. package/linthost/rules_guard_for_in.go +198 -0
  153. package/linthost/rules_id_length.go +77 -0
  154. package/linthost/rules_imports.go +70 -2
  155. package/linthost/rules_init_declarations.go +53 -0
  156. package/linthost/rules_jest.go +618 -0
  157. package/linthost/rules_jsdoc.go +502 -0
  158. package/linthost/rules_jsx_a11y.go +1264 -0
  159. package/linthost/rules_logic.go +12 -12
  160. package/linthost/rules_loops.go +1 -1
  161. package/linthost/rules_max_classes_per_file.go +58 -0
  162. package/linthost/rules_max_depth.go +83 -0
  163. package/linthost/rules_max_lines.go +101 -0
  164. package/linthost/rules_max_lines_per_function.go +57 -0
  165. package/linthost/rules_max_nested_callbacks.go +61 -0
  166. package/linthost/rules_max_params.go +49 -0
  167. package/linthost/rules_max_statements.go +59 -0
  168. package/linthost/rules_misc.go +1 -1
  169. package/linthost/rules_nextjs.go +697 -0
  170. package/linthost/rules_no_else_return.go +75 -0
  171. package/linthost/rules_no_empty_named_blocks.go +91 -0
  172. package/linthost/rules_no_extend_native.go +51 -0
  173. package/linthost/rules_no_import_assign.go +140 -0
  174. package/linthost/rules_no_invalid_this.go +52 -0
  175. package/linthost/rules_no_loop_func.go +85 -0
  176. package/linthost/rules_no_magic_numbers.go +123 -0
  177. package/linthost/rules_no_mixed_operators.go +80 -0
  178. package/linthost/rules_no_param_reassign.go +98 -0
  179. package/linthost/rules_no_redeclare.go +270 -0
  180. package/linthost/rules_no_restricted_imports.go +105 -0
  181. package/linthost/rules_no_restricted_syntax.go +49 -0
  182. package/linthost/rules_no_shadow.go +147 -0
  183. package/linthost/rules_no_unreachable.go +77 -0
  184. package/linthost/rules_no_useless_assignment.go +120 -0
  185. package/linthost/rules_no_useless_constructor.go +245 -0
  186. package/linthost/rules_no_useless_return.go +51 -0
  187. package/linthost/rules_params.go +1 -1
  188. package/linthost/rules_playwright.go +788 -0
  189. package/linthost/rules_prefer_arrow_callback.go +124 -0
  190. package/linthost/rules_prefer_destructuring.go +98 -0
  191. package/linthost/rules_prefer_named_capture_group.go +129 -0
  192. package/linthost/rules_prefer_rest_params.go +94 -0
  193. package/linthost/rules_problems.go +41 -30
  194. package/linthost/rules_promise.go +1128 -73
  195. package/linthost/rules_protos.go +2 -2
  196. package/linthost/rules_react.go +773 -0
  197. package/linthost/rules_react_extras.go +227 -0
  198. package/linthost/rules_react_hooks.go +848 -0
  199. package/linthost/rules_react_perf.go +293 -0
  200. package/linthost/rules_react_refresh.go +583 -0
  201. package/linthost/rules_regexp.go +410 -0
  202. package/linthost/rules_security.go +830 -0
  203. package/linthost/rules_self.go +2 -2
  204. package/linthost/rules_solid.go +1073 -0
  205. package/linthost/rules_sort_imports.go +69 -0
  206. package/linthost/rules_sort_keys.go +134 -0
  207. package/linthost/rules_storybook.go +1176 -0
  208. package/linthost/rules_strings.go +4 -4
  209. package/linthost/rules_suggestions.go +38 -38
  210. package/linthost/rules_tanstack_query.go +844 -0
  211. package/linthost/rules_testing_library.go +1204 -0
  212. package/linthost/rules_throw.go +1 -1
  213. package/linthost/rules_ts.go +19 -19
  214. package/linthost/rules_ts_async.go +399 -0
  215. package/linthost/rules_ts_class_literal_property_style.go +115 -0
  216. package/linthost/rules_ts_consistent_type_exports.go +256 -0
  217. package/linthost/rules_ts_explicit_function_return_type.go +67 -0
  218. package/linthost/rules_ts_extra.go +155 -36
  219. package/linthost/rules_ts_no_base_to_string.go +218 -0
  220. package/linthost/rules_ts_no_confusing_void.go +72 -0
  221. package/linthost/rules_ts_no_deprecated.go +195 -0
  222. package/linthost/rules_ts_no_for_in_array.go +84 -0
  223. package/linthost/rules_ts_no_magic_numbers.go +124 -0
  224. package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
  225. package/linthost/rules_ts_no_misused_spread.go +104 -0
  226. package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
  227. package/linthost/rules_ts_no_restricted_types.go +62 -0
  228. package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
  229. package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
  230. package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
  231. package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
  232. package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
  233. package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
  234. package/linthost/rules_ts_no_unsafe_argument.go +82 -0
  235. package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
  236. package/linthost/rules_ts_no_unsafe_call.go +82 -0
  237. package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
  238. package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
  239. package/linthost/rules_ts_no_unsafe_return.go +126 -0
  240. package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
  241. package/linthost/rules_ts_no_useless_constructor.go +52 -0
  242. package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
  243. package/linthost/rules_ts_parameter_properties.go +38 -0
  244. package/linthost/rules_ts_prefer_find.go +178 -0
  245. package/linthost/rules_ts_prefer_includes.go +255 -0
  246. package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
  247. package/linthost/rules_ts_prefer_optional_chain.go +228 -0
  248. package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
  249. package/linthost/rules_ts_prefer_readonly.go +73 -0
  250. package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
  251. package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
  252. package/linthost/rules_ts_prefer_return_this_type.go +135 -0
  253. package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
  254. package/linthost/rules_ts_promise_function_async.go +77 -0
  255. package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
  256. package/linthost/rules_ts_require_array_sort_compare.go +96 -0
  257. package/linthost/rules_ts_restrict_plus_operands.go +127 -0
  258. package/linthost/rules_ts_restrict_template_expressions.go +95 -0
  259. package/linthost/rules_ts_simple.go +387 -0
  260. package/linthost/rules_ts_sort_type_constituents.go +144 -0
  261. package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
  262. package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
  263. package/linthost/rules_ts_unbound_method.go +126 -0
  264. package/linthost/rules_tsdoc.go +136 -0
  265. package/linthost/rules_unicorn_better_regex.go +27 -0
  266. package/linthost/rules_unicorn_catch_error_name.go +46 -0
  267. package/linthost/rules_unicorn_consistent_assert.go +47 -0
  268. package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
  269. package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
  270. package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
  271. package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
  272. package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
  273. package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
  274. package/linthost/rules_unicorn_custom_error_definition.go +138 -0
  275. package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
  276. package/linthost/rules_unicorn_error_message.go +64 -0
  277. package/linthost/rules_unicorn_escape_case.go +51 -0
  278. package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
  279. package/linthost/rules_unicorn_explicit_length_check.go +88 -0
  280. package/linthost/rules_unicorn_filename_case.go +29 -0
  281. package/linthost/rules_unicorn_import_style.go +28 -0
  282. package/linthost/rules_unicorn_isolated_functions.go +29 -0
  283. package/linthost/rules_unicorn_new_for_builtins.go +87 -0
  284. package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
  285. package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
  286. package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
  287. package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
  288. package/linthost/rules_unicorn_no_array_for_each.go +40 -0
  289. package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
  290. package/linthost/rules_unicorn_no_array_reduce.go +41 -0
  291. package/linthost/rules_unicorn_no_array_reverse.go +44 -0
  292. package/linthost/rules_unicorn_no_array_sort.go +43 -0
  293. package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
  294. package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
  295. package/linthost/rules_unicorn_no_console_spaces.go +61 -0
  296. package/linthost/rules_unicorn_no_document_cookie.go +41 -0
  297. package/linthost/rules_unicorn_no_empty_file.go +44 -0
  298. package/linthost/rules_unicorn_no_for_loop.go +102 -0
  299. package/linthost/rules_unicorn_no_hex_escape.go +42 -0
  300. package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
  301. package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
  302. package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
  303. package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
  304. package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
  305. package/linthost/rules_unicorn_no_lonely_if.go +44 -0
  306. package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
  307. package/linthost/rules_unicorn_no_named_default.go +54 -0
  308. package/linthost/rules_unicorn_no_negated_condition.go +85 -0
  309. package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
  310. package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
  311. package/linthost/rules_unicorn_no_new_array.go +30 -0
  312. package/linthost/rules_unicorn_no_new_buffer.go +31 -0
  313. package/linthost/rules_unicorn_no_null.go +24 -0
  314. package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
  315. package/linthost/rules_unicorn_no_process_exit.go +43 -0
  316. package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
  317. package/linthost/rules_unicorn_no_static_only_class.go +56 -0
  318. package/linthost/rules_unicorn_no_thenable.go +74 -0
  319. package/linthost/rules_unicorn_no_this_assignment.go +35 -0
  320. package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
  321. package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
  322. package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
  323. package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
  324. package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
  325. package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
  326. package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
  327. package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
  328. package/linthost/rules_unicorn_no_unused_properties.go +30 -0
  329. package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
  330. package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
  331. package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
  332. package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
  333. package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
  334. package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
  335. package/linthost/rules_unicorn_no_useless_spread.go +67 -0
  336. package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
  337. package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
  338. package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
  339. package/linthost/rules_unicorn_number_literal_case.go +69 -0
  340. package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
  341. package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
  342. package/linthost/rules_unicorn_prefer_array_find.go +51 -0
  343. package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
  344. package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
  345. package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
  346. package/linthost/rules_unicorn_prefer_array_some.go +67 -0
  347. package/linthost/rules_unicorn_prefer_at.go +77 -0
  348. package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
  349. package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
  350. package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
  351. package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
  352. package/linthost/rules_unicorn_prefer_code_point.go +49 -0
  353. package/linthost/rules_unicorn_prefer_date_now.go +78 -0
  354. package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
  355. package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
  356. package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
  357. package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
  358. package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
  359. package/linthost/rules_unicorn_prefer_event_target.go +36 -0
  360. package/linthost/rules_unicorn_prefer_export_from.go +129 -0
  361. package/linthost/rules_unicorn_prefer_global_this.go +127 -0
  362. package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
  363. package/linthost/rules_unicorn_prefer_includes.go +149 -0
  364. package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
  365. package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
  366. package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
  367. package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
  368. package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
  369. package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
  370. package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
  371. package/linthost/rules_unicorn_prefer_module.go +50 -0
  372. package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
  373. package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
  374. package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
  375. package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
  376. package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
  377. package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
  378. package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
  379. package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
  380. package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
  381. package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
  382. package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
  383. package/linthost/rules_unicorn_prefer_set_has.go +42 -0
  384. package/linthost/rules_unicorn_prefer_set_size.go +48 -0
  385. package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
  386. package/linthost/rules_unicorn_prefer_single_call.go +96 -0
  387. package/linthost/rules_unicorn_prefer_spread.go +51 -0
  388. package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
  389. package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
  390. package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
  391. package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
  392. package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
  393. package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
  394. package/linthost/rules_unicorn_prefer_switch.go +103 -0
  395. package/linthost/rules_unicorn_prefer_ternary.go +71 -0
  396. package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
  397. package/linthost/rules_unicorn_prefer_type_error.go +127 -0
  398. package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
  399. package/linthost/rules_unicorn_relative_url_style.go +49 -0
  400. package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
  401. package/linthost/rules_unicorn_require_module_attributes.go +54 -0
  402. package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
  403. package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
  404. package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
  405. package/linthost/rules_unicorn_string_content.go +27 -0
  406. package/linthost/rules_unicorn_switch_case_braces.go +42 -0
  407. package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
  408. package/linthost/rules_unicorn_template_indent.go +28 -0
  409. package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
  410. package/linthost/rules_unicorn_throw_new_error.go +61 -0
  411. package/linthost/rules_var.go +4 -4
  412. package/linthost/rules_vitest.go +568 -0
  413. package/package.json +3 -3
  414. package/plugin/main.go +49 -3
  415. package/rule/astutil/astutil.go +10 -3
  416. package/rule/rule.go +12 -10
  417. package/src/defaultFormat.ts +11 -8
  418. package/src/index.ts +40 -16
  419. package/src/structures/ITtscLintConfig.ts +12 -6
  420. package/src/structures/{ITtscLintFormatConfig.ts → ITtscLintFormat.ts} +2 -2
  421. package/src/structures/TtscLintRuleSetting.ts +55 -0
  422. package/src/structures/index.ts +3 -4
  423. package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +128 -0
  424. package/src/structures/rules/ITtscLintBoundariesRules.ts +95 -0
  425. package/src/structures/rules/ITtscLintContributorRules.ts +21 -0
  426. package/src/structures/rules/ITtscLintCoreRules.ts +1406 -0
  427. package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
  428. package/src/structures/rules/ITtscLintCypressRules.ts +145 -0
  429. package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +213 -0
  430. package/src/structures/rules/ITtscLintFunctionalRules.ts +202 -0
  431. package/src/structures/rules/ITtscLintJestRules.ts +220 -0
  432. package/src/structures/rules/ITtscLintJsdocRules.ts +153 -0
  433. package/src/structures/rules/ITtscLintJsxA11yRules.ts +339 -0
  434. package/src/structures/rules/ITtscLintNextjsRules.ts +206 -0
  435. package/src/structures/rules/ITtscLintPlaywrightRules.ts +361 -0
  436. package/src/structures/rules/ITtscLintPromiseRules.ts +205 -0
  437. package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
  438. package/src/structures/rules/ITtscLintReactPerfRules.ts +55 -0
  439. package/src/structures/rules/ITtscLintReactRuleOptions.ts +43 -0
  440. package/src/structures/rules/ITtscLintReactRules.ts +321 -0
  441. package/src/structures/rules/ITtscLintRegexpRules.ts +212 -0
  442. package/src/structures/rules/ITtscLintRuleOptionsMap.ts +89 -0
  443. package/src/structures/rules/ITtscLintRules.ts +77 -0
  444. package/src/structures/rules/ITtscLintSecurityRules.ts +162 -0
  445. package/src/structures/rules/ITtscLintSolidRules.ts +202 -0
  446. package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +23 -0
  447. package/src/structures/rules/ITtscLintStorybookRules.ts +193 -0
  448. package/src/structures/rules/ITtscLintTanstackQueryRules.ts +102 -0
  449. package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +24 -0
  450. package/src/structures/rules/ITtscLintTestingLibraryRules.ts +342 -0
  451. package/src/structures/rules/ITtscLintTypeScriptRules.ts +1230 -0
  452. package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
  453. package/src/structures/rules/ITtscLintVitestRules.ts +154 -0
  454. package/src/structures/rules/index.ts +31 -0
  455. package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
  456. package/lib/structures/TtscLintRule.d.ts +0 -17
  457. package/lib/structures/TtscLintRule.js.map +0 -1
  458. package/lib/structures/TtscLintRuleMap.d.ts +0 -46
  459. package/lib/structures/TtscLintRuleMap.js.map +0 -1
  460. package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
  461. package/lib/structures/TtscLintRuleOptions.js.map +0 -1
  462. package/src/structures/TtscLintRule.ts +0 -160
  463. package/src/structures/TtscLintRuleMap.ts +0 -55
  464. 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 {
@@ -393,6 +478,40 @@ type boundRule struct {
393
478
  ctx *Context
394
479
  }
395
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
+
396
515
  // runFile is the per-file driver. The visitor is allocated once per file
397
516
  // to keep the per-node hot path branch-free; it visits children
398
517
  // post-order so parents see their already-checked subtrees.
@@ -415,9 +534,12 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
415
534
  // Context for every (node, rule) pair, which on a large program meant
416
535
  // millions of short-lived heap allocations and the GC pressure they
417
536
  // carry. Rules never mutate their Context, so reuse is safe.
418
- byKind := make(map[shimast.Kind][]boundRule, len(e.rules))
537
+ byKind := make([][]boundRule, len(e.rules))
419
538
  ctxByRule := make(map[string]*Context, len(e.enabled))
420
539
  for kind, rules := range e.rules {
540
+ if len(rules) == 0 {
541
+ continue
542
+ }
421
543
  for _, rule := range rules {
422
544
  name := rule.Name()
423
545
  ctx, built := ctxByRule[name]
@@ -444,40 +566,39 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
444
566
  }
445
567
  }
446
568
 
447
- var walk func(node *shimast.Node)
448
- walk = func(node *shimast.Node) {
449
- if node == nil {
450
- return
451
- }
452
- for _, bound := range byKind[node.Kind] {
453
- runRuleCheck(bound.rule, bound.ctx, node, collect)
454
- }
455
- node.ForEachChild(func(child *shimast.Node) bool {
456
- walk(child)
457
- return false // visit every child
458
- })
459
- }
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
460
577
 
461
578
  // SourceFile dispatches into its statement list directly; we walk
462
579
  // statements explicitly so the file node itself can be inspected by
463
580
  // rules (e.g., `ban-ts-comment` reads CommentDirectives off the
464
581
  // SourceFile).
465
- for _, bound := range byKind[shimast.KindSourceFile] {
466
- 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
+ }
467
586
  }
468
587
 
469
588
  statements := file.Statements
470
589
  if statements != nil {
471
590
  for _, stmt := range statements.Nodes {
472
- walk(stmt)
591
+ w.walk(stmt)
473
592
  }
474
593
  }
594
+ directives := parseLintInlineDirectives(file)
595
+ e.collectUnknownDirectiveRules(directives)
475
596
  // Apply inline-disable filtering even for files with no statement
476
597
  // list. A SourceFile-level rule that fires on a `// ttsc-lint-disable`
477
598
  // comment must still honor the directive; early-returning before
478
599
  // the filter would silently leak those findings into the diagnostic
479
600
  // stream.
480
- return filterInlineDisabledFindings(file, collected)
601
+ return filterInlineDisabledFindingsWithDirectives(file, collected, directives)
481
602
  }
482
603
 
483
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