@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
@@ -0,0 +1,773 @@
1
+ package linthost
2
+
3
+ import (
4
+ "strings"
5
+
6
+ shimast "github.com/microsoft/typescript-go/shim/ast"
7
+ )
8
+
9
+ const reactRulePrefix = "react/"
10
+
11
+ type reactRule struct {
12
+ name string
13
+ }
14
+
15
+ func (r reactRule) Name() string { return reactRulePrefix + r.name }
16
+ func (r reactRule) Visits() []shimast.Kind {
17
+ switch r.name {
18
+ case "no-find-dom-node", "no-is-mounted":
19
+ return []shimast.Kind{shimast.KindCallExpression}
20
+ case "no-direct-mutation-state":
21
+ return []shimast.Kind{shimast.KindBinaryExpression}
22
+ case "no-unescaped-entities":
23
+ return []shimast.Kind{shimast.KindJsxText}
24
+ case "jsx-no-useless-fragment":
25
+ return []shimast.Kind{shimast.KindJsxFragment, shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
26
+ }
27
+ return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
28
+ }
29
+
30
+ func (r reactRule) Check(ctx *Context, node *shimast.Node) {
31
+ switch r.name {
32
+ case "jsx-key":
33
+ checkReactJSXKey(ctx, node)
34
+ case "jsx-no-duplicate-props":
35
+ checkReactJSXNoDuplicateProps(ctx, node)
36
+ case "no-array-index-key":
37
+ checkReactNoArrayIndexKey(ctx, node)
38
+ case "no-children-prop":
39
+ checkReactNoChildrenProp(ctx, node)
40
+ case "no-danger":
41
+ checkReactNoDanger(ctx, node)
42
+ case "no-danger-with-children":
43
+ checkReactNoDangerWithChildren(ctx, node)
44
+ case "no-direct-mutation-state":
45
+ checkReactNoDirectMutationState(ctx, node)
46
+ case "no-find-dom-node":
47
+ checkReactNoFindDOMNode(ctx, node)
48
+ case "no-is-mounted":
49
+ checkReactNoIsMounted(ctx, node)
50
+ case "no-string-refs":
51
+ checkReactNoStringRefs(ctx, node)
52
+ case "no-unescaped-entities":
53
+ checkReactNoUnescapedEntities(ctx, node)
54
+ case "button-has-type":
55
+ checkReactButtonHasType(ctx, node)
56
+ case "iframe-missing-sandbox":
57
+ checkReactIframeMissingSandbox(ctx, node)
58
+ case "jsx-no-script-url":
59
+ checkReactJSXNoScriptURL(ctx, node)
60
+ case "jsx-no-target-blank":
61
+ checkReactJSXNoTargetBlank(ctx, node)
62
+ case "jsx-no-useless-fragment":
63
+ checkReactJSXNoUselessFragment(ctx, node)
64
+ case "style-prop-object":
65
+ checkReactStylePropObject(ctx, node)
66
+ case "void-dom-elements-no-children":
67
+ checkReactVoidDOMElementsNoChildren(ctx, node)
68
+ }
69
+ }
70
+
71
+ func checkReactJSXKey(ctx *Context, node *shimast.Node) {
72
+ info := reactJSXElementFromNode(node)
73
+ if info.opening == nil || reactJSXHasAttr(info.attrs, "key") {
74
+ return
75
+ }
76
+ if reactJSXNeedsKey(node) {
77
+ ctx.Report(info.opening, "Missing key prop for element in array or iterator.")
78
+ }
79
+ }
80
+
81
+ func checkReactJSXNoDuplicateProps(ctx *Context, node *shimast.Node) {
82
+ info := reactJSXElementFromNode(node)
83
+ seen := map[string]*shimast.Node{}
84
+ for _, attr := range info.attrs {
85
+ name := strings.ToLower(attr.name)
86
+ if name == "" {
87
+ continue
88
+ }
89
+ if prev := seen[name]; prev != nil {
90
+ ctx.Report(attr.node, "No duplicate props allowed.")
91
+ continue
92
+ }
93
+ seen[name] = attr.node
94
+ }
95
+ }
96
+
97
+ func checkReactNoArrayIndexKey(ctx *Context, node *shimast.Node) {
98
+ info := reactJSXElementFromNode(node)
99
+ attr, ok := reactFindJSXAttr(info.attrs, "key")
100
+ if !ok {
101
+ return
102
+ }
103
+ name := reactJSXAttrExpressionIdentifier(attr)
104
+ if name != "" && reactJSXNeedsKey(node) && reactJSXKeyUsesMapIndex(node, name) {
105
+ ctx.Report(attr.node, "Do not use array index as key.")
106
+ }
107
+ }
108
+
109
+ func checkReactNoChildrenProp(ctx *Context, node *shimast.Node) {
110
+ info := reactJSXElementFromNode(node)
111
+ if attr, ok := reactFindJSXAttr(info.attrs, "children"); ok {
112
+ ctx.Report(attr.node, "Do not pass children as a prop.")
113
+ }
114
+ }
115
+
116
+ func checkReactNoDanger(ctx *Context, node *shimast.Node) {
117
+ info := reactJSXElementFromNode(node)
118
+ if attr, ok := reactFindJSXAttr(info.attrs, "dangerouslySetInnerHTML"); ok {
119
+ ctx.Report(attr.node, "Do not use dangerouslySetInnerHTML.")
120
+ }
121
+ }
122
+
123
+ func checkReactNoDangerWithChildren(ctx *Context, node *shimast.Node) {
124
+ info := reactJSXElementFromNode(node)
125
+ attr, ok := reactFindJSXAttr(info.attrs, "dangerouslySetInnerHTML")
126
+ if ok && (reactJSXHasContent(info.children) || reactJSXHasAttr(info.attrs, "children")) {
127
+ ctx.Report(attr.node, "Do not use dangerouslySetInnerHTML with children.")
128
+ }
129
+ }
130
+
131
+ func checkReactNoDirectMutationState(ctx *Context, node *shimast.Node) {
132
+ expr := node.AsBinaryExpression()
133
+ if expr == nil || expr.OperatorToken == nil || !isAssignmentOperator(expr.OperatorToken.Kind) {
134
+ return
135
+ }
136
+ if reactIsThisStateAccess(expr.Left) && !reactIsConstructorThisStateInitialization(node) {
137
+ ctx.Report(expr.Left, "Do not mutate this.state directly.")
138
+ }
139
+ }
140
+
141
+ func checkReactNoFindDOMNode(ctx *Context, node *shimast.Node) {
142
+ call := node.AsCallExpression()
143
+ if call == nil {
144
+ return
145
+ }
146
+ if callCalleeName(call) == "findDOMNode" || reactIsNamedPropertyCall(call, "findDOMNode") {
147
+ ctx.Report(node, "Do not use findDOMNode.")
148
+ }
149
+ }
150
+
151
+ func checkReactNoIsMounted(ctx *Context, node *shimast.Node) {
152
+ call := node.AsCallExpression()
153
+ if call == nil {
154
+ return
155
+ }
156
+ if callCalleeName(call) == "isMounted" || reactIsNamedPropertyCall(call, "isMounted") {
157
+ ctx.Report(node, "Do not use isMounted.")
158
+ }
159
+ }
160
+
161
+ func checkReactNoStringRefs(ctx *Context, node *shimast.Node) {
162
+ info := reactJSXElementFromNode(node)
163
+ attr, ok := reactFindJSXAttr(info.attrs, "ref")
164
+ if ok && attr.known && attr.value != "" {
165
+ ctx.Report(attr.node, "String refs are deprecated.")
166
+ }
167
+ }
168
+
169
+ func checkReactNoUnescapedEntities(ctx *Context, node *shimast.Node) {
170
+ text := nodeText(ctx.File, node)
171
+ if strings.TrimSpace(text) == "" {
172
+ return
173
+ }
174
+ for _, ch := range text {
175
+ switch ch {
176
+ case '>', '"', '\'', '}':
177
+ ctx.Report(node, "Unescaped HTML entity in JSX text.")
178
+ return
179
+ }
180
+ }
181
+ }
182
+
183
+ func checkReactButtonHasType(ctx *Context, node *shimast.Node) {
184
+ info := reactJSXElementFromNode(node)
185
+ if info.tag != "button" {
186
+ return
187
+ }
188
+ attr, ok := reactFindJSXAttr(info.attrs, "type")
189
+ if !ok {
190
+ ctx.Report(info.opening, "button elements must have an explicit type.")
191
+ return
192
+ }
193
+ if !attr.known {
194
+ return
195
+ }
196
+ switch strings.ToLower(strings.TrimSpace(attr.value)) {
197
+ case "button", "submit", "reset":
198
+ return
199
+ }
200
+ ctx.Report(attr.node, "button type must be button, submit, or reset.")
201
+ }
202
+
203
+ func checkReactIframeMissingSandbox(ctx *Context, node *shimast.Node) {
204
+ info := reactJSXElementFromNode(node)
205
+ if info.tag == "iframe" && !reactJSXHasAttr(info.attrs, "sandbox") {
206
+ ctx.Report(info.opening, "iframe elements must include a sandbox attribute.")
207
+ }
208
+ }
209
+
210
+ func checkReactJSXNoScriptURL(ctx *Context, node *shimast.Node) {
211
+ info := reactJSXElementFromNode(node)
212
+ for _, name := range []string{"href", "src", "action", "formAction"} {
213
+ attr, ok := reactFindJSXAttr(info.attrs, name)
214
+ if ok && attr.known && strings.HasPrefix(strings.ToLower(strings.TrimSpace(attr.value)), "javascript:") {
215
+ ctx.Report(attr.node, "Do not use javascript: URLs in JSX props.")
216
+ }
217
+ }
218
+ }
219
+
220
+ func checkReactJSXNoTargetBlank(ctx *Context, node *shimast.Node) {
221
+ info := reactJSXElementFromNode(node)
222
+ if info.opening == nil {
223
+ return
224
+ }
225
+ target, ok := reactFindJSXAttr(info.attrs, "target")
226
+ if !ok || !target.known {
227
+ return
228
+ }
229
+ if strings.TrimSpace(target.value) != "_blank" {
230
+ return
231
+ }
232
+ rel, ok := reactFindJSXAttr(info.attrs, "rel")
233
+ if ok && rel.known && strings.Contains(rel.value, "noreferrer") {
234
+ return
235
+ }
236
+ ctx.Report(info.opening, "JSX elements with `target=\"_blank\"` must include `rel=\"noreferrer\"` (or `noopener noreferrer`).")
237
+ }
238
+
239
+ func checkReactJSXNoUselessFragment(ctx *Context, node *shimast.Node) {
240
+ children, ok := reactUselessFragmentChildren(node)
241
+ if !ok {
242
+ return
243
+ }
244
+ meaningful := reactMeaningfulJSXChildren(children)
245
+ if len(meaningful) == 0 {
246
+ ctx.Report(node, "Fragment wraps a single element — return the child directly.")
247
+ return
248
+ }
249
+ if len(meaningful) == 1 {
250
+ switch meaningful[0].Kind {
251
+ case shimast.KindJsxElement, shimast.KindJsxSelfClosingElement, shimast.KindJsxFragment:
252
+ ctx.Report(node, "Fragment wraps a single element — return the child directly.")
253
+ }
254
+ }
255
+ }
256
+
257
+ // reactUselessFragmentChildren returns the child list of a fragment-like node
258
+ // — the short `<>...</>` form or an explicit `<Fragment>` / `<React.Fragment>`
259
+ // element — along with `true`. For any other node it returns false.
260
+ //
261
+ // JsxSelfClosingElement is included so an empty `<Fragment />` is still
262
+ // considered a useless wrapper.
263
+ func reactUselessFragmentChildren(node *shimast.Node) (*shimast.NodeList, bool) {
264
+ if node == nil {
265
+ return nil, false
266
+ }
267
+ switch node.Kind {
268
+ case shimast.KindJsxFragment:
269
+ frag := node.AsJsxFragment()
270
+ if frag == nil {
271
+ return nil, false
272
+ }
273
+ return frag.Children, true
274
+ case shimast.KindJsxElement:
275
+ el := node.AsJsxElement()
276
+ if el == nil || el.OpeningElement == nil {
277
+ return nil, false
278
+ }
279
+ if !reactIsFragmentTagName(el.OpeningElement.TagName()) {
280
+ return nil, false
281
+ }
282
+ return el.Children, true
283
+ case shimast.KindJsxSelfClosingElement:
284
+ el := node.AsJsxSelfClosingElement()
285
+ if el == nil {
286
+ return nil, false
287
+ }
288
+ if !reactIsFragmentTagName(el.TagName) {
289
+ return nil, false
290
+ }
291
+ return nil, true
292
+ }
293
+ return nil, false
294
+ }
295
+
296
+ // reactIsFragmentTagName reports whether a JSX tag-name node refers to React's
297
+ // fragment component — either the bare `Fragment` identifier or the
298
+ // `React.Fragment` qualified form.
299
+ func reactIsFragmentTagName(node *shimast.Node) bool {
300
+ if node == nil {
301
+ return false
302
+ }
303
+ if identifierText(node) == "Fragment" {
304
+ return true
305
+ }
306
+ if node.Kind == shimast.KindPropertyAccessExpression {
307
+ access := node.AsPropertyAccessExpression()
308
+ if access == nil {
309
+ return false
310
+ }
311
+ if identifierText(access.Expression) == "React" && identifierText(access.Name()) == "Fragment" {
312
+ return true
313
+ }
314
+ }
315
+ return false
316
+ }
317
+
318
+ // reactMeaningfulJSXChildren returns the JSX children that contribute rendered
319
+ // output. Whitespace-only JsxText and empty JsxExpression nodes are skipped so
320
+ // the caller sees only nodes that could replace the fragment.
321
+ func reactMeaningfulJSXChildren(children *shimast.NodeList) []*shimast.Node {
322
+ if children == nil {
323
+ return nil
324
+ }
325
+ out := make([]*shimast.Node, 0, len(children.Nodes))
326
+ for _, child := range children.Nodes {
327
+ if child == nil {
328
+ continue
329
+ }
330
+ if child.Kind == shimast.KindJsxText {
331
+ if strings.TrimSpace(nodeText(nilSafeSourceFile(child), child)) == "" {
332
+ continue
333
+ }
334
+ out = append(out, child)
335
+ continue
336
+ }
337
+ if child.Kind == shimast.KindJsxExpression {
338
+ expr := child.AsJsxExpression()
339
+ if expr == nil || expr.Expression == nil {
340
+ continue
341
+ }
342
+ }
343
+ out = append(out, child)
344
+ }
345
+ return out
346
+ }
347
+
348
+ func checkReactStylePropObject(ctx *Context, node *shimast.Node) {
349
+ info := reactJSXElementFromNode(node)
350
+ attr, ok := reactFindJSXAttr(info.attrs, "style")
351
+ if !ok || !attr.known {
352
+ return
353
+ }
354
+ if attr.value != "" {
355
+ ctx.Report(attr.node, "Style prop value must be an object.")
356
+ }
357
+ }
358
+
359
+ func checkReactVoidDOMElementsNoChildren(ctx *Context, node *shimast.Node) {
360
+ info := reactJSXElementFromNode(node)
361
+ if !reactVoidDOMElement(info.tag) {
362
+ return
363
+ }
364
+ if reactJSXHasContent(info.children) || reactJSXHasAttr(info.attrs, "children", "dangerouslySetInnerHTML") {
365
+ ctx.Report(info.opening, "Void DOM elements must not receive children.")
366
+ }
367
+ }
368
+
369
+ type reactJSXAttr struct {
370
+ node *shimast.Node
371
+ name string
372
+ value string
373
+ known bool
374
+ boolean bool
375
+ }
376
+
377
+ type reactJSXElementInfo struct {
378
+ opening *shimast.Node
379
+ tag string
380
+ attrs []reactJSXAttr
381
+ children *shimast.NodeList
382
+ }
383
+
384
+ func reactJSXElementFromNode(node *shimast.Node) reactJSXElementInfo {
385
+ info := reactJSXElementInfo{}
386
+ if node == nil {
387
+ return info
388
+ }
389
+ switch node.Kind {
390
+ case shimast.KindJsxElement:
391
+ el := node.AsJsxElement()
392
+ if el == nil || el.OpeningElement == nil {
393
+ return info
394
+ }
395
+ info.opening = el.OpeningElement.AsNode()
396
+ info.tag = reactJSXTagName(el.OpeningElement.TagName())
397
+ info.attrs = reactJSXAttrs(el.OpeningElement.Attributes())
398
+ info.children = el.Children
399
+ case shimast.KindJsxSelfClosingElement:
400
+ el := node.AsJsxSelfClosingElement()
401
+ if el == nil {
402
+ return info
403
+ }
404
+ info.opening = node
405
+ info.tag = reactJSXTagName(el.TagName)
406
+ info.attrs = reactJSXAttrs(el.Attributes)
407
+ }
408
+ return info
409
+ }
410
+
411
+ func reactJSXTagName(node *shimast.Node) string {
412
+ if node == nil {
413
+ return ""
414
+ }
415
+ if name := identifierText(node); name != "" {
416
+ return name
417
+ }
418
+ return ""
419
+ }
420
+
421
+ func reactJSXAttrs(node *shimast.Node) []reactJSXAttr {
422
+ if node == nil {
423
+ return nil
424
+ }
425
+ attrs := node.AsJsxAttributes()
426
+ if attrs == nil || attrs.Properties == nil {
427
+ return nil
428
+ }
429
+ out := make([]reactJSXAttr, 0, len(attrs.Properties.Nodes))
430
+ for _, prop := range attrs.Properties.Nodes {
431
+ attr := prop.AsJsxAttribute()
432
+ if attr == nil || attr.Name() == nil {
433
+ continue
434
+ }
435
+ name := reactJSXAttrName(attr.Name())
436
+ if name == "" {
437
+ continue
438
+ }
439
+ out = append(out, reactJSXAttr{
440
+ node: prop,
441
+ name: name,
442
+ value: reactJSXAttrValue(attr.Initializer),
443
+ known: reactJSXAttrValueKnown(attr.Initializer),
444
+ boolean: attr.Initializer == nil,
445
+ })
446
+ }
447
+ return out
448
+ }
449
+
450
+ func reactJSXAttrName(node *shimast.Node) string {
451
+ if node == nil {
452
+ return ""
453
+ }
454
+ if name := identifierText(node); name != "" {
455
+ return name
456
+ }
457
+ if node.Kind == shimast.KindJsxNamespacedName {
458
+ ns := node.AsJsxNamespacedName()
459
+ if ns != nil {
460
+ left := identifierText(ns.Namespace)
461
+ right := identifierText(ns.Name())
462
+ if left != "" && right != "" {
463
+ return left + ":" + right
464
+ }
465
+ }
466
+ }
467
+ return ""
468
+ }
469
+
470
+ func reactFindJSXAttr(attrs []reactJSXAttr, name string) (reactJSXAttr, bool) {
471
+ for _, attr := range attrs {
472
+ if attr.name == name {
473
+ return attr, true
474
+ }
475
+ }
476
+ return reactJSXAttr{}, false
477
+ }
478
+
479
+ func reactJSXHasAttr(attrs []reactJSXAttr, names ...string) bool {
480
+ for _, name := range names {
481
+ if _, ok := reactFindJSXAttr(attrs, name); ok {
482
+ return true
483
+ }
484
+ }
485
+ return false
486
+ }
487
+
488
+ func reactJSXAttrValue(node *shimast.Node) string {
489
+ if node == nil {
490
+ return "true"
491
+ }
492
+ if text := stringLiteralText(node); text != "" || node.Kind == shimast.KindStringLiteral || node.Kind == shimast.KindNoSubstitutionTemplateLiteral {
493
+ return text
494
+ }
495
+ if node.Kind == shimast.KindJsxExpression {
496
+ expr := node.AsJsxExpression()
497
+ if expr == nil || expr.Expression == nil {
498
+ return ""
499
+ }
500
+ if text := stringLiteralText(expr.Expression); text != "" || expr.Expression.Kind == shimast.KindStringLiteral || expr.Expression.Kind == shimast.KindNoSubstitutionTemplateLiteral {
501
+ return text
502
+ }
503
+ }
504
+ return ""
505
+ }
506
+
507
+ func reactJSXAttrValueKnown(node *shimast.Node) bool {
508
+ if node == nil {
509
+ return true
510
+ }
511
+ switch node.Kind {
512
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
513
+ return true
514
+ case shimast.KindJsxExpression:
515
+ expr := node.AsJsxExpression()
516
+ if expr == nil || expr.Expression == nil {
517
+ return true
518
+ }
519
+ switch expr.Expression.Kind {
520
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
521
+ return true
522
+ }
523
+ }
524
+ return false
525
+ }
526
+
527
+ func reactJSXAttrExpressionIdentifier(attr reactJSXAttr) string {
528
+ if attr.node == nil {
529
+ return ""
530
+ }
531
+ jsxAttr := attr.node.AsJsxAttribute()
532
+ if jsxAttr == nil || jsxAttr.Initializer == nil || jsxAttr.Initializer.Kind != shimast.KindJsxExpression {
533
+ return ""
534
+ }
535
+ expr := jsxAttr.Initializer.AsJsxExpression()
536
+ if expr == nil || expr.Expression == nil {
537
+ return ""
538
+ }
539
+ return identifierText(stripParens(expr.Expression))
540
+ }
541
+
542
+ func reactJSXHasContent(children *shimast.NodeList) bool {
543
+ if children == nil {
544
+ return false
545
+ }
546
+ for _, child := range children.Nodes {
547
+ if child == nil {
548
+ continue
549
+ }
550
+ if child.Kind == shimast.KindJsxText {
551
+ if strings.TrimSpace(nodeText(nilSafeSourceFile(child), child)) != "" {
552
+ return true
553
+ }
554
+ continue
555
+ }
556
+ if child.Kind == shimast.KindJsxExpression {
557
+ expr := child.AsJsxExpression()
558
+ if expr == nil || expr.Expression == nil {
559
+ continue
560
+ }
561
+ }
562
+ return true
563
+ }
564
+ return false
565
+ }
566
+
567
+ func nilSafeSourceFile(node *shimast.Node) *shimast.SourceFile {
568
+ for cur := node; cur != nil; cur = cur.Parent {
569
+ if cur.Kind == shimast.KindSourceFile {
570
+ return cur.AsSourceFile()
571
+ }
572
+ }
573
+ return nil
574
+ }
575
+
576
+ func reactJSXNeedsKey(node *shimast.Node) bool {
577
+ if reactInsideArrayLiteral(node) {
578
+ return true
579
+ }
580
+ return reactIsMapCallbackReturn(node)
581
+ }
582
+
583
+ func reactInsideArrayLiteral(node *shimast.Node) bool {
584
+ for cur := node.Parent; cur != nil; cur = cur.Parent {
585
+ switch cur.Kind {
586
+ case shimast.KindArrayLiteralExpression:
587
+ return true
588
+ case shimast.KindJsxElement, shimast.KindJsxSelfClosingElement:
589
+ return false
590
+ }
591
+ if isFunctionLikeKind(cur) {
592
+ return false
593
+ }
594
+ }
595
+ return false
596
+ }
597
+
598
+ func reactIsMapCallbackReturn(node *shimast.Node) bool {
599
+ fn := reactNearestFunctionLike(node)
600
+ if fn == nil || !reactIsMapCallback(fn) {
601
+ return false
602
+ }
603
+ body := fn.Body()
604
+ if reactJSXIsReturnedExpression(node, body) {
605
+ return true
606
+ }
607
+ for cur := node.Parent; cur != nil && cur != fn; cur = cur.Parent {
608
+ if cur.Kind == shimast.KindReturnStatement {
609
+ ret := cur.AsReturnStatement()
610
+ return ret != nil && reactJSXIsReturnedExpression(node, ret.Expression)
611
+ }
612
+ }
613
+ return false
614
+ }
615
+
616
+ func reactJSXIsReturnedExpression(node, expr *shimast.Node) bool {
617
+ target := stripParens(expr)
618
+ if target == nil {
619
+ return false
620
+ }
621
+ for cur := node; cur != nil; cur = cur.Parent {
622
+ if cur != node && (cur.Kind == shimast.KindJsxElement || cur.Kind == shimast.KindJsxSelfClosingElement) {
623
+ return false
624
+ }
625
+ if stripParens(cur) == target {
626
+ return true
627
+ }
628
+ if cur != node {
629
+ switch cur.Kind {
630
+ case shimast.KindParenthesizedExpression, shimast.KindConditionalExpression, shimast.KindBinaryExpression:
631
+ default:
632
+ return false
633
+ }
634
+ }
635
+ }
636
+ return false
637
+ }
638
+
639
+ func reactIsMapCallback(fn *shimast.Node) bool {
640
+ if fn == nil || fn.Parent == nil || fn.Parent.Kind != shimast.KindCallExpression {
641
+ return false
642
+ }
643
+ call := fn.Parent.AsCallExpression()
644
+ if call == nil || call.Arguments == nil {
645
+ return false
646
+ }
647
+ found := false
648
+ for _, arg := range call.Arguments.Nodes {
649
+ if stripParens(arg) == fn {
650
+ found = true
651
+ break
652
+ }
653
+ }
654
+ if !found {
655
+ return false
656
+ }
657
+ _, method, ok := reactPropertyAccessParts(call.Expression)
658
+ return ok && method == "map"
659
+ }
660
+
661
+ func reactJSXKeyUsesMapIndex(node *shimast.Node, name string) bool {
662
+ fn := reactNearestFunctionLike(node)
663
+ if fn == nil || !reactIsMapCallback(fn) {
664
+ return false
665
+ }
666
+ params := fn.Parameters()
667
+ if len(params) < 2 {
668
+ return false
669
+ }
670
+ param := params[1].AsParameterDeclaration()
671
+ return param != nil && identifierText(param.Name()) == name
672
+ }
673
+
674
+ func reactPropertyAccessParts(node *shimast.Node) (*shimast.Node, string, bool) {
675
+ node = stripParens(node)
676
+ if node == nil || node.Kind != shimast.KindPropertyAccessExpression {
677
+ return nil, "", false
678
+ }
679
+ access := node.AsPropertyAccessExpression()
680
+ if access == nil {
681
+ return nil, "", false
682
+ }
683
+ name := identifierText(access.Name())
684
+ return access.Expression, name, name != ""
685
+ }
686
+
687
+ func reactIsNamedPropertyCall(call *shimast.CallExpression, name string) bool {
688
+ if call == nil {
689
+ return false
690
+ }
691
+ _, prop, ok := reactPropertyAccessParts(call.Expression)
692
+ return ok && prop == name
693
+ }
694
+
695
+ func reactIsThisStateAccess(node *shimast.Node) bool {
696
+ for cur := stripParens(node); cur != nil; {
697
+ obj, prop, ok := reactPropertyAccessParts(cur)
698
+ if !ok {
699
+ return false
700
+ }
701
+ if prop == "state" && obj != nil && obj.Kind == shimast.KindThisKeyword {
702
+ return true
703
+ }
704
+ cur = obj
705
+ }
706
+ return false
707
+ }
708
+
709
+ func reactIsConstructorThisStateInitialization(node *shimast.Node) bool {
710
+ if !reactIsExactThisStateAssignment(node) {
711
+ return false
712
+ }
713
+ for cur := node.Parent; cur != nil; cur = cur.Parent {
714
+ if cur.Kind == shimast.KindConstructor {
715
+ return true
716
+ }
717
+ if isFunctionLikeKind(cur) && cur.Kind != shimast.KindConstructor {
718
+ return false
719
+ }
720
+ }
721
+ return false
722
+ }
723
+
724
+ func reactIsExactThisStateAssignment(node *shimast.Node) bool {
725
+ expr := node.AsBinaryExpression()
726
+ if expr == nil || expr.Left == nil {
727
+ return false
728
+ }
729
+ obj, prop, ok := reactPropertyAccessParts(expr.Left)
730
+ return ok && prop == "state" && obj != nil && obj.Kind == shimast.KindThisKeyword
731
+ }
732
+
733
+ func reactVoidDOMElement(tag string) bool {
734
+ switch tag {
735
+ case "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr":
736
+ return true
737
+ }
738
+ return false
739
+ }
740
+
741
+ func reactNearestFunctionLike(node *shimast.Node) *shimast.Node {
742
+ for cur := node.Parent; cur != nil; cur = cur.Parent {
743
+ if isFunctionLikeKind(cur) {
744
+ return cur
745
+ }
746
+ }
747
+ return nil
748
+ }
749
+
750
+ func init() {
751
+ for _, name := range []string{
752
+ "button-has-type",
753
+ "iframe-missing-sandbox",
754
+ "jsx-key",
755
+ "jsx-no-duplicate-props",
756
+ "jsx-no-script-url",
757
+ "jsx-no-target-blank",
758
+ "jsx-no-useless-fragment",
759
+ "no-array-index-key",
760
+ "no-children-prop",
761
+ "no-danger",
762
+ "no-danger-with-children",
763
+ "no-direct-mutation-state",
764
+ "no-find-dom-node",
765
+ "no-is-mounted",
766
+ "no-string-refs",
767
+ "no-unescaped-entities",
768
+ "style-prop-object",
769
+ "void-dom-elements-no-children",
770
+ } {
771
+ Register(reactRule{name: name})
772
+ }
773
+ }