@ttsc/lint 0.13.0 → 0.14.0-dev.20250530.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +955 -85
- package/lib/defaultFormat.d.ts +15 -8
- package/lib/defaultFormat.js +15 -8
- package/lib/defaultFormat.js.map +1 -1
- package/lib/index.d.ts +46 -0
- package/lib/index.js +60 -37
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +12 -6
- 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/format/ITtscLintFormat.d.ts +128 -0
- package/lib/structures/{TtscLintRuleMap.js → format/ITtscLintFormat.js} +1 -1
- package/lib/structures/format/ITtscLintFormat.js.map +1 -0
- package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +17 -0
- package/lib/structures/{ITtscLintFormatConfig.js → format/ITtscLintFormatJsDoc.js} +1 -1
- package/lib/structures/format/ITtscLintFormatJsDoc.js.map +1 -0
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +54 -0
- package/lib/structures/format/ITtscLintFormatSortImports.js +3 -0
- package/lib/structures/format/ITtscLintFormatSortImports.js.map +1 -0
- package/lib/structures/format/index.d.ts +3 -0
- package/lib/structures/format/index.js +20 -0
- package/lib/structures/format/index.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 +95 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +13 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +75 -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 +16 -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 +1229 -0
- package/lib/structures/rules/ITtscLintCoreRules.js +3 -0
- 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 +125 -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 +154 -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 +162 -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 +190 -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 +133 -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 +294 -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 +180 -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 +312 -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 +179 -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 +49 -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 +37 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js +11 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRules.d.ts +276 -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 +182 -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 +65 -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 +141 -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 +178 -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 +20 -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 +165 -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 +88 -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 +21 -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 +293 -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 +1074 -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 +133 -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/compile.go +33 -2
- package/linthost/config.go +118 -71
- package/linthost/config_format.go +270 -104
- package/linthost/directives.go +67 -25
- package/linthost/dispatch.go +14 -3
- package/linthost/engine.go +160 -40
- package/linthost/fix.go +10 -3
- package/linthost/flags_gen.go +17 -15
- package/linthost/format.go +222 -25
- package/linthost/format_editor_settings.go +262 -0
- package/linthost/host.go +40 -3
- package/linthost/lsp.go +898 -0
- package/linthost/print_dispatch.go +5 -2
- package/linthost/print_engine.go +5 -5
- package/linthost/print_nodes_call.go +149 -67
- package/linthost/print_nodes_function.go +27 -2
- package/linthost/print_nodes_list.go +40 -5
- package/linthost/print_nodes_object.go +1 -1
- package/linthost/print_nodes_ternary.go +86 -0
- 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 +95 -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_arrow_parens.go +167 -0
- package/linthost/rules_format_bracket_spacing.go +117 -0
- package/linthost/rules_format_clause_join.go +172 -0
- package/linthost/rules_format_declaration_header.go +595 -0
- package/linthost/rules_format_indent.go +372 -0
- package/linthost/rules_format_jsdoc.go +5 -5
- package/linthost/rules_format_orphan_semi.go +106 -0
- package/linthost/rules_format_parameter_properties.go +175 -0
- package/linthost/rules_format_print_width.go +66 -55
- package/linthost/rules_format_quote_props.go +169 -0
- package/linthost/rules_format_quotes.go +76 -30
- package/linthost/rules_format_semi.go +186 -11
- package/linthost/rules_format_sort_imports.go +437 -118
- package/linthost/rules_format_statement_split.go +343 -0
- package/linthost/rules_format_ternary_nullish_parens.go +77 -0
- package/linthost/rules_format_trailing_comma.go +22 -3
- package/linthost/rules_format_whitespace.go +336 -0
- 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 +1057 -2
- 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 +159 -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 +10 -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 +17 -10
- package/src/index.ts +65 -43
- package/src/structures/ITtscLintConfig.ts +14 -8
- package/src/structures/TtscLintRuleSetting.ts +55 -0
- package/src/structures/format/ITtscLintFormat.ts +141 -0
- package/src/structures/format/ITtscLintFormatJsDoc.ts +18 -0
- package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
- package/src/structures/format/index.ts +3 -0
- package/src/structures/index.ts +3 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +117 -0
- package/src/structures/rules/ITtscLintBoundariesRules.ts +91 -0
- package/src/structures/rules/ITtscLintContributorRules.ts +20 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +1378 -0
- package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
- package/src/structures/rules/ITtscLintCypressRules.ts +141 -0
- package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +200 -0
- package/src/structures/rules/ITtscLintFunctionalRules.ts +197 -0
- package/src/structures/rules/ITtscLintJestRules.ts +209 -0
- package/src/structures/rules/ITtscLintJsdocRules.ts +146 -0
- package/src/structures/rules/ITtscLintJsxA11yRules.ts +331 -0
- package/src/structures/rules/ITtscLintNextjsRules.ts +201 -0
- package/src/structures/rules/ITtscLintPlaywrightRules.ts +343 -0
- package/src/structures/rules/ITtscLintPromiseRules.ts +196 -0
- package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
- package/src/structures/rules/ITtscLintReactPerfRules.ts +53 -0
- package/src/structures/rules/ITtscLintReactRuleOptions.ts +41 -0
- package/src/structures/rules/ITtscLintReactRules.ts +308 -0
- package/src/structures/rules/ITtscLintRegexpRules.ts +204 -0
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -0
- package/src/structures/rules/ITtscLintRules.ts +77 -0
- package/src/structures/rules/ITtscLintSecurityRules.ts +155 -0
- package/src/structures/rules/ITtscLintSolidRules.ts +199 -0
- package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +22 -0
- package/src/structures/rules/ITtscLintStorybookRules.ts +184 -0
- package/src/structures/rules/ITtscLintTanstackQueryRules.ts +96 -0
- package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +23 -0
- package/src/structures/rules/ITtscLintTestingLibraryRules.ts +325 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +1172 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
- package/src/structures/rules/ITtscLintVitestRules.ts +146 -0
- package/src/structures/rules/index.ts +31 -0
- package/lib/structures/ITtscLintFormatConfig.d.ts +0 -153
- 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/ITtscLintFormatConfig.ts +0 -168
- package/src/structures/TtscLintRule.ts +0 -160
- package/src/structures/TtscLintRuleMap.ts +0 -55
- package/src/structures/TtscLintRuleOptions.ts +0 -151
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
// typescript/prefer-string-starts-ends-with: prefer
|
|
2
|
+
// `str.startsWith(prefix)` / `str.endsWith(suffix)` over the older
|
|
3
|
+
// `indexOf` / `lastIndexOf` / anchored-regex shapes:
|
|
4
|
+
//
|
|
5
|
+
// - `str.indexOf(p) === 0` → startsWith
|
|
6
|
+
// - `str.indexOf(p, str.length - p.length) !== -1` → endsWith
|
|
7
|
+
// - `str.lastIndexOf(p) === str.length - p.length` → endsWith
|
|
8
|
+
// - `/^prefix/.test(str)` (a static-prefix regex) → startsWith
|
|
9
|
+
// - `/suffix$/.test(str)` (a static-suffix regex) → endsWith
|
|
10
|
+
//
|
|
11
|
+
// The dedicated methods state the intent directly and avoid the
|
|
12
|
+
// off-by-one arithmetic and regex-anchor pitfalls of the older shapes.
|
|
13
|
+
// typescript-eslint:
|
|
14
|
+
// https://typescript-eslint.io/rules/prefer-string-starts-ends-with/
|
|
15
|
+
//
|
|
16
|
+
// Type-aware. Without a Checker the rule cannot prove the receiver of
|
|
17
|
+
// `indexOf` / `lastIndexOf` (or the argument of `.test(str)`) is a
|
|
18
|
+
// `string`, so Context.Checker == nil short-circuits each Check to a
|
|
19
|
+
// no-op the way `no-base-to-string` and `prefer-includes` do. `any` /
|
|
20
|
+
// `unknown` / `never` are intentionally NOT treated as matching — they
|
|
21
|
+
// leak from generic helpers and would explode the false-positive
|
|
22
|
+
// volume on user-defined `indexOf` / `test` methods.
|
|
23
|
+
package linthost
|
|
24
|
+
|
|
25
|
+
import (
|
|
26
|
+
"strings"
|
|
27
|
+
|
|
28
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
29
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
type preferStringStartsEndsWith struct{}
|
|
33
|
+
|
|
34
|
+
func (preferStringStartsEndsWith) Name() string {
|
|
35
|
+
return "typescript/prefer-string-starts-ends-with"
|
|
36
|
+
}
|
|
37
|
+
func (preferStringStartsEndsWith) NeedsTypeChecker() bool {
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
func (preferStringStartsEndsWith) Visits() []shimast.Kind {
|
|
41
|
+
return []shimast.Kind{
|
|
42
|
+
shimast.KindBinaryExpression,
|
|
43
|
+
shimast.KindCallExpression,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
func (preferStringStartsEndsWith) Check(ctx *Context, node *shimast.Node) {
|
|
47
|
+
if ctx.Checker == nil {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
switch node.Kind {
|
|
51
|
+
case shimast.KindBinaryExpression:
|
|
52
|
+
bin := node.AsBinaryExpression()
|
|
53
|
+
if bin == nil || bin.OperatorToken == nil {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
if !preferStringStartsEndsWithIsEqualityOp(bin.OperatorToken.Kind) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
if preferStringStartsEndsWithBinary(ctx, bin.Left, bin.Right) {
|
|
60
|
+
ctx.Report(node, preferStringStartsEndsWithMessage)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if preferStringStartsEndsWithBinary(ctx, bin.Right, bin.Left) {
|
|
64
|
+
ctx.Report(node, preferStringStartsEndsWithMessage)
|
|
65
|
+
}
|
|
66
|
+
case shimast.KindCallExpression:
|
|
67
|
+
call := node.AsCallExpression()
|
|
68
|
+
if call == nil {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
if preferStringStartsEndsWithRegexTest(ctx, call) {
|
|
72
|
+
ctx.Report(node, preferStringStartsEndsWithMessage)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const preferStringStartsEndsWithMessage = "Prefer `String#startsWith` / `String#endsWith` over the equivalent `indexOf` / `lastIndexOf` / anchored-regex idioms — clearer and avoids the off-by-one arithmetic."
|
|
78
|
+
|
|
79
|
+
// preferStringStartsEndsWithIsEqualityOp reports whether `kind` is one
|
|
80
|
+
// of the equality tokens the rule cares about. Only `==`, `===`,
|
|
81
|
+
// `!=`, `!==` matter: every recognized shape compares two integers
|
|
82
|
+
// for equality (or inequality with a sentinel) rather than ordering.
|
|
83
|
+
func preferStringStartsEndsWithIsEqualityOp(kind shimast.Kind) bool {
|
|
84
|
+
switch kind {
|
|
85
|
+
case shimast.KindEqualsEqualsToken,
|
|
86
|
+
shimast.KindEqualsEqualsEqualsToken,
|
|
87
|
+
shimast.KindExclamationEqualsToken,
|
|
88
|
+
shimast.KindExclamationEqualsEqualsToken:
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// preferStringStartsEndsWithBinary inspects `call op other` for the
|
|
95
|
+
// `indexOf` / `lastIndexOf` shapes. Returns true when the receiver of
|
|
96
|
+
// the call is provably string-typed AND the right-hand side matches
|
|
97
|
+
// the sentinel of one of the recognized rewrites:
|
|
98
|
+
//
|
|
99
|
+
// str.indexOf(p) === 0
|
|
100
|
+
// str.indexOf(p, str.length - p.length) !== -1
|
|
101
|
+
// str.lastIndexOf(p) === str.length - p.length
|
|
102
|
+
func preferStringStartsEndsWithBinary(
|
|
103
|
+
ctx *Context,
|
|
104
|
+
callNode, otherNode *shimast.Node,
|
|
105
|
+
) bool {
|
|
106
|
+
callNode = stripParens(callNode)
|
|
107
|
+
otherNode = stripParens(otherNode)
|
|
108
|
+
if callNode == nil || otherNode == nil {
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
if callNode.Kind != shimast.KindCallExpression {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
call := callNode.AsCallExpression()
|
|
115
|
+
if call == nil || call.Expression == nil {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
receiver, method, ok := promisePropertyAccessParts(call.Expression)
|
|
119
|
+
if !ok {
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
if method != "indexOf" && method != "lastIndexOf" {
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
if receiver == nil {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
t := ctx.Checker.GetTypeAtLocation(receiver)
|
|
129
|
+
if !preferStringStartsEndsWithIsString(t) {
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
args := call.Arguments
|
|
133
|
+
if args == nil || len(args.Nodes) == 0 {
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
arg := stripParens(args.Nodes[0])
|
|
137
|
+
if arg == nil {
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
receiverText := nodeText(ctx.File, receiver)
|
|
141
|
+
argText := nodeText(ctx.File, arg)
|
|
142
|
+
otherText := nodeText(ctx.File, otherNode)
|
|
143
|
+
if receiverText == "" || argText == "" {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
switch method {
|
|
147
|
+
case "indexOf":
|
|
148
|
+
// startsWith: `str.indexOf(p) === 0`
|
|
149
|
+
if len(args.Nodes) == 1 {
|
|
150
|
+
return otherText == "0"
|
|
151
|
+
}
|
|
152
|
+
// endsWith via positional indexOf: the second argument must
|
|
153
|
+
// equal `str.length - arg.length`, and the comparison must be
|
|
154
|
+
// against -1 (the sentinel for "not present at that
|
|
155
|
+
// position").
|
|
156
|
+
if len(args.Nodes) != 2 {
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
second := stripParens(args.Nodes[1])
|
|
160
|
+
if !preferStringStartsEndsWithIsLengthMinusLength(ctx, second, receiverText, argText) {
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
return preferStringStartsEndsWithIsMinusOne(otherText)
|
|
164
|
+
case "lastIndexOf":
|
|
165
|
+
// endsWith: `str.lastIndexOf(p) === str.length - p.length`
|
|
166
|
+
if len(args.Nodes) != 1 {
|
|
167
|
+
return false
|
|
168
|
+
}
|
|
169
|
+
return preferStringStartsEndsWithIsLengthMinusLengthText(otherText, receiverText, argText)
|
|
170
|
+
}
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// preferStringStartsEndsWithRegexTest reports whether `call` is a
|
|
175
|
+
// `/^.../.test(str)` or `/...$/.test(str)` invocation on a regex
|
|
176
|
+
// literal whose body is a static prefix or suffix — no metacharacters
|
|
177
|
+
// other than the anchor and no flags that change the matching
|
|
178
|
+
// semantics. The argument to `.test` must be a `string`-typed value;
|
|
179
|
+
// without that the call is not equivalent to a startsWith/endsWith
|
|
180
|
+
// rewrite (e.g. coercing a non-string regex argument).
|
|
181
|
+
func preferStringStartsEndsWithRegexTest(
|
|
182
|
+
ctx *Context,
|
|
183
|
+
call *shimast.CallExpression,
|
|
184
|
+
) bool {
|
|
185
|
+
if call == nil || call.Expression == nil || call.Arguments == nil || len(call.Arguments.Nodes) != 1 {
|
|
186
|
+
return false
|
|
187
|
+
}
|
|
188
|
+
receiver, method, ok := promisePropertyAccessParts(call.Expression)
|
|
189
|
+
if !ok || method != "test" {
|
|
190
|
+
return false
|
|
191
|
+
}
|
|
192
|
+
receiver = stripParens(receiver)
|
|
193
|
+
if receiver == nil || receiver.Kind != shimast.KindRegularExpressionLiteral {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
raw := nodeText(ctx.File, receiver)
|
|
197
|
+
if !preferStringStartsEndsWithIsAnchoredStaticRegex(raw) {
|
|
198
|
+
return false
|
|
199
|
+
}
|
|
200
|
+
arg := stripParens(call.Arguments.Nodes[0])
|
|
201
|
+
if arg == nil {
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
return preferStringStartsEndsWithIsString(ctx.Checker.GetTypeAtLocation(arg))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// preferStringStartsEndsWithIsAnchoredStaticRegex reports whether
|
|
208
|
+
// `raw` (the source text of a RegularExpressionLiteral, including the
|
|
209
|
+
// surrounding `/`) encodes either `^prefix` or `suffix$` with no
|
|
210
|
+
// metacharacters other than the anchor itself, and carries no flags
|
|
211
|
+
// that change matching semantics (`i`, `m`, `s`, `u`, `v`). A flag
|
|
212
|
+
// like `i` would lower-case both sides, which `startsWith` does not
|
|
213
|
+
// do — so the rewrite would change meaning.
|
|
214
|
+
func preferStringStartsEndsWithIsAnchoredStaticRegex(raw string) bool {
|
|
215
|
+
if len(raw) < 3 || raw[0] != '/' {
|
|
216
|
+
return false
|
|
217
|
+
}
|
|
218
|
+
closing := strings.LastIndexByte(raw, '/')
|
|
219
|
+
if closing <= 0 {
|
|
220
|
+
return false
|
|
221
|
+
}
|
|
222
|
+
pattern := raw[1:closing]
|
|
223
|
+
flags := raw[closing+1:]
|
|
224
|
+
if pattern == "" {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
for i := 0; i < len(flags); i++ {
|
|
228
|
+
switch flags[i] {
|
|
229
|
+
case 'g', 'y', 'd':
|
|
230
|
+
// stateful/position flags — fine for `.test` since we
|
|
231
|
+
// only call once
|
|
232
|
+
default:
|
|
233
|
+
return false
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if pattern[0] == '^' {
|
|
237
|
+
return preferStringStartsEndsWithIsStaticString(pattern[1:])
|
|
238
|
+
}
|
|
239
|
+
if pattern[len(pattern)-1] == '$' {
|
|
240
|
+
return preferStringStartsEndsWithIsStaticString(pattern[:len(pattern)-1])
|
|
241
|
+
}
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// preferStringStartsEndsWithIsStaticString reports whether `pattern`
|
|
246
|
+
// is a regex body made up only of literal characters (and accepted
|
|
247
|
+
// escape sequences `\.`, `\\`, etc. that represent a single literal
|
|
248
|
+
// character). Any metacharacter — `^`, `$`, `*`, `+`, `?`, `(`, `)`,
|
|
249
|
+
// `[`, `]`, `{`, `}`, `|`, `.` — disqualifies the body because the
|
|
250
|
+
// startsWith/endsWith rewrite would change matching semantics.
|
|
251
|
+
func preferStringStartsEndsWithIsStaticString(pattern string) bool {
|
|
252
|
+
if pattern == "" {
|
|
253
|
+
return false
|
|
254
|
+
}
|
|
255
|
+
for i := 0; i < len(pattern); i++ {
|
|
256
|
+
ch := pattern[i]
|
|
257
|
+
if ch == '\\' {
|
|
258
|
+
// Accept only escapes that name a single literal
|
|
259
|
+
// character (`\.`, `\$`, `\^`, `\/`, `\\`, `\(`, `\)`,
|
|
260
|
+
// `\[`, `\]`, `\{`, `\}`, `\|`, `\+`, `\*`, `\?`).
|
|
261
|
+
// Reject character-class shortcuts (`\d`, `\w`, `\s`)
|
|
262
|
+
// and back-references — those would not survive a
|
|
263
|
+
// rewrite to startsWith/endsWith.
|
|
264
|
+
if i+1 >= len(pattern) {
|
|
265
|
+
return false
|
|
266
|
+
}
|
|
267
|
+
next := pattern[i+1]
|
|
268
|
+
switch next {
|
|
269
|
+
case '.', '$', '^', '/', '\\', '(', ')', '[', ']', '{', '}', '|', '+', '*', '?', '-':
|
|
270
|
+
i++
|
|
271
|
+
continue
|
|
272
|
+
}
|
|
273
|
+
return false
|
|
274
|
+
}
|
|
275
|
+
switch ch {
|
|
276
|
+
case '^', '$', '*', '+', '?', '(', ')', '[', ']', '{', '}', '|', '.':
|
|
277
|
+
return false
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return true
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// preferStringStartsEndsWithIsLengthMinusLength reports whether
|
|
284
|
+
// `node` textually evaluates to `<receiver>.length - <arg>.length`.
|
|
285
|
+
// The comparison is text-based (after stripping parens) because the
|
|
286
|
+
// rewrite only fires when the call site spelled the offset that way
|
|
287
|
+
// — matching the upstream rule's textual shape detector.
|
|
288
|
+
func preferStringStartsEndsWithIsLengthMinusLength(
|
|
289
|
+
ctx *Context,
|
|
290
|
+
node *shimast.Node,
|
|
291
|
+
receiverText, argText string,
|
|
292
|
+
) bool {
|
|
293
|
+
if node == nil {
|
|
294
|
+
return false
|
|
295
|
+
}
|
|
296
|
+
return preferStringStartsEndsWithIsLengthMinusLengthText(nodeText(ctx.File, node), receiverText, argText)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// preferStringStartsEndsWithIsLengthMinusLengthText is the text-level
|
|
300
|
+
// half of `preferStringStartsEndsWithIsLengthMinusLength` — split out
|
|
301
|
+
// so the `lastIndexOf === str.length - p.length` shape can use the
|
|
302
|
+
// same matcher against the binary RHS text.
|
|
303
|
+
func preferStringStartsEndsWithIsLengthMinusLengthText(text, receiverText, argText string) bool {
|
|
304
|
+
wanted := receiverText + ".length-" + argText + ".length"
|
|
305
|
+
return preferStringStartsEndsWithStripSpaces(text) == wanted
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// preferStringStartsEndsWithStripSpaces removes whitespace from `s`.
|
|
309
|
+
// Used for the textual equality match — `a.length - b.length` and
|
|
310
|
+
// `a.length-b.length` should both match the canonical form.
|
|
311
|
+
func preferStringStartsEndsWithStripSpaces(s string) string {
|
|
312
|
+
var b strings.Builder
|
|
313
|
+
for i := 0; i < len(s); i++ {
|
|
314
|
+
ch := s[i]
|
|
315
|
+
if ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' {
|
|
316
|
+
continue
|
|
317
|
+
}
|
|
318
|
+
b.WriteByte(ch)
|
|
319
|
+
}
|
|
320
|
+
return b.String()
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// preferStringStartsEndsWithIsMinusOne reports whether `text` is the
|
|
324
|
+
// numeric literal `-1` (with or without internal whitespace).
|
|
325
|
+
func preferStringStartsEndsWithIsMinusOne(text string) bool {
|
|
326
|
+
return preferStringStartsEndsWithStripSpaces(text) == "-1"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// preferStringStartsEndsWithIsString reports whether t is provably
|
|
330
|
+
// string-like. Mirrors `isStringLikeType` in no-base-to-string but
|
|
331
|
+
// rejects `any` / `unknown` / `never` so generic helpers don't fire.
|
|
332
|
+
// Union and intersection types must have every constituent string-like.
|
|
333
|
+
func preferStringStartsEndsWithIsString(t *shimchecker.Type) bool {
|
|
334
|
+
if t == nil {
|
|
335
|
+
return false
|
|
336
|
+
}
|
|
337
|
+
flags := t.Flags()
|
|
338
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
|
|
339
|
+
return false
|
|
340
|
+
}
|
|
341
|
+
if flags&shimchecker.TypeFlagsStringLike != 0 {
|
|
342
|
+
return true
|
|
343
|
+
}
|
|
344
|
+
if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
|
|
345
|
+
for _, part := range t.Types() {
|
|
346
|
+
if part == nil {
|
|
347
|
+
continue
|
|
348
|
+
}
|
|
349
|
+
if !preferStringStartsEndsWithIsString(part) {
|
|
350
|
+
return false
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return true
|
|
354
|
+
}
|
|
355
|
+
return false
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
func init() {
|
|
359
|
+
Register(preferStringStartsEndsWith{})
|
|
360
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// promiseFunctionAsync requires a function whose return type is
|
|
2
|
+
// `Promise<T>` (or a Promise-like thenable) to be declared with the
|
|
3
|
+
// `async` keyword. typescript-eslint:
|
|
4
|
+
// https://typescript-eslint.io/rules/promise-function-async/
|
|
5
|
+
//
|
|
6
|
+
// Type-aware. An `async` function wraps a synchronous `throw` into a
|
|
7
|
+
// rejected Promise so the caller's `await` / `.catch(...)` observes
|
|
8
|
+
// it; the non-async equivalent throws synchronously and bypasses every
|
|
9
|
+
// Promise-aware handler downstream. Marking the function `async` keeps
|
|
10
|
+
// the rejection channel consistent with the declared return type.
|
|
11
|
+
//
|
|
12
|
+
// Skipped:
|
|
13
|
+
// - functions already declared `async` (the contract holds);
|
|
14
|
+
// - abstract methods and overload signatures (no body to wrap — the
|
|
15
|
+
// `async` keyword can only be applied to the implementation);
|
|
16
|
+
// - functions whose declared / inferred return type is not a
|
|
17
|
+
// Promise (`any` / `unknown` / `never` pass through, matching the
|
|
18
|
+
// conservative `isPromiseTypedExpression` baseline used by the
|
|
19
|
+
// surrounding async family).
|
|
20
|
+
package linthost
|
|
21
|
+
|
|
22
|
+
import (
|
|
23
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
type promiseFunctionAsync struct{}
|
|
27
|
+
|
|
28
|
+
func (promiseFunctionAsync) Name() string { return "typescript/promise-function-async" }
|
|
29
|
+
func (promiseFunctionAsync) NeedsTypeChecker() bool {
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
func (promiseFunctionAsync) Visits() []shimast.Kind {
|
|
33
|
+
return []shimast.Kind{
|
|
34
|
+
shimast.KindFunctionDeclaration,
|
|
35
|
+
shimast.KindFunctionExpression,
|
|
36
|
+
shimast.KindArrowFunction,
|
|
37
|
+
shimast.KindMethodDeclaration,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
func (promiseFunctionAsync) Check(ctx *Context, node *shimast.Node) {
|
|
41
|
+
if ctx.Checker == nil {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if hasAsyncModifier(node) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
// Skip overload signatures and abstract methods — only the
|
|
48
|
+
// implementation signature carries an executable body that the
|
|
49
|
+
// `async` keyword could wrap.
|
|
50
|
+
if node.Body() == nil {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
if hasModifier(node, shimast.KindAbstractKeyword) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
sig := ctx.Checker.GetSignatureFromDeclaration(node)
|
|
57
|
+
if sig == nil {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
returnType := ctx.Checker.GetReturnTypeOfSignature(sig)
|
|
61
|
+
if returnType == nil {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
if !isPromiseTypedExpression(ctx.Checker, returnType) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
startPos := keywordStart(ctx.File, node, "function")
|
|
68
|
+
if startPos >= 0 {
|
|
69
|
+
ctx.ReportRange(startPos, startPos+len("function"), "Function returning a Promise should be declared `async` so synchronous throws surface as a rejected Promise.")
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
ctx.Report(node, "Function returning a Promise should be declared `async` so synchronous throws surface as a rejected Promise.")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func init() {
|
|
76
|
+
Register(promiseFunctionAsync{})
|
|
77
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// typescript/related-getter-setter-pairs reports a `get` accessor
|
|
2
|
+
// whose declared return type does not match the parameter type of the
|
|
3
|
+
// companion `set` accessor on the same class. The accessor pair
|
|
4
|
+
// presents a single conceptual field at the call site — `obj.value =
|
|
5
|
+
// X` followed by `Y = obj.value` should round-trip with compatible
|
|
6
|
+
// types — but TypeScript otherwise lets the two accessors carry
|
|
7
|
+
// independent annotations. The rule fires when the writer cannot
|
|
8
|
+
// accept what the reader is about to surface.
|
|
9
|
+
// https://typescript-eslint.io/rules/related-getter-setter-pairs/
|
|
10
|
+
//
|
|
11
|
+
// Type-aware. The comparison resolves both sides through
|
|
12
|
+
// `GetTypeFromTypeNode` so type aliases, generic parameters, and union
|
|
13
|
+
// constituents collapse to the same set of values before the
|
|
14
|
+
// equality check. Without a Checker the rule short-circuits to a
|
|
15
|
+
// no-op the way the other type-aware rules do.
|
|
16
|
+
//
|
|
17
|
+
// The rule visits each `KindGetAccessor`. For every getter that
|
|
18
|
+
// carries a return-type annotation and lives inside a class
|
|
19
|
+
// declaration / expression, it locates the matching `set` accessor by
|
|
20
|
+
// name (using the same `classMembers` + `classMemberName` helpers as
|
|
21
|
+
// `class-literal-property-style`) and compares the annotated types
|
|
22
|
+
// bidirectionally — both sides must describe the same set of values
|
|
23
|
+
// for the accessor pair to round-trip safely.
|
|
24
|
+
package linthost
|
|
25
|
+
|
|
26
|
+
import (
|
|
27
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
type relatedGetterSetterPairs struct{}
|
|
31
|
+
|
|
32
|
+
func (relatedGetterSetterPairs) Name() string {
|
|
33
|
+
return "typescript/related-getter-setter-pairs"
|
|
34
|
+
}
|
|
35
|
+
func (relatedGetterSetterPairs) NeedsTypeChecker() bool { return true }
|
|
36
|
+
func (relatedGetterSetterPairs) Visits() []shimast.Kind {
|
|
37
|
+
return []shimast.Kind{shimast.KindGetAccessor}
|
|
38
|
+
}
|
|
39
|
+
func (relatedGetterSetterPairs) Check(ctx *Context, node *shimast.Node) {
|
|
40
|
+
if ctx.Checker == nil {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
parent := node.Parent
|
|
44
|
+
if parent == nil ||
|
|
45
|
+
(parent.Kind != shimast.KindClassDeclaration && parent.Kind != shimast.KindClassExpression) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
getReturnType := node.Type()
|
|
49
|
+
if getReturnType == nil {
|
|
50
|
+
// No annotated return type — nothing to compare against.
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
name := classMemberName(node)
|
|
54
|
+
if name == "" {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
setter := findClassSetterFor(parent, name)
|
|
58
|
+
if setter == nil {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
setParamType := setterParameterType(setter)
|
|
62
|
+
if setParamType == nil {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
getType := ctx.Checker.GetTypeFromTypeNode(getReturnType)
|
|
66
|
+
setType := ctx.Checker.GetTypeFromTypeNode(setParamType)
|
|
67
|
+
if getType == nil || setType == nil {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
// Bidirectional assignability stands in for type identity — the
|
|
71
|
+
// same shape `no-unnecessary-type-assertion` uses. The accessor
|
|
72
|
+
// pair is safe only when reading and writing describe the same
|
|
73
|
+
// set of values.
|
|
74
|
+
if ctx.Checker.IsTypeAssignableTo(getType, setType) &&
|
|
75
|
+
ctx.Checker.IsTypeAssignableTo(setType, getType) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
ctx.Report(node, "Getter return type does not match the corresponding setter parameter type on `"+name+"`.")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// findClassSetterFor returns the `set` accessor declared on `class`
|
|
82
|
+
// whose name matches `name`, or nil when no such setter exists.
|
|
83
|
+
func findClassSetterFor(class *shimast.Node, name string) *shimast.Node {
|
|
84
|
+
for _, member := range classMembers(class) {
|
|
85
|
+
if member == nil || member.Kind != shimast.KindSetAccessor {
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
if classMemberName(member) == name {
|
|
89
|
+
return member
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return nil
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// setterParameterType returns the type-annotation node of a `set`
|
|
96
|
+
// accessor's single parameter. A setter without a parameter, or with a
|
|
97
|
+
// parameter that carries no annotation, returns nil — the rule cannot
|
|
98
|
+
// compare against an inferred type.
|
|
99
|
+
func setterParameterType(setter *shimast.Node) *shimast.Node {
|
|
100
|
+
params := setter.Parameters()
|
|
101
|
+
if len(params) == 0 {
|
|
102
|
+
return nil
|
|
103
|
+
}
|
|
104
|
+
decl := params[0].AsParameterDeclaration()
|
|
105
|
+
if decl == nil {
|
|
106
|
+
return nil
|
|
107
|
+
}
|
|
108
|
+
return decl.Type
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func init() {
|
|
112
|
+
Register(relatedGetterSetterPairs{})
|
|
113
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// typescript/require-array-sort-compare: require an explicit comparator
|
|
2
|
+
// for `arr.sort()` and `arr.toSorted()`. Without a comparator, both
|
|
3
|
+
// methods coerce elements to strings before ordering them, so
|
|
4
|
+
// `[10, 2, 1].sort()` evaluates to `[1, 10, 2]` — almost never what the
|
|
5
|
+
// caller meant. The fix is to pass `(a, b) => a - b`-style comparator.
|
|
6
|
+
// typescript-eslint:
|
|
7
|
+
// https://typescript-eslint.io/rules/require-array-sort-compare/
|
|
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
|
+
// requireArraySortCompare reports `<arr>.sort()` and `<arr>.toSorted()`
|
|
16
|
+
// when no comparator argument is supplied. Type-aware: the Checker
|
|
17
|
+
// confirms the receiver is an array (or tuple) so user-defined methods
|
|
18
|
+
// named `sort` / `toSorted` on non-array types do not trip the rule.
|
|
19
|
+
type requireArraySortCompare struct{}
|
|
20
|
+
|
|
21
|
+
func (requireArraySortCompare) Name() string {
|
|
22
|
+
return "typescript/require-array-sort-compare"
|
|
23
|
+
}
|
|
24
|
+
func (requireArraySortCompare) NeedsTypeChecker() bool {
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
func (requireArraySortCompare) Visits() []shimast.Kind {
|
|
28
|
+
return []shimast.Kind{shimast.KindCallExpression}
|
|
29
|
+
}
|
|
30
|
+
func (requireArraySortCompare) Check(ctx *Context, node *shimast.Node) {
|
|
31
|
+
if ctx.Checker == nil {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
call := node.AsCallExpression()
|
|
35
|
+
if call == nil || call.Expression == nil {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
// Only fire when the callee is a property access ending in `.sort`
|
|
39
|
+
// or `.toSorted` and the call passes zero arguments.
|
|
40
|
+
receiver, method, ok := promisePropertyAccessParts(call.Expression)
|
|
41
|
+
if !ok || (method != "sort" && method != "toSorted") {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
if receiver == nil {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
t := ctx.Checker.GetTypeAtLocation(receiver)
|
|
51
|
+
if t == nil {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
if !requireArraySortCompareIsArrayLike(ctx.Checker, t) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
ctx.Report(node, "Require an explicit compareFunction for `."+method+"()` — without one, elements are coerced to strings before being ordered.")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// requireArraySortCompareIsArrayLike reports whether t is provably an
|
|
61
|
+
// array or a tuple. The check follows the same recurse-into-constituents
|
|
62
|
+
// pattern as `isAwaitable` so a union like `number[] | string[]` still
|
|
63
|
+
// resolves to "array" via every constituent. `any` / `unknown` / `never`
|
|
64
|
+
// are intentionally NOT treated as array-like — they propagate from
|
|
65
|
+
// generic helpers and would explode the false-positive volume.
|
|
66
|
+
func requireArraySortCompareIsArrayLike(checker *shimchecker.Checker, t *shimchecker.Type) bool {
|
|
67
|
+
if checker == nil || t == nil {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
flags := t.Flags()
|
|
71
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
|
|
75
|
+
for _, part := range t.Types() {
|
|
76
|
+
if part == nil {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
if !requireArraySortCompareIsArrayLike(checker, part) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
if shimchecker.Checker_isArrayType(checker, t) {
|
|
86
|
+
return true
|
|
87
|
+
}
|
|
88
|
+
if shimchecker.IsTupleType(t) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func init() {
|
|
95
|
+
Register(requireArraySortCompare{})
|
|
96
|
+
}
|