@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,144 @@
|
|
|
1
|
+
// typescript/sort-type-constituents: union and intersection type
|
|
2
|
+
// constituents render the same set of values regardless of the order
|
|
3
|
+
// the author chose, so two authors writing the same type can disagree
|
|
4
|
+
// on the spelling. The rule pins one canonical ordering — primitives
|
|
5
|
+
// alphabetized first, then named / object types alphabetized, then
|
|
6
|
+
// `null` / `undefined` last — so reviewers don't have to argue about
|
|
7
|
+
// which spelling to land. typescript-eslint:
|
|
8
|
+
// https://typescript-eslint.io/rules/sort-type-constituents/
|
|
9
|
+
package linthost
|
|
10
|
+
|
|
11
|
+
import (
|
|
12
|
+
"sort"
|
|
13
|
+
"strings"
|
|
14
|
+
|
|
15
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// sortTypeConstituents fires on `UnionType` and `IntersectionType` when
|
|
19
|
+
// the constituent order does not match the canonical ordering. The
|
|
20
|
+
// check is AST-only: ordering is decided by syntactic group (keyword
|
|
21
|
+
// primitive vs. named reference vs. nullish) and a within-group source
|
|
22
|
+
// sort, none of which need the Checker.
|
|
23
|
+
//
|
|
24
|
+
// The diagnostic anchors to the union / intersection node so the
|
|
25
|
+
// rendered location names the whole type, not just one constituent.
|
|
26
|
+
// One report per misordered type is enough — the autofixer would
|
|
27
|
+
// rewrite the entire list together anyway.
|
|
28
|
+
type sortTypeConstituents struct{}
|
|
29
|
+
|
|
30
|
+
func (sortTypeConstituents) Name() string {
|
|
31
|
+
return "typescript/sort-type-constituents"
|
|
32
|
+
}
|
|
33
|
+
func (sortTypeConstituents) Visits() []shimast.Kind {
|
|
34
|
+
return []shimast.Kind{
|
|
35
|
+
shimast.KindUnionType,
|
|
36
|
+
shimast.KindIntersectionType,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
func (sortTypeConstituents) Check(ctx *Context, node *shimast.Node) {
|
|
40
|
+
types := unionOrIntersectionTypes(node)
|
|
41
|
+
if len(types) < 2 {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
// Skip nested unions/intersections — the outermost shape is the one
|
|
45
|
+
// the user wrote, and reporting on every nesting level would
|
|
46
|
+
// double-fire on `(A | B) | C` shapes.
|
|
47
|
+
if node.Parent != nil &&
|
|
48
|
+
(node.Parent.Kind == shimast.KindUnionType || node.Parent.Kind == shimast.KindIntersectionType) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
entries := make([]sortTypeConstituentsEntry, len(types))
|
|
52
|
+
for i, t := range types {
|
|
53
|
+
entries[i] = sortTypeConstituentsEntry{
|
|
54
|
+
index: i,
|
|
55
|
+
group: sortTypeConstituentsClassify(t),
|
|
56
|
+
text: strings.TrimSpace(nodeText(ctx.File, t)),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
sorted := make([]sortTypeConstituentsEntry, len(entries))
|
|
60
|
+
copy(sorted, entries)
|
|
61
|
+
sort.SliceStable(sorted, func(i, j int) bool {
|
|
62
|
+
if sorted[i].group != sorted[j].group {
|
|
63
|
+
return sorted[i].group < sorted[j].group
|
|
64
|
+
}
|
|
65
|
+
return sorted[i].text < sorted[j].text
|
|
66
|
+
})
|
|
67
|
+
for i := range entries {
|
|
68
|
+
if entries[i].index != sorted[i].index {
|
|
69
|
+
label := "union"
|
|
70
|
+
if node.Kind == shimast.KindIntersectionType {
|
|
71
|
+
label = "intersection"
|
|
72
|
+
}
|
|
73
|
+
ctx.Report(node, "Constituents of the "+label+" are not in the canonical order — primitives alphabetized first, named types alphabetized next, `null` / `undefined` last.")
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// sortTypeConstituentsEntry is one constituent's sort key. `index`
|
|
80
|
+
// records the original position so the reorder check can compare the
|
|
81
|
+
// before/after sequences without losing identity.
|
|
82
|
+
type sortTypeConstituentsEntry struct {
|
|
83
|
+
index int
|
|
84
|
+
group int
|
|
85
|
+
text string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// sortTypeConstituentsClassify buckets a constituent into one of the
|
|
89
|
+
// canonical groups. Smaller numbers come first in the canonical order.
|
|
90
|
+
func sortTypeConstituentsClassify(node *shimast.Node) int {
|
|
91
|
+
if node == nil {
|
|
92
|
+
return sortTypeConstituentsGroupNamed
|
|
93
|
+
}
|
|
94
|
+
switch node.Kind {
|
|
95
|
+
case shimast.KindStringKeyword,
|
|
96
|
+
shimast.KindNumberKeyword,
|
|
97
|
+
shimast.KindBooleanKeyword,
|
|
98
|
+
shimast.KindBigIntKeyword,
|
|
99
|
+
shimast.KindSymbolKeyword,
|
|
100
|
+
shimast.KindAnyKeyword,
|
|
101
|
+
shimast.KindUnknownKeyword,
|
|
102
|
+
shimast.KindVoidKeyword,
|
|
103
|
+
shimast.KindNeverKeyword,
|
|
104
|
+
shimast.KindObjectKeyword:
|
|
105
|
+
return sortTypeConstituentsGroupPrimitive
|
|
106
|
+
case shimast.KindLiteralType:
|
|
107
|
+
// `null` and `undefined` parse as LiteralType / undefined
|
|
108
|
+
// keyword inside type position — surface them as the nullish
|
|
109
|
+
// group so the canonical order keeps them at the end.
|
|
110
|
+
if sortTypeConstituentsIsNullish(node) {
|
|
111
|
+
return sortTypeConstituentsGroupNullish
|
|
112
|
+
}
|
|
113
|
+
return sortTypeConstituentsGroupLiteral
|
|
114
|
+
case shimast.KindUndefinedKeyword, shimast.KindNullKeyword:
|
|
115
|
+
return sortTypeConstituentsGroupNullish
|
|
116
|
+
}
|
|
117
|
+
return sortTypeConstituentsGroupNamed
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const (
|
|
121
|
+
sortTypeConstituentsGroupPrimitive = iota
|
|
122
|
+
sortTypeConstituentsGroupLiteral
|
|
123
|
+
sortTypeConstituentsGroupNamed
|
|
124
|
+
sortTypeConstituentsGroupNullish
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// sortTypeConstituentsIsNullish reports whether a LiteralType node
|
|
128
|
+
// wraps the `null` keyword token. The `undefined` keyword in type
|
|
129
|
+
// position is a regular Identifier-shaped reference and is handled by
|
|
130
|
+
// the named group, not by this helper.
|
|
131
|
+
func sortTypeConstituentsIsNullish(node *shimast.Node) bool {
|
|
132
|
+
if node == nil || node.Kind != shimast.KindLiteralType {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
lit := node.AsLiteralTypeNode()
|
|
136
|
+
if lit == nil || lit.Literal == nil {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
return lit.Literal.Kind == shimast.KindNullKeyword
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
func init() {
|
|
143
|
+
Register(sortTypeConstituents{})
|
|
144
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// strictBooleanExpressions fires when a non-boolean value is used in
|
|
2
|
+
// a boolean context — the test of an `if` / `while` / `do` / `for` /
|
|
3
|
+
// ternary, the operand of `!`, or either side of `&&` / `||`.
|
|
4
|
+
// JavaScript coerces every truthy / falsy shape silently:
|
|
5
|
+
//
|
|
6
|
+
// - numbers: `0`, `-0`, and `NaN` are falsy; every other number is
|
|
7
|
+
// truthy. `if (count)` therefore skips a legitimate zero.
|
|
8
|
+
// - strings: `""` is falsy; every other string is truthy.
|
|
9
|
+
// `if (name)` therefore skips a legitimate empty string.
|
|
10
|
+
// - nullable objects: `null` / `undefined` are falsy, the object is
|
|
11
|
+
// truthy. `if (obj)` collapses the present-vs-absent and
|
|
12
|
+
// present-but-empty cases together.
|
|
13
|
+
//
|
|
14
|
+
// The fix is always an explicit comparison naming the intent —
|
|
15
|
+
// `count !== 0`, `str.length > 0`, `obj != null`. typescript-eslint:
|
|
16
|
+
// https://typescript-eslint.io/rules/strict-boolean-expressions/
|
|
17
|
+
//
|
|
18
|
+
// Type-aware. Without a Checker the rule cannot tell a `boolean` apart
|
|
19
|
+
// from a `string`, so Context.Checker == nil short-circuits each Check
|
|
20
|
+
// to a no-op the way `no-for-in-array` and `no-misused-promises` do.
|
|
21
|
+
package linthost
|
|
22
|
+
|
|
23
|
+
import (
|
|
24
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
25
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
type strictBooleanExpressions struct{}
|
|
29
|
+
|
|
30
|
+
func (strictBooleanExpressions) Name() string { return "typescript/strict-boolean-expressions" }
|
|
31
|
+
func (strictBooleanExpressions) NeedsTypeChecker() bool {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
func (strictBooleanExpressions) Visits() []shimast.Kind {
|
|
35
|
+
return []shimast.Kind{
|
|
36
|
+
shimast.KindIfStatement,
|
|
37
|
+
shimast.KindWhileStatement,
|
|
38
|
+
shimast.KindDoStatement,
|
|
39
|
+
shimast.KindForStatement,
|
|
40
|
+
shimast.KindConditionalExpression,
|
|
41
|
+
shimast.KindPrefixUnaryExpression,
|
|
42
|
+
shimast.KindBinaryExpression,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
func (strictBooleanExpressions) Check(ctx *Context, node *shimast.Node) {
|
|
46
|
+
if ctx.Checker == nil {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
switch node.Kind {
|
|
50
|
+
case shimast.KindIfStatement:
|
|
51
|
+
if stmt := node.AsIfStatement(); stmt != nil {
|
|
52
|
+
strictBooleanExpressionsReport(ctx, stmt.Expression)
|
|
53
|
+
}
|
|
54
|
+
case shimast.KindWhileStatement:
|
|
55
|
+
if stmt := node.AsWhileStatement(); stmt != nil {
|
|
56
|
+
strictBooleanExpressionsReport(ctx, stmt.Expression)
|
|
57
|
+
}
|
|
58
|
+
case shimast.KindDoStatement:
|
|
59
|
+
if stmt := node.AsDoStatement(); stmt != nil {
|
|
60
|
+
strictBooleanExpressionsReport(ctx, stmt.Expression)
|
|
61
|
+
}
|
|
62
|
+
case shimast.KindForStatement:
|
|
63
|
+
if stmt := node.AsForStatement(); stmt != nil {
|
|
64
|
+
strictBooleanExpressionsReport(ctx, stmt.Condition)
|
|
65
|
+
}
|
|
66
|
+
case shimast.KindConditionalExpression:
|
|
67
|
+
if expr := node.AsConditionalExpression(); expr != nil {
|
|
68
|
+
strictBooleanExpressionsReport(ctx, expr.Condition)
|
|
69
|
+
}
|
|
70
|
+
case shimast.KindPrefixUnaryExpression:
|
|
71
|
+
expr := node.AsPrefixUnaryExpression()
|
|
72
|
+
if expr != nil && expr.Operator == shimast.KindExclamationToken {
|
|
73
|
+
strictBooleanExpressionsReport(ctx, expr.Operand)
|
|
74
|
+
}
|
|
75
|
+
case shimast.KindBinaryExpression:
|
|
76
|
+
bin := node.AsBinaryExpression()
|
|
77
|
+
if bin == nil || bin.OperatorToken == nil {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
// Only `&&` / `||` test their left operand for truthiness.
|
|
81
|
+
// `??` short-circuits on nullish, not on falsy, so a non-boolean
|
|
82
|
+
// left operand is exactly the point of `??` and is not flagged.
|
|
83
|
+
switch bin.OperatorToken.Kind {
|
|
84
|
+
case shimast.KindAmpersandAmpersandToken, shimast.KindBarBarToken:
|
|
85
|
+
strictBooleanExpressionsReport(ctx, bin.Left)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// strictBooleanExpressionsReport flags `expr` when its static type is
|
|
91
|
+
// not pure boolean-like. The recursion arm peels through `&&` / `||`
|
|
92
|
+
// chains so `if (a && b)` reports both `a` and `b`, mirroring the
|
|
93
|
+
// upstream rule. Parenthesized wrappers are skipped via stripParens
|
|
94
|
+
// so `if ((x))` resolves to `x`.
|
|
95
|
+
func strictBooleanExpressionsReport(ctx *Context, expr *shimast.Node) {
|
|
96
|
+
if expr == nil {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
expr = stripParens(expr)
|
|
100
|
+
if expr == nil {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
// Descend through `&&` / `||` — each conjunct/disjunct sits in
|
|
104
|
+
// boolean position too. The outer BinaryExpression visit only
|
|
105
|
+
// checks Left; the descent here finds Right as well.
|
|
106
|
+
if expr.Kind == shimast.KindBinaryExpression {
|
|
107
|
+
bin := expr.AsBinaryExpression()
|
|
108
|
+
if bin != nil && bin.OperatorToken != nil {
|
|
109
|
+
switch bin.OperatorToken.Kind {
|
|
110
|
+
case shimast.KindAmpersandAmpersandToken, shimast.KindBarBarToken:
|
|
111
|
+
strictBooleanExpressionsReport(ctx, bin.Left)
|
|
112
|
+
strictBooleanExpressionsReport(ctx, bin.Right)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
t := ctx.Checker.GetTypeAtLocation(expr)
|
|
118
|
+
if t == nil {
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
if strictBooleanExpressionsIsBoolean(t) {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
ctx.Report(expr, "Unexpected non-boolean value in a boolean conditional position. Use an explicit comparison (`!== 0`, `.length > 0`, `!= null`) to name the intent.")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// strictBooleanExpressionsIsBoolean reports whether t is pure
|
|
128
|
+
// boolean-like. `any` / `unknown` / `never` are intentionally NOT
|
|
129
|
+
// considered boolean — they leak through generic helpers and would
|
|
130
|
+
// suppress the rule everywhere a value of unknown origin lands in a
|
|
131
|
+
// conditional. Union and intersection types must have every
|
|
132
|
+
// constituent boolean for the whole type to count.
|
|
133
|
+
func strictBooleanExpressionsIsBoolean(t *shimchecker.Type) bool {
|
|
134
|
+
if t == nil {
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
flags := t.Flags()
|
|
138
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsUnknown|shimchecker.TypeFlagsNever) != 0 {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
if flags&(shimchecker.TypeFlagsUnion|shimchecker.TypeFlagsIntersection) != 0 {
|
|
142
|
+
for _, part := range t.Types() {
|
|
143
|
+
if part == nil {
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
if !strictBooleanExpressionsIsBoolean(part) {
|
|
147
|
+
return false
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
152
|
+
return flags&shimchecker.TypeFlagsBooleanLike != 0
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
func init() {
|
|
156
|
+
Register(strictBooleanExpressions{})
|
|
157
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// switchExhaustivenessCheck reports `switch (e)` statements where the
|
|
2
|
+
// discriminant is a union of literal types or an enum and at least one
|
|
3
|
+
// constituent has no matching `case` clause AND the switch carries no
|
|
4
|
+
// `default` clause. Either covering every member explicitly or adding a
|
|
5
|
+
// `default` arm makes the intent visible and forces the compiler to
|
|
6
|
+
// flag any future addition to the union/enum at every switch site.
|
|
7
|
+
// typescript-eslint strict-type-checked:
|
|
8
|
+
// https://typescript-eslint.io/rules/switch-exhaustiveness-check/
|
|
9
|
+
package linthost
|
|
10
|
+
|
|
11
|
+
import (
|
|
12
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
13
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// switchExhaustivenessCheck is type-aware: the discriminant is resolved
|
|
17
|
+
// via the Checker, and uncovered members are computed by removing every
|
|
18
|
+
// case-clause expression's type from the union's constituent set. The
|
|
19
|
+
// rule only fires when the discriminant resolves to a union — plain
|
|
20
|
+
// `string` or `number` discriminants are open-ended and outside the
|
|
21
|
+
// rule's scope, matching typescript-eslint's behavior.
|
|
22
|
+
//
|
|
23
|
+
// Identity is established by `*Type` pointer comparison rather than
|
|
24
|
+
// literal value comparison: TypeScript canonicalizes string and number
|
|
25
|
+
// literal types (`stringLiteralTypes` / `numberLiteralTypes` maps inside
|
|
26
|
+
// the checker), so two references to `"a"` resolve to the same `*Type`
|
|
27
|
+
// instance. Enum literal types are likewise canonicalized per enum
|
|
28
|
+
// member, so `case E.A:` and the `E.A` constituent of the discriminant's
|
|
29
|
+
// union share their `*Type` pointer.
|
|
30
|
+
type switchExhaustivenessCheck struct{}
|
|
31
|
+
|
|
32
|
+
func (switchExhaustivenessCheck) Name() string { return "typescript/switch-exhaustiveness-check" }
|
|
33
|
+
func (switchExhaustivenessCheck) NeedsTypeChecker() bool {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
func (switchExhaustivenessCheck) Visits() []shimast.Kind {
|
|
37
|
+
return []shimast.Kind{shimast.KindSwitchStatement}
|
|
38
|
+
}
|
|
39
|
+
func (switchExhaustivenessCheck) Check(ctx *Context, node *shimast.Node) {
|
|
40
|
+
if ctx.Checker == nil {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
sw := node.AsSwitchStatement()
|
|
44
|
+
if sw == nil || sw.Expression == nil || sw.CaseBlock == nil {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
block := sw.CaseBlock.AsCaseBlock()
|
|
48
|
+
if block == nil || block.Clauses == nil {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
// A `default` clause covers anything not enumerated above, so the
|
|
52
|
+
// rule has nothing to add. typescript-eslint also exits early in
|
|
53
|
+
// this case.
|
|
54
|
+
for _, clause := range block.Clauses.Nodes {
|
|
55
|
+
if clause != nil && clause.Kind == shimast.KindDefaultClause {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
discriminantType := ctx.Checker.GetTypeAtLocation(sw.Expression)
|
|
60
|
+
if discriminantType == nil {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
// Only consider discriminants whose static type is a finite union of
|
|
64
|
+
// literal-like constituents. Plain `string` / `number` / `unknown`
|
|
65
|
+
// are open-ended and never exhaustively coverable.
|
|
66
|
+
members := switchExhaustivenessCheckCollectMembers(discriminantType)
|
|
67
|
+
if len(members) == 0 {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
covered := make(map[*shimchecker.Type]bool, len(members))
|
|
71
|
+
for _, clause := range block.Clauses.Nodes {
|
|
72
|
+
if clause == nil || clause.Kind != shimast.KindCaseClause {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
caseClause := clause.AsCaseOrDefaultClause()
|
|
76
|
+
if caseClause == nil || caseClause.Expression == nil {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
caseType := ctx.Checker.GetTypeAtLocation(caseClause.Expression)
|
|
80
|
+
if caseType == nil {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
covered[caseType] = true
|
|
84
|
+
}
|
|
85
|
+
missing := 0
|
|
86
|
+
for _, member := range members {
|
|
87
|
+
if !covered[member] {
|
|
88
|
+
missing++
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if missing == 0 {
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
ctx.Report(node, "Switch is not exhaustive. Cases not matched: add a `case` for every union/enum member, or include a `default` clause.")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// switchExhaustivenessCheckCollectMembers returns the finite literal /
|
|
98
|
+
// enum-literal constituents of t when t is a discriminable union, and
|
|
99
|
+
// nil when t is open-ended (plain `string`, `number`, `any`, `unknown`,
|
|
100
|
+
// etc.). A union whose constituents are all literals or enum literals
|
|
101
|
+
// is treated as exhaustively coverable; any non-literal constituent
|
|
102
|
+
// (e.g. `string` mixed into `"a" | "b" | string`) widens the union and
|
|
103
|
+
// disqualifies the whole discriminant.
|
|
104
|
+
func switchExhaustivenessCheckCollectMembers(t *shimchecker.Type) []*shimchecker.Type {
|
|
105
|
+
if t == nil {
|
|
106
|
+
return nil
|
|
107
|
+
}
|
|
108
|
+
flags := t.Flags()
|
|
109
|
+
if flags&shimchecker.TypeFlagsUnion == 0 {
|
|
110
|
+
return nil
|
|
111
|
+
}
|
|
112
|
+
parts := t.Types()
|
|
113
|
+
if len(parts) == 0 {
|
|
114
|
+
return nil
|
|
115
|
+
}
|
|
116
|
+
members := make([]*shimchecker.Type, 0, len(parts))
|
|
117
|
+
for _, part := range parts {
|
|
118
|
+
if part == nil {
|
|
119
|
+
return nil
|
|
120
|
+
}
|
|
121
|
+
if !switchExhaustivenessCheckIsLiteralLike(part) {
|
|
122
|
+
return nil
|
|
123
|
+
}
|
|
124
|
+
members = append(members, part)
|
|
125
|
+
}
|
|
126
|
+
return members
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// switchExhaustivenessCheckIsLiteralLike reports whether t is a
|
|
130
|
+
// constituent that participates in finite enumeration: a unit type
|
|
131
|
+
// (string/number/bigint/boolean literal), an enum literal, or one of
|
|
132
|
+
// the nullish singletons. The matching set mirrors typescript-eslint's
|
|
133
|
+
// `lib.isTypeFlagSet(constituent, TypeFlags.Literal | ...)` filter.
|
|
134
|
+
func switchExhaustivenessCheckIsLiteralLike(t *shimchecker.Type) bool {
|
|
135
|
+
flags := t.Flags()
|
|
136
|
+
if flags&shimchecker.TypeFlagsLiteral != 0 {
|
|
137
|
+
return true
|
|
138
|
+
}
|
|
139
|
+
if flags&(shimchecker.TypeFlagsUndefined|shimchecker.TypeFlagsNull|shimchecker.TypeFlagsVoid) != 0 {
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func init() {
|
|
146
|
+
Register(switchExhaustivenessCheck{})
|
|
147
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// unboundMethod reports a property access that names a class instance
|
|
2
|
+
// method but is used as a value instead of being immediately invoked.
|
|
3
|
+
// JavaScript methods are not bound to their receiver — `obj.method`,
|
|
4
|
+
// once extracted, loses the `this` binding the body relies on, so
|
|
5
|
+
// passing it as a callback (`setTimeout(obj.method, 0)`) or aliasing
|
|
6
|
+
// it (`const f = obj.method`) silently breaks at the first `this.x`
|
|
7
|
+
// dereference. typescript-eslint:
|
|
8
|
+
// https://typescript-eslint.io/rules/unbound-method/
|
|
9
|
+
//
|
|
10
|
+
// Type-aware. The Checker resolves the property's symbol; the rule
|
|
11
|
+
// fires when any of the symbol's declarations is a class
|
|
12
|
+
// MethodDeclaration. The conservative baseline skips:
|
|
13
|
+
//
|
|
14
|
+
// - the callee position of a call (`obj.method()` is fine);
|
|
15
|
+
// - the left-hand side of an assignment (`obj.method = fn`);
|
|
16
|
+
// - the operand of `typeof`, `delete`, `instanceof`, and `in`;
|
|
17
|
+
// - tagged-template tag positions;
|
|
18
|
+
// - static methods (the constructor that owns them is stably bound);
|
|
19
|
+
// - methods declared only as MethodSignature on an interface or
|
|
20
|
+
// type literal (those are structural — the runtime value may be a
|
|
21
|
+
// plain function property, and flagging them at every consumer
|
|
22
|
+
// produces too many false positives).
|
|
23
|
+
package linthost
|
|
24
|
+
|
|
25
|
+
import (
|
|
26
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
type unboundMethod struct{}
|
|
30
|
+
|
|
31
|
+
func (unboundMethod) Name() string { return "typescript/unbound-method" }
|
|
32
|
+
func (unboundMethod) NeedsTypeChecker() bool {
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
func (unboundMethod) Visits() []shimast.Kind {
|
|
36
|
+
return []shimast.Kind{shimast.KindPropertyAccessExpression}
|
|
37
|
+
}
|
|
38
|
+
func (unboundMethod) Check(ctx *Context, node *shimast.Node) {
|
|
39
|
+
if ctx.Checker == nil {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
access := node.AsPropertyAccessExpression()
|
|
43
|
+
if access == nil || access.Name() == nil {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
if unboundMethodSafePosition(node) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
symbol := ctx.Checker.GetSymbolAtLocation(access.Name())
|
|
50
|
+
if symbol == nil {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
if !unboundMethodSymbolIsClassMethod(symbol) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
ctx.Report(node, "Avoid referencing an unbound method which may cause unintentional scoping of `this`.")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// unboundMethodSafePosition reports whether the property access sits in
|
|
60
|
+
// a syntactic position that already discharges the unbound-method risk.
|
|
61
|
+
// The list mirrors typescript-eslint's safe-position whitelist: an
|
|
62
|
+
// immediately-invoked call, an assignment target, a typeof / delete
|
|
63
|
+
// operand, an instanceof / in operand, and a tagged-template tag are
|
|
64
|
+
// all positions where the method reference is consumed in place rather
|
|
65
|
+
// than carried away as a free function value.
|
|
66
|
+
func unboundMethodSafePosition(node *shimast.Node) bool {
|
|
67
|
+
parent := node.Parent
|
|
68
|
+
if parent == nil {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
switch parent.Kind {
|
|
72
|
+
case shimast.KindCallExpression:
|
|
73
|
+
call := parent.AsCallExpression()
|
|
74
|
+
if call != nil && call.Expression == node {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
case shimast.KindTaggedTemplateExpression:
|
|
78
|
+
return true
|
|
79
|
+
case shimast.KindBinaryExpression:
|
|
80
|
+
bin := parent.AsBinaryExpression()
|
|
81
|
+
if bin == nil || bin.OperatorToken == nil {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
switch bin.OperatorToken.Kind {
|
|
85
|
+
case shimast.KindEqualsToken,
|
|
86
|
+
shimast.KindInstanceOfKeyword,
|
|
87
|
+
shimast.KindInKeyword:
|
|
88
|
+
return true
|
|
89
|
+
}
|
|
90
|
+
case shimast.KindTypeOfExpression, shimast.KindDeleteExpression:
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// unboundMethodSymbolIsClassMethod reports whether symbol denotes a
|
|
97
|
+
// non-static class instance method. The rule ignores plain method
|
|
98
|
+
// signatures on interfaces and type literals because those are
|
|
99
|
+
// structural — the runtime value may be a regular function rather
|
|
100
|
+
// than a bound class member, and flagging them at every consumer
|
|
101
|
+
// would produce a high false-positive volume. Static methods slip
|
|
102
|
+
// past this check because they are accessed through the class
|
|
103
|
+
// constructor itself, which is bound for the lifetime of the
|
|
104
|
+
// program.
|
|
105
|
+
func unboundMethodSymbolIsClassMethod(symbol *shimast.Symbol) bool {
|
|
106
|
+
if symbol == nil {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
for _, decl := range symbol.Declarations {
|
|
110
|
+
if decl == nil {
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
if decl.Kind != shimast.KindMethodDeclaration {
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
if hasModifier(decl, shimast.KindStaticKeyword) {
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
func init() {
|
|
125
|
+
Register(unboundMethod{})
|
|
126
|
+
}
|