@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.
- package/README.md +948 -69
- package/lib/defaultFormat.d.ts +9 -6
- package/lib/defaultFormat.js +9 -6
- package/lib/defaultFormat.js.map +1 -1
- package/lib/index.d.ts +46 -0
- package/lib/index.js +35 -10
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +12 -6
- package/lib/structures/{ITtscLintFormatConfig.d.ts → ITtscLintFormat.d.ts} +2 -2
- package/lib/structures/{TtscLintRuleMap.js → ITtscLintFormat.js} +1 -1
- package/lib/structures/ITtscLintFormat.js.map +1 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
- package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
- package/lib/structures/TtscLintRuleSetting.js.map +1 -0
- package/lib/structures/index.d.ts +3 -4
- package/lib/structures/index.js +3 -4
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +104 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +14 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +79 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +17 -0
- package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
- package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +1257 -0
- package/lib/structures/{ITtscLintFormatConfig.js → rules/ITtscLintCoreRules.js} +1 -1
- package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCypressRules.d.ts +129 -0
- package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
- package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +157 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +167 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJestRules.d.ts +201 -0
- package/lib/structures/rules/ITtscLintJestRules.js +3 -0
- package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJsdocRules.d.ts +140 -0
- package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
- package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +302 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintNextjsRules.d.ts +185 -0
- package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
- package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +330 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintPromiseRules.d.ts +188 -0
- package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
- package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +51 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +39 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js +12 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRules.d.ts +289 -0
- package/lib/structures/rules/ITtscLintReactRules.js +3 -0
- package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +190 -0
- package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
- package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +66 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
- package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
- package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
- package/lib/structures/rules/ITtscLintRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintSecurityRules.d.ts +148 -0
- package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
- package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +181 -0
- package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
- package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +21 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintStorybookRules.d.ts +174 -0
- package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
- package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +94 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +22 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +310 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1132 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
- package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintVitestRules.d.ts +141 -0
- package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
- package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
- package/lib/structures/rules/index.d.ts +31 -0
- package/lib/structures/rules/index.js +48 -0
- package/lib/structures/rules/index.js.map +1 -0
- package/linthost/ast_helpers.go +30 -5
- package/linthost/config.go +48 -17
- package/linthost/config_format.go +9 -9
- package/linthost/directives.go +67 -25
- package/linthost/dispatch.go +14 -3
- package/linthost/engine.go +152 -31
- package/linthost/fix.go +2 -2
- package/linthost/lsp.go +747 -0
- package/linthost/print_dispatch.go +2 -2
- package/linthost/print_nodes_function.go +2 -2
- package/linthost/print_nodes_object.go +1 -1
- package/linthost/rule_names.go +14 -0
- package/linthost/rules_arrays.go +2 -2
- package/linthost/rules_boundaries.go +532 -0
- package/linthost/rules_camelcase.go +133 -0
- package/linthost/rules_complexity.go +94 -0
- package/linthost/rules_consistent_return.go +130 -0
- package/linthost/rules_console.go +1 -1
- package/linthost/rules_core_extra.go +739 -0
- package/linthost/rules_curly.go +70 -0
- package/linthost/rules_cypress.go +608 -0
- package/linthost/rules_debugger.go +2 -2
- package/linthost/rules_default_case.go +39 -0
- package/linthost/rules_default_case_last.go +46 -0
- package/linthost/rules_dupes.go +3 -3
- package/linthost/rules_empty.go +3 -3
- package/linthost/rules_escape.go +44 -24
- package/linthost/rules_eval.go +2 -2
- package/linthost/rules_finally.go +2 -2
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_print_width.go +9 -9
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +1 -1
- package/linthost/rules_format_sort_imports.go +1 -1
- package/linthost/rules_format_trailing_comma.go +1 -1
- package/linthost/rules_functional.go +967 -0
- package/linthost/rules_gap.go +24 -45
- package/linthost/rules_grouped_accessor_pairs.go +87 -0
- package/linthost/rules_guard_for_in.go +198 -0
- package/linthost/rules_id_length.go +77 -0
- package/linthost/rules_imports.go +70 -2
- package/linthost/rules_init_declarations.go +53 -0
- package/linthost/rules_jest.go +618 -0
- package/linthost/rules_jsdoc.go +502 -0
- package/linthost/rules_jsx_a11y.go +1264 -0
- package/linthost/rules_logic.go +12 -12
- package/linthost/rules_loops.go +1 -1
- package/linthost/rules_max_classes_per_file.go +58 -0
- package/linthost/rules_max_depth.go +83 -0
- package/linthost/rules_max_lines.go +101 -0
- package/linthost/rules_max_lines_per_function.go +57 -0
- package/linthost/rules_max_nested_callbacks.go +61 -0
- package/linthost/rules_max_params.go +49 -0
- package/linthost/rules_max_statements.go +59 -0
- package/linthost/rules_misc.go +1 -1
- package/linthost/rules_nextjs.go +697 -0
- package/linthost/rules_no_else_return.go +75 -0
- package/linthost/rules_no_empty_named_blocks.go +91 -0
- package/linthost/rules_no_extend_native.go +51 -0
- package/linthost/rules_no_import_assign.go +140 -0
- package/linthost/rules_no_invalid_this.go +52 -0
- package/linthost/rules_no_loop_func.go +85 -0
- package/linthost/rules_no_magic_numbers.go +123 -0
- package/linthost/rules_no_mixed_operators.go +80 -0
- package/linthost/rules_no_param_reassign.go +98 -0
- package/linthost/rules_no_redeclare.go +270 -0
- package/linthost/rules_no_restricted_imports.go +105 -0
- package/linthost/rules_no_restricted_syntax.go +49 -0
- package/linthost/rules_no_shadow.go +147 -0
- package/linthost/rules_no_unreachable.go +77 -0
- package/linthost/rules_no_useless_assignment.go +120 -0
- package/linthost/rules_no_useless_constructor.go +245 -0
- package/linthost/rules_no_useless_return.go +51 -0
- package/linthost/rules_params.go +1 -1
- package/linthost/rules_playwright.go +788 -0
- package/linthost/rules_prefer_arrow_callback.go +124 -0
- package/linthost/rules_prefer_destructuring.go +98 -0
- package/linthost/rules_prefer_named_capture_group.go +129 -0
- package/linthost/rules_prefer_rest_params.go +94 -0
- package/linthost/rules_problems.go +41 -30
- package/linthost/rules_promise.go +1128 -73
- package/linthost/rules_protos.go +2 -2
- package/linthost/rules_react.go +773 -0
- package/linthost/rules_react_extras.go +227 -0
- package/linthost/rules_react_hooks.go +848 -0
- package/linthost/rules_react_perf.go +293 -0
- package/linthost/rules_react_refresh.go +583 -0
- package/linthost/rules_regexp.go +410 -0
- package/linthost/rules_security.go +830 -0
- package/linthost/rules_self.go +2 -2
- package/linthost/rules_solid.go +1073 -0
- package/linthost/rules_sort_imports.go +69 -0
- package/linthost/rules_sort_keys.go +134 -0
- package/linthost/rules_storybook.go +1176 -0
- package/linthost/rules_strings.go +4 -4
- package/linthost/rules_suggestions.go +38 -38
- package/linthost/rules_tanstack_query.go +844 -0
- package/linthost/rules_testing_library.go +1204 -0
- package/linthost/rules_throw.go +1 -1
- package/linthost/rules_ts.go +19 -19
- package/linthost/rules_ts_async.go +399 -0
- package/linthost/rules_ts_class_literal_property_style.go +115 -0
- package/linthost/rules_ts_consistent_type_exports.go +256 -0
- package/linthost/rules_ts_explicit_function_return_type.go +67 -0
- package/linthost/rules_ts_extra.go +155 -36
- package/linthost/rules_ts_no_base_to_string.go +218 -0
- package/linthost/rules_ts_no_confusing_void.go +72 -0
- package/linthost/rules_ts_no_deprecated.go +195 -0
- package/linthost/rules_ts_no_for_in_array.go +84 -0
- package/linthost/rules_ts_no_magic_numbers.go +124 -0
- package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
- package/linthost/rules_ts_no_misused_spread.go +104 -0
- package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
- package/linthost/rules_ts_no_restricted_types.go +62 -0
- package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
- package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
- package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
- package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
- package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
- package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
- package/linthost/rules_ts_no_unsafe_argument.go +82 -0
- package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
- package/linthost/rules_ts_no_unsafe_call.go +82 -0
- package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
- package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
- package/linthost/rules_ts_no_unsafe_return.go +126 -0
- package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
- package/linthost/rules_ts_no_useless_constructor.go +52 -0
- package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
- package/linthost/rules_ts_parameter_properties.go +38 -0
- package/linthost/rules_ts_prefer_find.go +178 -0
- package/linthost/rules_ts_prefer_includes.go +255 -0
- package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
- package/linthost/rules_ts_prefer_optional_chain.go +228 -0
- package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
- package/linthost/rules_ts_prefer_readonly.go +73 -0
- package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
- package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
- package/linthost/rules_ts_prefer_return_this_type.go +135 -0
- package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
- package/linthost/rules_ts_promise_function_async.go +77 -0
- package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
- package/linthost/rules_ts_require_array_sort_compare.go +96 -0
- package/linthost/rules_ts_restrict_plus_operands.go +127 -0
- package/linthost/rules_ts_restrict_template_expressions.go +95 -0
- package/linthost/rules_ts_simple.go +387 -0
- package/linthost/rules_ts_sort_type_constituents.go +144 -0
- package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
- package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
- package/linthost/rules_ts_unbound_method.go +126 -0
- package/linthost/rules_tsdoc.go +136 -0
- package/linthost/rules_unicorn_better_regex.go +27 -0
- package/linthost/rules_unicorn_catch_error_name.go +46 -0
- package/linthost/rules_unicorn_consistent_assert.go +47 -0
- package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
- package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
- package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
- package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
- package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
- package/linthost/rules_unicorn_custom_error_definition.go +138 -0
- package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
- package/linthost/rules_unicorn_error_message.go +64 -0
- package/linthost/rules_unicorn_escape_case.go +51 -0
- package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
- package/linthost/rules_unicorn_explicit_length_check.go +88 -0
- package/linthost/rules_unicorn_filename_case.go +29 -0
- package/linthost/rules_unicorn_import_style.go +28 -0
- package/linthost/rules_unicorn_isolated_functions.go +29 -0
- package/linthost/rules_unicorn_new_for_builtins.go +87 -0
- package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
- package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
- package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
- package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
- package/linthost/rules_unicorn_no_array_for_each.go +40 -0
- package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
- package/linthost/rules_unicorn_no_array_reduce.go +41 -0
- package/linthost/rules_unicorn_no_array_reverse.go +44 -0
- package/linthost/rules_unicorn_no_array_sort.go +43 -0
- package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
- package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
- package/linthost/rules_unicorn_no_console_spaces.go +61 -0
- package/linthost/rules_unicorn_no_document_cookie.go +41 -0
- package/linthost/rules_unicorn_no_empty_file.go +44 -0
- package/linthost/rules_unicorn_no_for_loop.go +102 -0
- package/linthost/rules_unicorn_no_hex_escape.go +42 -0
- package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
- package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
- package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
- package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
- package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
- package/linthost/rules_unicorn_no_lonely_if.go +44 -0
- package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
- package/linthost/rules_unicorn_no_named_default.go +54 -0
- package/linthost/rules_unicorn_no_negated_condition.go +85 -0
- package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
- package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
- package/linthost/rules_unicorn_no_new_array.go +30 -0
- package/linthost/rules_unicorn_no_new_buffer.go +31 -0
- package/linthost/rules_unicorn_no_null.go +24 -0
- package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
- package/linthost/rules_unicorn_no_process_exit.go +43 -0
- package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
- package/linthost/rules_unicorn_no_static_only_class.go +56 -0
- package/linthost/rules_unicorn_no_thenable.go +74 -0
- package/linthost/rules_unicorn_no_this_assignment.go +35 -0
- package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
- package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
- package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
- package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
- package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
- package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
- package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
- package/linthost/rules_unicorn_no_unused_properties.go +30 -0
- package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
- package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
- package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
- package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
- package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
- package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
- package/linthost/rules_unicorn_no_useless_spread.go +67 -0
- package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
- package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
- package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
- package/linthost/rules_unicorn_number_literal_case.go +69 -0
- package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
- package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
- package/linthost/rules_unicorn_prefer_array_find.go +51 -0
- package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
- package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
- package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
- package/linthost/rules_unicorn_prefer_array_some.go +67 -0
- package/linthost/rules_unicorn_prefer_at.go +77 -0
- package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
- package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
- package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
- package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
- package/linthost/rules_unicorn_prefer_code_point.go +49 -0
- package/linthost/rules_unicorn_prefer_date_now.go +78 -0
- package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
- package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
- package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
- package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
- package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
- package/linthost/rules_unicorn_prefer_event_target.go +36 -0
- package/linthost/rules_unicorn_prefer_export_from.go +129 -0
- package/linthost/rules_unicorn_prefer_global_this.go +127 -0
- package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
- package/linthost/rules_unicorn_prefer_includes.go +149 -0
- package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
- package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
- package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
- package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
- package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
- package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
- package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
- package/linthost/rules_unicorn_prefer_module.go +50 -0
- package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
- package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
- package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
- package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
- package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
- package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
- package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
- package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
- package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
- package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
- package/linthost/rules_unicorn_prefer_set_has.go +42 -0
- package/linthost/rules_unicorn_prefer_set_size.go +48 -0
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
- package/linthost/rules_unicorn_prefer_single_call.go +96 -0
- package/linthost/rules_unicorn_prefer_spread.go +51 -0
- package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
- package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
- package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
- package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
- package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
- package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
- package/linthost/rules_unicorn_prefer_switch.go +103 -0
- package/linthost/rules_unicorn_prefer_ternary.go +71 -0
- package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
- package/linthost/rules_unicorn_prefer_type_error.go +127 -0
- package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
- package/linthost/rules_unicorn_relative_url_style.go +49 -0
- package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
- package/linthost/rules_unicorn_require_module_attributes.go +54 -0
- package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
- package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
- package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
- package/linthost/rules_unicorn_string_content.go +27 -0
- package/linthost/rules_unicorn_switch_case_braces.go +42 -0
- package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
- package/linthost/rules_unicorn_template_indent.go +28 -0
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
- package/linthost/rules_unicorn_throw_new_error.go +61 -0
- package/linthost/rules_var.go +4 -4
- package/linthost/rules_vitest.go +568 -0
- package/package.json +3 -3
- package/plugin/main.go +49 -3
- package/rule/astutil/astutil.go +10 -3
- package/rule/rule.go +12 -10
- package/src/defaultFormat.ts +11 -8
- package/src/index.ts +40 -16
- package/src/structures/ITtscLintConfig.ts +12 -6
- package/src/structures/{ITtscLintFormatConfig.ts → ITtscLintFormat.ts} +2 -2
- package/src/structures/TtscLintRuleSetting.ts +55 -0
- package/src/structures/index.ts +3 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +128 -0
- package/src/structures/rules/ITtscLintBoundariesRules.ts +95 -0
- package/src/structures/rules/ITtscLintContributorRules.ts +21 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +1406 -0
- package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
- package/src/structures/rules/ITtscLintCypressRules.ts +145 -0
- package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +213 -0
- package/src/structures/rules/ITtscLintFunctionalRules.ts +202 -0
- package/src/structures/rules/ITtscLintJestRules.ts +220 -0
- package/src/structures/rules/ITtscLintJsdocRules.ts +153 -0
- package/src/structures/rules/ITtscLintJsxA11yRules.ts +339 -0
- package/src/structures/rules/ITtscLintNextjsRules.ts +206 -0
- package/src/structures/rules/ITtscLintPlaywrightRules.ts +361 -0
- package/src/structures/rules/ITtscLintPromiseRules.ts +205 -0
- package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
- package/src/structures/rules/ITtscLintReactPerfRules.ts +55 -0
- package/src/structures/rules/ITtscLintReactRuleOptions.ts +43 -0
- package/src/structures/rules/ITtscLintReactRules.ts +321 -0
- package/src/structures/rules/ITtscLintRegexpRules.ts +212 -0
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +89 -0
- package/src/structures/rules/ITtscLintRules.ts +77 -0
- package/src/structures/rules/ITtscLintSecurityRules.ts +162 -0
- package/src/structures/rules/ITtscLintSolidRules.ts +202 -0
- package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +23 -0
- package/src/structures/rules/ITtscLintStorybookRules.ts +193 -0
- package/src/structures/rules/ITtscLintTanstackQueryRules.ts +102 -0
- package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +24 -0
- package/src/structures/rules/ITtscLintTestingLibraryRules.ts +342 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +1230 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
- package/src/structures/rules/ITtscLintVitestRules.ts +154 -0
- package/src/structures/rules/index.ts +31 -0
- package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
- package/lib/structures/TtscLintRule.d.ts +0 -17
- package/lib/structures/TtscLintRule.js.map +0 -1
- package/lib/structures/TtscLintRuleMap.d.ts +0 -46
- package/lib/structures/TtscLintRuleMap.js.map +0 -1
- package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
- package/lib/structures/TtscLintRuleOptions.js.map +0 -1
- package/src/structures/TtscLintRule.ts +0 -160
- package/src/structures/TtscLintRuleMap.ts +0 -55
- package/src/structures/TtscLintRuleOptions.ts +0 -151
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// typescript/no-useless-constructor: TS-aware extension of the core
|
|
2
|
+
// `no-useless-constructor` rule. A class constructor with an empty body
|
|
3
|
+
// and no parameter properties is noise — the engine generates the same
|
|
4
|
+
// default constructor anyway.
|
|
5
|
+
//
|
|
6
|
+
// The TS variant differs from the core rule in one detail: a
|
|
7
|
+
// parameter-property declaration (`constructor(public foo: number) {}`)
|
|
8
|
+
// carries a semantic side effect — it implicitly declares the class
|
|
9
|
+
// field and assigns the argument to it. Removing the constructor would
|
|
10
|
+
// remove the field. So an otherwise-empty body is NOT useless when at
|
|
11
|
+
// least one parameter is a parameter property.
|
|
12
|
+
// typescript-eslint strict:
|
|
13
|
+
// https://typescript-eslint.io/rules/no-useless-constructor/
|
|
14
|
+
//
|
|
15
|
+
// AST-only. The trigger is a `KindConstructor` whose body is an empty
|
|
16
|
+
// block and whose parameter list contains no `public` / `private` /
|
|
17
|
+
// `protected` / `readonly` / `override` parameter property.
|
|
18
|
+
package linthost
|
|
19
|
+
|
|
20
|
+
import (
|
|
21
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
type tsNoUselessConstructor struct{}
|
|
25
|
+
|
|
26
|
+
func (tsNoUselessConstructor) Name() string { return "typescript/no-useless-constructor" }
|
|
27
|
+
func (tsNoUselessConstructor) Visits() []shimast.Kind {
|
|
28
|
+
return []shimast.Kind{shimast.KindConstructor}
|
|
29
|
+
}
|
|
30
|
+
func (tsNoUselessConstructor) Check(ctx *Context, node *shimast.Node) {
|
|
31
|
+
ctor := node.AsConstructorDeclaration()
|
|
32
|
+
if ctor == nil || ctor.Body == nil {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
body := ctor.Body.AsBlock()
|
|
36
|
+
if body == nil || body.Statements == nil || len(body.Statements.Nodes) != 0 {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
// Any parameter property keeps the constructor meaningful: the
|
|
40
|
+
// shorthand IS the field declaration. Removing the constructor
|
|
41
|
+
// would silently delete the field, so this is not "useless".
|
|
42
|
+
for _, param := range node.Parameters() {
|
|
43
|
+
if isParameterProperty(param) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
ctx.Report(node, "Useless constructor with no parameter properties.")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func init() {
|
|
51
|
+
Register(tsNoUselessConstructor{})
|
|
52
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// typescript/non-nullable-type-assertion-style: when `x as T` strips
|
|
2
|
+
// exactly the `null`/`undefined` constituents from the static type of
|
|
3
|
+
// `x`, the assertion is equivalent to the non-null assertion `x!`. The
|
|
4
|
+
// `!` form is shorter and communicates "I am asserting non-null"
|
|
5
|
+
// explicitly, while `as T` may be read as "I am narrowing to a
|
|
6
|
+
// different type". typescript-eslint stylistic-type-checked:
|
|
7
|
+
// https://typescript-eslint.io/rules/non-nullable-type-assertion-style/
|
|
8
|
+
package linthost
|
|
9
|
+
|
|
10
|
+
import (
|
|
11
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
12
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// nonNullableTypeAssertionStyle reports `expr as T` where `T` equals the
|
|
16
|
+
// non-nullable version of `expr`'s static type. Implementation notes:
|
|
17
|
+
//
|
|
18
|
+
// - Type-aware: requires the Checker to compute the non-nullable form
|
|
19
|
+
// of the source expression and to compare it against the asserted
|
|
20
|
+
// type. The shim re-exports the unexported `GetNonNullableType` and
|
|
21
|
+
// `GetTypeFromTypeNode` methods directly.
|
|
22
|
+
// - Symmetric assignability is used instead of literal type identity
|
|
23
|
+
// because the Checker may widen literal types differently between
|
|
24
|
+
// the two sides (e.g. the source `string | undefined` becomes
|
|
25
|
+
// `string` after non-null stripping, and the asserted node `string`
|
|
26
|
+
// resolves to the same primitive — both directions of assignability
|
|
27
|
+
// hold and identity is unnecessary).
|
|
28
|
+
// - The rule only fires when the source type was actually nullable;
|
|
29
|
+
// `42 as number` is left alone because removing nothing from
|
|
30
|
+
// `number` still yields `number` and the assertion is not
|
|
31
|
+
// equivalent to `42!` (which would be a syntax error on a literal).
|
|
32
|
+
type nonNullableTypeAssertionStyle struct{}
|
|
33
|
+
|
|
34
|
+
func (nonNullableTypeAssertionStyle) Name() string {
|
|
35
|
+
return "typescript/non-nullable-type-assertion-style"
|
|
36
|
+
}
|
|
37
|
+
func (nonNullableTypeAssertionStyle) NeedsTypeChecker() bool {
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
func (nonNullableTypeAssertionStyle) Visits() []shimast.Kind {
|
|
41
|
+
return []shimast.Kind{shimast.KindAsExpression}
|
|
42
|
+
}
|
|
43
|
+
func (nonNullableTypeAssertionStyle) Check(ctx *Context, node *shimast.Node) {
|
|
44
|
+
if ctx.Checker == nil {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
as := node.AsAsExpression()
|
|
48
|
+
if as == nil || as.Expression == nil || as.Type == nil {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
sourceType := ctx.Checker.GetTypeAtLocation(as.Expression)
|
|
52
|
+
if sourceType == nil {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
// Only consider expressions whose static type actually carries
|
|
56
|
+
// `null` or `undefined`; otherwise `x!` is not the same as `x as T`.
|
|
57
|
+
if !nonNullableTypeAssertionStyleIsNullable(sourceType) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
nonNullable := ctx.Checker.GetNonNullableType(sourceType)
|
|
61
|
+
if nonNullable == nil {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
assertedType := ctx.Checker.GetTypeFromTypeNode(as.Type)
|
|
65
|
+
if assertedType == nil {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
// Bidirectional assignability stands in for type identity: the
|
|
69
|
+
// stripped source type must coincide with the asserted type up to
|
|
70
|
+
// structural equivalence. Both sides have to hold so that
|
|
71
|
+
// `x as SomeSupertype` or `x as SomeSubtype` does not get rewritten
|
|
72
|
+
// to `x!` and silently lose information.
|
|
73
|
+
if !ctx.Checker.IsTypeAssignableTo(nonNullable, assertedType) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if !ctx.Checker.IsTypeAssignableTo(assertedType, nonNullable) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
ctx.Report(node, "Use a `!` assertion to more succinctly remove `null` and `undefined` from the type.")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// nonNullableTypeAssertionStyleIsNullable reports whether t carries
|
|
83
|
+
// `null` or `undefined` either at the top level (for direct
|
|
84
|
+
// `T | null` / `T | undefined` shapes) or inside a union constituent.
|
|
85
|
+
func nonNullableTypeAssertionStyleIsNullable(t *shimchecker.Type) bool {
|
|
86
|
+
if t == nil {
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
flags := t.Flags()
|
|
90
|
+
if flags&(shimchecker.TypeFlagsNull|shimchecker.TypeFlagsUndefined) != 0 {
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
if flags&shimchecker.TypeFlagsUnion != 0 {
|
|
94
|
+
for _, part := range t.Types() {
|
|
95
|
+
if part == nil {
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
if nonNullableTypeAssertionStyleIsNullable(part) {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func init() {
|
|
107
|
+
Register(nonNullableTypeAssertionStyle{})
|
|
108
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// typescript/parameter-properties reports TypeScript parameter
|
|
2
|
+
// properties — constructor parameters prefixed with `public`,
|
|
3
|
+
// `private`, `protected`, `readonly`, or `override` that implicitly
|
|
4
|
+
// declare a class field. The rule's default policy
|
|
5
|
+
// (`prefer: "class-property"`) flags the shorthand so the class
|
|
6
|
+
// member list stays the single source of truth for the class shape;
|
|
7
|
+
// fields buried in the constructor signature are easy to miss.
|
|
8
|
+
// https://typescript-eslint.io/rules/parameter-properties/
|
|
9
|
+
//
|
|
10
|
+
// AST-only — the trigger is a syntactic modifier on a `KindParameter`
|
|
11
|
+
// whose parent is a `KindConstructor`. The shared `isParameterProperty`
|
|
12
|
+
// helper (also used by `no-unnecessary-parameter-property-assignment`)
|
|
13
|
+
// is the single decoding point for the modifier set.
|
|
14
|
+
package linthost
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
type parameterProperties struct{}
|
|
21
|
+
|
|
22
|
+
func (parameterProperties) Name() string { return "typescript/parameter-properties" }
|
|
23
|
+
func (parameterProperties) Visits() []shimast.Kind {
|
|
24
|
+
return []shimast.Kind{shimast.KindParameter}
|
|
25
|
+
}
|
|
26
|
+
func (parameterProperties) Check(ctx *Context, node *shimast.Node) {
|
|
27
|
+
if node == nil || node.Parent == nil || node.Parent.Kind != shimast.KindConstructor {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
if !isParameterProperty(node) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
ctx.Report(node, "Prefer a plain class field declaration over a constructor parameter property.")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func init() {
|
|
37
|
+
Register(parameterProperties{})
|
|
38
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// typescript/prefer-find: prefer `arr.find(predicate)` over the
|
|
2
|
+
// `arr.filter(predicate)[0]` and `arr.filter(predicate).at(0)` shapes.
|
|
3
|
+
// `find` short-circuits on the first match instead of materializing the
|
|
4
|
+
// whole filtered array, so for sufficiently large inputs it is strictly
|
|
5
|
+
// faster — and at every input size it expresses the intent ("get me
|
|
6
|
+
// the first match") more directly than reading the head of a filtered
|
|
7
|
+
// list. typescript-eslint:
|
|
8
|
+
// https://typescript-eslint.io/rules/prefer-find/
|
|
9
|
+
//
|
|
10
|
+
// Type-aware. Without a Checker the rule cannot prove the receiver of
|
|
11
|
+
// `filter` is array-like, so Context.Checker == nil short-circuits each
|
|
12
|
+
// Check to a no-op the way `no-for-in-array` and `prefer-includes` do.
|
|
13
|
+
// The receiver type must be a provable array or tuple — generic, `any`,
|
|
14
|
+
// `unknown`, and `never` pass through so generic helpers don't explode
|
|
15
|
+
// with false positives on user-defined `filter` methods.
|
|
16
|
+
package linthost
|
|
17
|
+
|
|
18
|
+
import (
|
|
19
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
20
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
type preferFind struct{}
|
|
24
|
+
|
|
25
|
+
func (preferFind) Name() string { return "typescript/prefer-find" }
|
|
26
|
+
func (preferFind) NeedsTypeChecker() bool {
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
func (preferFind) Visits() []shimast.Kind {
|
|
30
|
+
return []shimast.Kind{
|
|
31
|
+
shimast.KindElementAccessExpression,
|
|
32
|
+
shimast.KindCallExpression,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
func (preferFind) Check(ctx *Context, node *shimast.Node) {
|
|
36
|
+
if ctx.Checker == nil {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
switch node.Kind {
|
|
40
|
+
case shimast.KindElementAccessExpression:
|
|
41
|
+
access := node.AsElementAccessExpression()
|
|
42
|
+
if access == nil || access.Expression == nil || access.ArgumentExpression == nil {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
// Index must be the literal `0`.
|
|
46
|
+
if !preferFindIsZeroLiteral(access.ArgumentExpression) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
if !preferFindIsArrayFilterCall(ctx, access.Expression) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
ctx.Report(node, preferFindMessage)
|
|
53
|
+
case shimast.KindCallExpression:
|
|
54
|
+
call := node.AsCallExpression()
|
|
55
|
+
if call == nil || call.Expression == nil {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
// Match `<filter-call>.at(0)`.
|
|
59
|
+
receiver, method, ok := promisePropertyAccessParts(call.Expression)
|
|
60
|
+
if !ok || method != "at" {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if call.Arguments == nil || len(call.Arguments.Nodes) != 1 {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
if !preferFindIsZeroLiteral(call.Arguments.Nodes[0]) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if !preferFindIsArrayFilterCall(ctx, receiver) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
ctx.Report(node, preferFindMessage)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const preferFindMessage = "Prefer `.find(predicate)` over `.filter(predicate)[0]` / `.filter(predicate).at(0)` — `find` short-circuits on the first match and avoids materializing the full filtered array."
|
|
77
|
+
|
|
78
|
+
// preferFindIsZeroLiteral reports whether `node` (after stripping
|
|
79
|
+
// parens) is the numeric literal `0` — the only index `find` can
|
|
80
|
+
// replace. `+0` and `0` are both accepted; `-0` is rejected because it
|
|
81
|
+
// is technically a separate value at the language level even though
|
|
82
|
+
// `arr[-0]` resolves the same. Keeps the rule's noise floor narrow.
|
|
83
|
+
func preferFindIsZeroLiteral(node *shimast.Node) bool {
|
|
84
|
+
node = stripParens(node)
|
|
85
|
+
if node == nil {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
if node.Kind == shimast.KindNumericLiteral {
|
|
89
|
+
return numericLiteralText(node) == "0"
|
|
90
|
+
}
|
|
91
|
+
if node.Kind == shimast.KindPrefixUnaryExpression {
|
|
92
|
+
prefix := node.AsPrefixUnaryExpression()
|
|
93
|
+
if prefix == nil || prefix.Operand == nil {
|
|
94
|
+
return false
|
|
95
|
+
}
|
|
96
|
+
if prefix.Operator != shimast.KindPlusToken {
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
return prefix.Operand.Kind == shimast.KindNumericLiteral &&
|
|
100
|
+
numericLiteralText(prefix.Operand) == "0"
|
|
101
|
+
}
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// preferFindIsArrayFilterCall reports whether `node` (after stripping
|
|
106
|
+
// parens) is a `<receiver>.filter(predicate)` CallExpression whose
|
|
107
|
+
// receiver type is provably an array or tuple. Mirrors the shape used
|
|
108
|
+
// by `prefer-includes` for `.indexOf(x)`: a user type with a custom
|
|
109
|
+
// `filter` method should NOT trigger the rule.
|
|
110
|
+
func preferFindIsArrayFilterCall(ctx *Context, node *shimast.Node) bool {
|
|
111
|
+
node = stripParens(node)
|
|
112
|
+
if node == nil || node.Kind != shimast.KindCallExpression {
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
call := node.AsCallExpression()
|
|
116
|
+
if call == nil || call.Expression == nil {
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
receiver, method, ok := promisePropertyAccessParts(call.Expression)
|
|
120
|
+
if !ok || method != "filter" {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
if receiver == nil {
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
// `.filter` accepts an optional `thisArg`, so require at least one
|
|
127
|
+
// argument (the predicate) — a zero-arg call would not parse on the
|
|
128
|
+
// real lib.es5 declaration and is conservatively skipped.
|
|
129
|
+
if call.Arguments == nil || len(call.Arguments.Nodes) == 0 {
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
t := ctx.Checker.GetTypeAtLocation(receiver)
|
|
133
|
+
if t == nil {
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
return preferFindIsArrayLike(ctx.Checker, t)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// preferFindIsArrayLike reports whether t is provably an array or
|
|
140
|
+
// tuple. The constituent-recursion shape mirrors
|
|
141
|
+
// `preferIncludesIsArrayOrString`: a union like `T[] | T[]` is still
|
|
142
|
+
// accepted because every constituent is rewritable. `any` / `unknown` /
|
|
143
|
+
// `never` are intentionally NOT treated as matching — they leak from
|
|
144
|
+
// generic helpers and would explode the false-positive volume on
|
|
145
|
+
// user-defined `filter` methods. Strings are NOT included here even
|
|
146
|
+
// though they expose a `.filter`-shaped method via lib polyfills,
|
|
147
|
+
// because the upstream rule only fires on the genuine `Array#filter`.
|
|
148
|
+
func preferFindIsArrayLike(checker *shimchecker.Checker, t *shimchecker.Type) bool {
|
|
149
|
+
if checker == nil || t == nil {
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
152
|
+
flags := t.Flags()
|
|
153
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
|
|
157
|
+
for _, part := range t.Types() {
|
|
158
|
+
if part == nil {
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
if !preferFindIsArrayLike(checker, part) {
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return true
|
|
166
|
+
}
|
|
167
|
+
if shimchecker.Checker_isArrayType(checker, t) {
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
if shimchecker.IsTupleType(t) {
|
|
171
|
+
return true
|
|
172
|
+
}
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
func init() {
|
|
177
|
+
Register(preferFind{})
|
|
178
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// typescript/prefer-includes: prefer `array.includes(x)` over the
|
|
2
|
+
// `array.indexOf(x) !== -1` (and the related `=== -1`, `>= 0`, `< 0`,
|
|
3
|
+
// `> -1`) idioms. The `includes` form states the intent directly —
|
|
4
|
+
// "does the collection contain this value?" — and avoids the
|
|
5
|
+
// sentinel-vs-position confusion that comes with comparing an
|
|
6
|
+
// `indexOf` return value against `-1`. typescript-eslint:
|
|
7
|
+
// https://typescript-eslint.io/rules/prefer-includes/
|
|
8
|
+
//
|
|
9
|
+
// Type-aware. Without a Checker the rule cannot prove the receiver of
|
|
10
|
+
// `indexOf` is array-like or string-like, so Context.Checker == nil
|
|
11
|
+
// short-circuits each Check to a no-op the way `no-for-in-array` and
|
|
12
|
+
// `require-array-sort-compare` do. The receiver type must be a
|
|
13
|
+
// provable array, tuple, or string — generic, `any`, `unknown`, and
|
|
14
|
+
// `never` pass through so generic helpers don't explode with false
|
|
15
|
+
// positives.
|
|
16
|
+
package linthost
|
|
17
|
+
|
|
18
|
+
import (
|
|
19
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
20
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
type preferIncludes struct{}
|
|
24
|
+
|
|
25
|
+
func (preferIncludes) Name() string { return "typescript/prefer-includes" }
|
|
26
|
+
func (preferIncludes) NeedsTypeChecker() bool {
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
func (preferIncludes) Visits() []shimast.Kind {
|
|
30
|
+
return []shimast.Kind{shimast.KindBinaryExpression}
|
|
31
|
+
}
|
|
32
|
+
func (preferIncludes) Check(ctx *Context, node *shimast.Node) {
|
|
33
|
+
if ctx.Checker == nil {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
bin := node.AsBinaryExpression()
|
|
37
|
+
if bin == nil || bin.OperatorToken == nil {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
// One side of the comparison must be a `.indexOf(...)` call; the
|
|
41
|
+
// other side must be a numeric sentinel (-1 or 0) and the operator
|
|
42
|
+
// chosen so the boolean meaning is "is the value present".
|
|
43
|
+
if !preferIncludesIsTargetedComparison(bin.OperatorToken.Kind) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
if preferIncludesCheckSide(ctx, bin.Left, bin.Right, bin.OperatorToken.Kind, false) {
|
|
47
|
+
ctx.Report(node, preferIncludesMessage)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
if preferIncludesCheckSide(ctx, bin.Right, bin.Left, bin.OperatorToken.Kind, true) {
|
|
51
|
+
ctx.Report(node, preferIncludesMessage)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const preferIncludesMessage = "Prefer `.includes(x)` over `.indexOf(x)` compared against `-1` / `0` — `.includes` states the containment intent directly."
|
|
56
|
+
|
|
57
|
+
// preferIncludesIsTargetedComparison reports whether the operator is
|
|
58
|
+
// one of the equality / ordering tokens the rule cares about. The
|
|
59
|
+
// rule fires on `===`, `!==`, `==`, `!=`, `<`, `<=`, `>`, `>=`; other
|
|
60
|
+
// operators leave the binary alone.
|
|
61
|
+
func preferIncludesIsTargetedComparison(kind shimast.Kind) bool {
|
|
62
|
+
switch kind {
|
|
63
|
+
case shimast.KindEqualsEqualsToken,
|
|
64
|
+
shimast.KindEqualsEqualsEqualsToken,
|
|
65
|
+
shimast.KindExclamationEqualsToken,
|
|
66
|
+
shimast.KindExclamationEqualsEqualsToken,
|
|
67
|
+
shimast.KindLessThanToken,
|
|
68
|
+
shimast.KindLessThanEqualsToken,
|
|
69
|
+
shimast.KindGreaterThanToken,
|
|
70
|
+
shimast.KindGreaterThanEqualsToken:
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// preferIncludesCheckSide reports whether `call` is the
|
|
77
|
+
// `<receiver>.indexOf(arg)` call and `sentinel` is the numeric
|
|
78
|
+
// constant the comparison is paired with. `reversed` flips the
|
|
79
|
+
// direction of ordering operators so `arr.indexOf(x) > -1` and
|
|
80
|
+
// `-1 < arr.indexOf(x)` both match.
|
|
81
|
+
func preferIncludesCheckSide(
|
|
82
|
+
ctx *Context,
|
|
83
|
+
call, sentinel *shimast.Node,
|
|
84
|
+
op shimast.Kind,
|
|
85
|
+
reversed bool,
|
|
86
|
+
) bool {
|
|
87
|
+
call = stripParens(call)
|
|
88
|
+
sentinel = stripParens(sentinel)
|
|
89
|
+
if call == nil || sentinel == nil {
|
|
90
|
+
return false
|
|
91
|
+
}
|
|
92
|
+
if call.Kind != shimast.KindCallExpression {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
callExpr := call.AsCallExpression()
|
|
96
|
+
if callExpr == nil || callExpr.Expression == nil {
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
receiver, method, ok := promisePropertyAccessParts(callExpr.Expression)
|
|
100
|
+
if !ok || method != "indexOf" {
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
if callExpr.Arguments == nil || len(callExpr.Arguments.Nodes) != 1 {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
value, ok := preferIncludesSentinelValue(sentinel)
|
|
107
|
+
if !ok {
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
effectiveOp := op
|
|
111
|
+
if reversed {
|
|
112
|
+
effectiveOp = preferIncludesFlipOperator(op)
|
|
113
|
+
}
|
|
114
|
+
if !preferIncludesIsPresenceComparison(effectiveOp, value) {
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
if receiver == nil {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
t := ctx.Checker.GetTypeAtLocation(receiver)
|
|
121
|
+
if t == nil {
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
124
|
+
return preferIncludesIsArrayOrString(ctx.Checker, t)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// preferIncludesSentinelValue returns the numeric sentinel encoded by
|
|
128
|
+
// `node`, recognizing the literals `-1`, `0`, and `1` (the only
|
|
129
|
+
// constants the rule's shapes reduce to). Returns ok=false for any
|
|
130
|
+
// other expression.
|
|
131
|
+
func preferIncludesSentinelValue(node *shimast.Node) (int, bool) {
|
|
132
|
+
if node == nil {
|
|
133
|
+
return 0, false
|
|
134
|
+
}
|
|
135
|
+
if node.Kind == shimast.KindNumericLiteral {
|
|
136
|
+
switch numericLiteralText(node) {
|
|
137
|
+
case "0":
|
|
138
|
+
return 0, true
|
|
139
|
+
case "1":
|
|
140
|
+
return 1, true
|
|
141
|
+
}
|
|
142
|
+
return 0, false
|
|
143
|
+
}
|
|
144
|
+
if node.Kind != shimast.KindPrefixUnaryExpression {
|
|
145
|
+
return 0, false
|
|
146
|
+
}
|
|
147
|
+
prefix := node.AsPrefixUnaryExpression()
|
|
148
|
+
if prefix == nil || prefix.Operand == nil {
|
|
149
|
+
return 0, false
|
|
150
|
+
}
|
|
151
|
+
if prefix.Operand.Kind != shimast.KindNumericLiteral {
|
|
152
|
+
return 0, false
|
|
153
|
+
}
|
|
154
|
+
text := numericLiteralText(prefix.Operand)
|
|
155
|
+
switch prefix.Operator {
|
|
156
|
+
case shimast.KindMinusToken:
|
|
157
|
+
if text == "1" {
|
|
158
|
+
return -1, true
|
|
159
|
+
}
|
|
160
|
+
if text == "0" {
|
|
161
|
+
return 0, true
|
|
162
|
+
}
|
|
163
|
+
case shimast.KindPlusToken:
|
|
164
|
+
if text == "0" {
|
|
165
|
+
return 0, true
|
|
166
|
+
}
|
|
167
|
+
if text == "1" {
|
|
168
|
+
return 1, true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return 0, false
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// preferIncludesFlipOperator returns the operator that has the same
|
|
175
|
+
// boolean meaning when the operands are swapped. `a < b` flipped is
|
|
176
|
+
// `b > a`, `a >= b` flipped is `b <= a`, and equality operators are
|
|
177
|
+
// symmetric so they are returned unchanged.
|
|
178
|
+
func preferIncludesFlipOperator(kind shimast.Kind) shimast.Kind {
|
|
179
|
+
switch kind {
|
|
180
|
+
case shimast.KindLessThanToken:
|
|
181
|
+
return shimast.KindGreaterThanToken
|
|
182
|
+
case shimast.KindLessThanEqualsToken:
|
|
183
|
+
return shimast.KindGreaterThanEqualsToken
|
|
184
|
+
case shimast.KindGreaterThanToken:
|
|
185
|
+
return shimast.KindLessThanToken
|
|
186
|
+
case shimast.KindGreaterThanEqualsToken:
|
|
187
|
+
return shimast.KindLessThanEqualsToken
|
|
188
|
+
}
|
|
189
|
+
return kind
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// preferIncludesIsPresenceComparison reports whether `op` paired with
|
|
193
|
+
// `value` is one of the equivalences for "the element is present":
|
|
194
|
+
// `!= -1`, `>= 0`, `> -1` mean "present"; `== -1`, `< 0`, `<= -1`
|
|
195
|
+
// mean "absent". Either polarity is rewritable to `includes(x)` (the
|
|
196
|
+
// "absent" cases negate the call), so both pass.
|
|
197
|
+
func preferIncludesIsPresenceComparison(op shimast.Kind, value int) bool {
|
|
198
|
+
switch op {
|
|
199
|
+
case shimast.KindExclamationEqualsToken, shimast.KindExclamationEqualsEqualsToken:
|
|
200
|
+
return value == -1
|
|
201
|
+
case shimast.KindEqualsEqualsToken, shimast.KindEqualsEqualsEqualsToken:
|
|
202
|
+
return value == -1
|
|
203
|
+
case shimast.KindGreaterThanEqualsToken:
|
|
204
|
+
return value == 0
|
|
205
|
+
case shimast.KindGreaterThanToken:
|
|
206
|
+
return value == -1
|
|
207
|
+
case shimast.KindLessThanToken:
|
|
208
|
+
return value == 0
|
|
209
|
+
case shimast.KindLessThanEqualsToken:
|
|
210
|
+
return value == -1
|
|
211
|
+
}
|
|
212
|
+
return false
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// preferIncludesIsArrayOrString reports whether t is provably an
|
|
216
|
+
// array, tuple, or string. The constituent-recursion shape mirrors
|
|
217
|
+
// `requireArraySortCompareIsArrayLike`: a union like `string[] |
|
|
218
|
+
// string` is still accepted because every constituent is rewritable.
|
|
219
|
+
// `any` / `unknown` / `never` are intentionally NOT treated as
|
|
220
|
+
// matching — they leak from generic helpers and would explode the
|
|
221
|
+
// false-positive volume on user-defined `indexOf` methods.
|
|
222
|
+
func preferIncludesIsArrayOrString(checker *shimchecker.Checker, t *shimchecker.Type) bool {
|
|
223
|
+
if checker == nil || t == nil {
|
|
224
|
+
return false
|
|
225
|
+
}
|
|
226
|
+
flags := t.Flags()
|
|
227
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
|
|
228
|
+
return false
|
|
229
|
+
}
|
|
230
|
+
if flags&shimchecker.TypeFlagsStringLike != 0 {
|
|
231
|
+
return true
|
|
232
|
+
}
|
|
233
|
+
if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
|
|
234
|
+
for _, part := range t.Types() {
|
|
235
|
+
if part == nil {
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
if !preferIncludesIsArrayOrString(checker, part) {
|
|
239
|
+
return false
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return true
|
|
243
|
+
}
|
|
244
|
+
if shimchecker.Checker_isArrayType(checker, t) {
|
|
245
|
+
return true
|
|
246
|
+
}
|
|
247
|
+
if shimchecker.IsTupleType(t) {
|
|
248
|
+
return true
|
|
249
|
+
}
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
func init() {
|
|
254
|
+
Register(preferIncludes{})
|
|
255
|
+
}
|