@ttsc/lint 0.13.0 → 0.14.0-dev.20260528
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +948 -69
- package/lib/defaultFormat.d.ts +9 -6
- package/lib/defaultFormat.js +9 -6
- package/lib/defaultFormat.js.map +1 -1
- package/lib/index.d.ts +46 -0
- package/lib/index.js +60 -37
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +12 -6
- package/lib/structures/{ITtscLintFormatConfig.d.ts → ITtscLintFormat.d.ts} +2 -2
- package/lib/structures/{TtscLintRuleMap.js → ITtscLintFormat.js} +1 -1
- package/lib/structures/ITtscLintFormat.js.map +1 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
- package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
- package/lib/structures/TtscLintRuleSetting.js.map +1 -0
- package/lib/structures/index.d.ts +3 -4
- package/lib/structures/index.js +3 -4
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +104 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +14 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +79 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +17 -0
- package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
- package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +1257 -0
- package/lib/structures/{ITtscLintFormatConfig.js → rules/ITtscLintCoreRules.js} +1 -1
- package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCypressRules.d.ts +129 -0
- package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
- package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +157 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +167 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJestRules.d.ts +201 -0
- package/lib/structures/rules/ITtscLintJestRules.js +3 -0
- package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJsdocRules.d.ts +140 -0
- package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
- package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +302 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintNextjsRules.d.ts +185 -0
- package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
- package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +330 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintPromiseRules.d.ts +188 -0
- package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
- package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +51 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +39 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js +12 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRules.d.ts +289 -0
- package/lib/structures/rules/ITtscLintReactRules.js +3 -0
- package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +190 -0
- package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
- package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +66 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
- package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
- package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
- package/lib/structures/rules/ITtscLintRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintSecurityRules.d.ts +148 -0
- package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
- package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +181 -0
- package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
- package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +21 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintStorybookRules.d.ts +174 -0
- package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
- package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +94 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +22 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +310 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1132 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
- package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintVitestRules.d.ts +141 -0
- package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
- package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
- package/lib/structures/rules/index.d.ts +31 -0
- package/lib/structures/rules/index.js +48 -0
- package/lib/structures/rules/index.js.map +1 -0
- package/linthost/ast_helpers.go +30 -5
- package/linthost/compile.go +33 -2
- package/linthost/config.go +48 -17
- package/linthost/config_format.go +9 -9
- package/linthost/directives.go +67 -25
- package/linthost/dispatch.go +14 -3
- package/linthost/engine.go +160 -40
- package/linthost/fix.go +2 -2
- package/linthost/flags_gen.go +17 -15
- package/linthost/host.go +40 -3
- package/linthost/lsp.go +747 -0
- package/linthost/print_dispatch.go +2 -2
- package/linthost/print_nodes_function.go +2 -2
- package/linthost/print_nodes_object.go +1 -1
- package/linthost/rule_names.go +14 -0
- package/linthost/rules_arrays.go +2 -2
- package/linthost/rules_boundaries.go +532 -0
- package/linthost/rules_camelcase.go +133 -0
- package/linthost/rules_complexity.go +94 -0
- package/linthost/rules_consistent_return.go +130 -0
- package/linthost/rules_console.go +1 -1
- package/linthost/rules_core_extra.go +739 -0
- package/linthost/rules_curly.go +70 -0
- package/linthost/rules_cypress.go +608 -0
- package/linthost/rules_debugger.go +2 -2
- package/linthost/rules_default_case.go +39 -0
- package/linthost/rules_default_case_last.go +46 -0
- package/linthost/rules_dupes.go +3 -3
- package/linthost/rules_empty.go +3 -3
- package/linthost/rules_escape.go +44 -24
- package/linthost/rules_eval.go +2 -2
- package/linthost/rules_finally.go +2 -2
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_print_width.go +9 -9
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +1 -1
- package/linthost/rules_format_sort_imports.go +1 -1
- package/linthost/rules_format_trailing_comma.go +1 -1
- package/linthost/rules_functional.go +967 -0
- package/linthost/rules_gap.go +24 -45
- package/linthost/rules_grouped_accessor_pairs.go +87 -0
- package/linthost/rules_guard_for_in.go +198 -0
- package/linthost/rules_id_length.go +77 -0
- package/linthost/rules_imports.go +70 -2
- package/linthost/rules_init_declarations.go +53 -0
- package/linthost/rules_jest.go +618 -0
- package/linthost/rules_jsdoc.go +502 -0
- package/linthost/rules_jsx_a11y.go +1264 -0
- package/linthost/rules_logic.go +12 -12
- package/linthost/rules_loops.go +1 -1
- package/linthost/rules_max_classes_per_file.go +58 -0
- package/linthost/rules_max_depth.go +83 -0
- package/linthost/rules_max_lines.go +101 -0
- package/linthost/rules_max_lines_per_function.go +57 -0
- package/linthost/rules_max_nested_callbacks.go +61 -0
- package/linthost/rules_max_params.go +49 -0
- package/linthost/rules_max_statements.go +59 -0
- package/linthost/rules_misc.go +1 -1
- package/linthost/rules_nextjs.go +697 -0
- package/linthost/rules_no_else_return.go +75 -0
- package/linthost/rules_no_empty_named_blocks.go +91 -0
- package/linthost/rules_no_extend_native.go +51 -0
- package/linthost/rules_no_import_assign.go +140 -0
- package/linthost/rules_no_invalid_this.go +52 -0
- package/linthost/rules_no_loop_func.go +85 -0
- package/linthost/rules_no_magic_numbers.go +123 -0
- package/linthost/rules_no_mixed_operators.go +80 -0
- package/linthost/rules_no_param_reassign.go +98 -0
- package/linthost/rules_no_redeclare.go +270 -0
- package/linthost/rules_no_restricted_imports.go +105 -0
- package/linthost/rules_no_restricted_syntax.go +49 -0
- package/linthost/rules_no_shadow.go +147 -0
- package/linthost/rules_no_unreachable.go +77 -0
- package/linthost/rules_no_useless_assignment.go +120 -0
- package/linthost/rules_no_useless_constructor.go +245 -0
- package/linthost/rules_no_useless_return.go +51 -0
- package/linthost/rules_params.go +1 -1
- package/linthost/rules_playwright.go +788 -0
- package/linthost/rules_prefer_arrow_callback.go +124 -0
- package/linthost/rules_prefer_destructuring.go +98 -0
- package/linthost/rules_prefer_named_capture_group.go +129 -0
- package/linthost/rules_prefer_rest_params.go +94 -0
- package/linthost/rules_problems.go +41 -30
- package/linthost/rules_promise.go +1128 -73
- package/linthost/rules_protos.go +2 -2
- package/linthost/rules_react.go +773 -0
- package/linthost/rules_react_extras.go +227 -0
- package/linthost/rules_react_hooks.go +848 -0
- package/linthost/rules_react_perf.go +293 -0
- package/linthost/rules_react_refresh.go +583 -0
- package/linthost/rules_regexp.go +410 -0
- package/linthost/rules_security.go +830 -0
- package/linthost/rules_self.go +2 -2
- package/linthost/rules_solid.go +1073 -0
- package/linthost/rules_sort_imports.go +69 -0
- package/linthost/rules_sort_keys.go +134 -0
- package/linthost/rules_storybook.go +1176 -0
- package/linthost/rules_strings.go +4 -4
- package/linthost/rules_suggestions.go +38 -38
- package/linthost/rules_tanstack_query.go +844 -0
- package/linthost/rules_testing_library.go +1204 -0
- package/linthost/rules_throw.go +1 -1
- package/linthost/rules_ts.go +19 -19
- package/linthost/rules_ts_async.go +399 -0
- package/linthost/rules_ts_class_literal_property_style.go +115 -0
- package/linthost/rules_ts_consistent_type_exports.go +256 -0
- package/linthost/rules_ts_explicit_function_return_type.go +67 -0
- package/linthost/rules_ts_extra.go +155 -36
- package/linthost/rules_ts_no_base_to_string.go +218 -0
- package/linthost/rules_ts_no_confusing_void.go +72 -0
- package/linthost/rules_ts_no_deprecated.go +195 -0
- package/linthost/rules_ts_no_for_in_array.go +84 -0
- package/linthost/rules_ts_no_magic_numbers.go +124 -0
- package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
- package/linthost/rules_ts_no_misused_spread.go +104 -0
- package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
- package/linthost/rules_ts_no_restricted_types.go +62 -0
- package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
- package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
- package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
- package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
- package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
- package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
- package/linthost/rules_ts_no_unsafe_argument.go +82 -0
- package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
- package/linthost/rules_ts_no_unsafe_call.go +82 -0
- package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
- package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
- package/linthost/rules_ts_no_unsafe_return.go +126 -0
- package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
- package/linthost/rules_ts_no_useless_constructor.go +52 -0
- package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
- package/linthost/rules_ts_parameter_properties.go +38 -0
- package/linthost/rules_ts_prefer_find.go +178 -0
- package/linthost/rules_ts_prefer_includes.go +255 -0
- package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
- package/linthost/rules_ts_prefer_optional_chain.go +228 -0
- package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
- package/linthost/rules_ts_prefer_readonly.go +73 -0
- package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
- package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
- package/linthost/rules_ts_prefer_return_this_type.go +135 -0
- package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
- package/linthost/rules_ts_promise_function_async.go +77 -0
- package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
- package/linthost/rules_ts_require_array_sort_compare.go +96 -0
- package/linthost/rules_ts_restrict_plus_operands.go +127 -0
- package/linthost/rules_ts_restrict_template_expressions.go +95 -0
- package/linthost/rules_ts_simple.go +387 -0
- package/linthost/rules_ts_sort_type_constituents.go +144 -0
- package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
- package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
- package/linthost/rules_ts_unbound_method.go +126 -0
- package/linthost/rules_tsdoc.go +136 -0
- package/linthost/rules_unicorn_better_regex.go +27 -0
- package/linthost/rules_unicorn_catch_error_name.go +46 -0
- package/linthost/rules_unicorn_consistent_assert.go +47 -0
- package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
- package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
- package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
- package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
- package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
- package/linthost/rules_unicorn_custom_error_definition.go +138 -0
- package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
- package/linthost/rules_unicorn_error_message.go +64 -0
- package/linthost/rules_unicorn_escape_case.go +51 -0
- package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
- package/linthost/rules_unicorn_explicit_length_check.go +88 -0
- package/linthost/rules_unicorn_filename_case.go +29 -0
- package/linthost/rules_unicorn_import_style.go +28 -0
- package/linthost/rules_unicorn_isolated_functions.go +29 -0
- package/linthost/rules_unicorn_new_for_builtins.go +87 -0
- package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
- package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
- package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
- package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
- package/linthost/rules_unicorn_no_array_for_each.go +40 -0
- package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
- package/linthost/rules_unicorn_no_array_reduce.go +41 -0
- package/linthost/rules_unicorn_no_array_reverse.go +44 -0
- package/linthost/rules_unicorn_no_array_sort.go +43 -0
- package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
- package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
- package/linthost/rules_unicorn_no_console_spaces.go +61 -0
- package/linthost/rules_unicorn_no_document_cookie.go +41 -0
- package/linthost/rules_unicorn_no_empty_file.go +44 -0
- package/linthost/rules_unicorn_no_for_loop.go +102 -0
- package/linthost/rules_unicorn_no_hex_escape.go +42 -0
- package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
- package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
- package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
- package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
- package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
- package/linthost/rules_unicorn_no_lonely_if.go +44 -0
- package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
- package/linthost/rules_unicorn_no_named_default.go +54 -0
- package/linthost/rules_unicorn_no_negated_condition.go +85 -0
- package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
- package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
- package/linthost/rules_unicorn_no_new_array.go +30 -0
- package/linthost/rules_unicorn_no_new_buffer.go +31 -0
- package/linthost/rules_unicorn_no_null.go +24 -0
- package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
- package/linthost/rules_unicorn_no_process_exit.go +43 -0
- package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
- package/linthost/rules_unicorn_no_static_only_class.go +56 -0
- package/linthost/rules_unicorn_no_thenable.go +74 -0
- package/linthost/rules_unicorn_no_this_assignment.go +35 -0
- package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
- package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
- package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
- package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
- package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
- package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
- package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
- package/linthost/rules_unicorn_no_unused_properties.go +30 -0
- package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
- package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
- package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
- package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
- package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
- package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
- package/linthost/rules_unicorn_no_useless_spread.go +67 -0
- package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
- package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
- package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
- package/linthost/rules_unicorn_number_literal_case.go +69 -0
- package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
- package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
- package/linthost/rules_unicorn_prefer_array_find.go +51 -0
- package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
- package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
- package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
- package/linthost/rules_unicorn_prefer_array_some.go +67 -0
- package/linthost/rules_unicorn_prefer_at.go +77 -0
- package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
- package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
- package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
- package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
- package/linthost/rules_unicorn_prefer_code_point.go +49 -0
- package/linthost/rules_unicorn_prefer_date_now.go +78 -0
- package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
- package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
- package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
- package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
- package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
- package/linthost/rules_unicorn_prefer_event_target.go +36 -0
- package/linthost/rules_unicorn_prefer_export_from.go +129 -0
- package/linthost/rules_unicorn_prefer_global_this.go +127 -0
- package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
- package/linthost/rules_unicorn_prefer_includes.go +149 -0
- package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
- package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
- package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
- package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
- package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
- package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
- package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
- package/linthost/rules_unicorn_prefer_module.go +50 -0
- package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
- package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
- package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
- package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
- package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
- package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
- package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
- package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
- package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
- package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
- package/linthost/rules_unicorn_prefer_set_has.go +42 -0
- package/linthost/rules_unicorn_prefer_set_size.go +48 -0
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
- package/linthost/rules_unicorn_prefer_single_call.go +96 -0
- package/linthost/rules_unicorn_prefer_spread.go +51 -0
- package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
- package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
- package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
- package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
- package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
- package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
- package/linthost/rules_unicorn_prefer_switch.go +103 -0
- package/linthost/rules_unicorn_prefer_ternary.go +71 -0
- package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
- package/linthost/rules_unicorn_prefer_type_error.go +127 -0
- package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
- package/linthost/rules_unicorn_relative_url_style.go +49 -0
- package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
- package/linthost/rules_unicorn_require_module_attributes.go +54 -0
- package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
- package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
- package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
- package/linthost/rules_unicorn_string_content.go +27 -0
- package/linthost/rules_unicorn_switch_case_braces.go +42 -0
- package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
- package/linthost/rules_unicorn_template_indent.go +28 -0
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
- package/linthost/rules_unicorn_throw_new_error.go +61 -0
- package/linthost/rules_var.go +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 +11 -8
- package/src/index.ts +65 -43
- package/src/structures/ITtscLintConfig.ts +12 -6
- package/src/structures/{ITtscLintFormatConfig.ts → ITtscLintFormat.ts} +2 -2
- package/src/structures/TtscLintRuleSetting.ts +55 -0
- package/src/structures/index.ts +3 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +128 -0
- package/src/structures/rules/ITtscLintBoundariesRules.ts +95 -0
- package/src/structures/rules/ITtscLintContributorRules.ts +21 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +1406 -0
- package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
- package/src/structures/rules/ITtscLintCypressRules.ts +145 -0
- package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +213 -0
- package/src/structures/rules/ITtscLintFunctionalRules.ts +202 -0
- package/src/structures/rules/ITtscLintJestRules.ts +220 -0
- package/src/structures/rules/ITtscLintJsdocRules.ts +153 -0
- package/src/structures/rules/ITtscLintJsxA11yRules.ts +339 -0
- package/src/structures/rules/ITtscLintNextjsRules.ts +206 -0
- package/src/structures/rules/ITtscLintPlaywrightRules.ts +361 -0
- package/src/structures/rules/ITtscLintPromiseRules.ts +205 -0
- package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
- package/src/structures/rules/ITtscLintReactPerfRules.ts +55 -0
- package/src/structures/rules/ITtscLintReactRuleOptions.ts +43 -0
- package/src/structures/rules/ITtscLintReactRules.ts +321 -0
- package/src/structures/rules/ITtscLintRegexpRules.ts +212 -0
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +89 -0
- package/src/structures/rules/ITtscLintRules.ts +77 -0
- package/src/structures/rules/ITtscLintSecurityRules.ts +162 -0
- package/src/structures/rules/ITtscLintSolidRules.ts +202 -0
- package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +23 -0
- package/src/structures/rules/ITtscLintStorybookRules.ts +193 -0
- package/src/structures/rules/ITtscLintTanstackQueryRules.ts +102 -0
- package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +24 -0
- package/src/structures/rules/ITtscLintTestingLibraryRules.ts +342 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +1230 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
- package/src/structures/rules/ITtscLintVitestRules.ts +154 -0
- package/src/structures/rules/index.ts +31 -0
- package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
- package/lib/structures/TtscLintRule.d.ts +0 -17
- package/lib/structures/TtscLintRule.js.map +0 -1
- package/lib/structures/TtscLintRuleMap.d.ts +0 -46
- package/lib/structures/TtscLintRuleMap.js.map +0 -1
- package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
- package/lib/structures/TtscLintRuleOptions.js.map +0 -1
- package/src/structures/TtscLintRule.ts +0 -160
- package/src/structures/TtscLintRuleMap.ts +0 -55
- package/src/structures/TtscLintRuleOptions.ts +0 -151
|
@@ -0,0 +1,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
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
4
|
+
|
|
5
|
+
// tsdocSyntax validates a conservative subset of eslint-plugin-tsdoc's
|
|
6
|
+
// `tsdoc/syntax` rule. It intentionally starts with structural diagnostics
|
|
7
|
+
// that can be checked from source text alone: malformed top-level block tags
|
|
8
|
+
// and malformed or unclosed inline tags.
|
|
9
|
+
type tsdocSyntax struct{}
|
|
10
|
+
|
|
11
|
+
func (tsdocSyntax) Name() string { return "jsdoc/tsdoc-syntax" }
|
|
12
|
+
func (tsdocSyntax) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
|
|
13
|
+
|
|
14
|
+
func (tsdocSyntax) Check(ctx *Context, node *shimast.Node) {
|
|
15
|
+
if ctx == nil || ctx.File == nil {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
src := ctx.File.Text()
|
|
19
|
+
for _, block := range findJSDocBlocks(src) {
|
|
20
|
+
checkTSDocBlockTags(ctx, src, block)
|
|
21
|
+
checkTSDocInlineTags(ctx, src, block)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func checkTSDocBlockTags(ctx *Context, src string, block jsdocBlock) {
|
|
26
|
+
walkJSDocLines(src, block, func(contentStart, lineEnd int, inFence bool) {
|
|
27
|
+
if inFence || contentStart >= lineEnd || src[contentStart] != '@' {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
tagStart := contentStart + 1
|
|
31
|
+
if tagStart >= lineEnd || !isTSDocTagStart(src[tagStart]) {
|
|
32
|
+
ctx.ReportRange(contentStart, minInt(lineEnd, contentStart+2), "Malformed TSDoc block tag.")
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func checkTSDocInlineTags(ctx *Context, src string, block jsdocBlock) {
|
|
38
|
+
walkJSDocLines(src, block, func(contentStart, lineEnd int, inFence bool) {
|
|
39
|
+
if inFence {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
for i := contentStart; i+1 < lineEnd; i++ {
|
|
43
|
+
if src[i] != '{' || src[i+1] != '@' {
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
tagStart := i + 2
|
|
47
|
+
if tagStart >= lineEnd || !isTSDocTagStart(src[tagStart]) {
|
|
48
|
+
ctx.ReportRange(i, minInt(lineEnd, i+2), "Malformed TSDoc inline tag.")
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
tagEnd := tagStart + 1
|
|
52
|
+
for tagEnd < lineEnd && isTSDocTagContinue(src[tagEnd]) {
|
|
53
|
+
tagEnd++
|
|
54
|
+
}
|
|
55
|
+
close := findByteInRange(src, '}', tagEnd, block.bodyEnd)
|
|
56
|
+
if close < 0 {
|
|
57
|
+
ctx.ReportRange(i, tagEnd, "Unclosed TSDoc inline tag.")
|
|
58
|
+
i = tagEnd - 1
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
i = close
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func walkJSDocLines(src string, block jsdocBlock, visit func(contentStart, lineEnd int, inFence bool)) {
|
|
67
|
+
inFence := false
|
|
68
|
+
for lineStart := block.bodyStart; lineStart < block.bodyEnd; {
|
|
69
|
+
lineEnd := lineStart
|
|
70
|
+
for lineEnd < block.bodyEnd && src[lineEnd] != '\n' {
|
|
71
|
+
lineEnd++
|
|
72
|
+
}
|
|
73
|
+
contentStart := tsdocLineContentStart(src, lineStart, lineEnd)
|
|
74
|
+
visit(contentStart, trimLineEnd(src, lineEnd), inFence)
|
|
75
|
+
if startsWithFenceMarker(src, contentStart, trimLineEnd(src, lineEnd)) {
|
|
76
|
+
inFence = !inFence
|
|
77
|
+
}
|
|
78
|
+
if lineEnd >= block.bodyEnd {
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
lineStart = lineEnd + 1
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func tsdocLineContentStart(src string, lineStart, lineEnd int) int {
|
|
86
|
+
i := lineStart
|
|
87
|
+
for i < lineEnd && (src[i] == ' ' || src[i] == '\t' || src[i] == '\r') {
|
|
88
|
+
i++
|
|
89
|
+
}
|
|
90
|
+
if i < lineEnd && src[i] == '*' {
|
|
91
|
+
i++
|
|
92
|
+
if i < lineEnd && src[i] == ' ' {
|
|
93
|
+
i++
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return i
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func trimLineEnd(src string, lineEnd int) int {
|
|
100
|
+
for lineEnd > 0 && src[lineEnd-1] == '\r' {
|
|
101
|
+
lineEnd--
|
|
102
|
+
}
|
|
103
|
+
return lineEnd
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func startsWithFenceMarker(src string, start, end int) bool {
|
|
107
|
+
return end-start >= 3 && src[start] == '`' && src[start+1] == '`' && src[start+2] == '`'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
func findByteInRange(src string, target byte, start, end int) int {
|
|
111
|
+
for i := start; i < end; i++ {
|
|
112
|
+
if src[i] == target {
|
|
113
|
+
return i
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return -1
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func isTSDocTagStart(b byte) bool {
|
|
120
|
+
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func isTSDocTagContinue(b byte) bool {
|
|
124
|
+
return isTSDocTagStart(b) || (b >= '0' && b <= '9') || b == '-'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func minInt(a, b int) int {
|
|
128
|
+
if a < b {
|
|
129
|
+
return a
|
|
130
|
+
}
|
|
131
|
+
return b
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
func init() {
|
|
135
|
+
Register(tsdocSyntax{})
|
|
136
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// unicorn/better-regex: the upstream rule rewrites regex literals into
|
|
2
|
+
// the shortest equivalent form (e.g. `[0-9]` -> `\d`, `[^\W\D]` -> `\d`,
|
|
3
|
+
// dedupes character classes, collapses redundant quantifiers). That
|
|
4
|
+
// rewrite requires a full regex parser with character-class semantics —
|
|
5
|
+
// far beyond the AST-only scope of this slice — so the rule is
|
|
6
|
+
// registered as a no-op stub to keep `TestTypedKeysMatchRegisteredRules`
|
|
7
|
+
// honest with the typed `ITtscLintUnicornRules` surface.
|
|
8
|
+
//
|
|
9
|
+
// Replace this stub with a real implementation when a regex AST helper
|
|
10
|
+
// lands; the matching fixture under
|
|
11
|
+
// `tests/test-lint/src/cases/unicorn-better-regex.ts` carries the
|
|
12
|
+
// `@ttsc-corpus-skip` directive so the corpus test suite walks past it.
|
|
13
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/better-regex.md
|
|
14
|
+
package linthost
|
|
15
|
+
|
|
16
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
17
|
+
|
|
18
|
+
type unicornBetterRegex struct{}
|
|
19
|
+
|
|
20
|
+
func (unicornBetterRegex) Name() string { return "unicorn/better-regex" }
|
|
21
|
+
func (unicornBetterRegex) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
|
|
22
|
+
func (unicornBetterRegex) Check(_ *Context, _ *shimast.Node) {
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func init() {
|
|
26
|
+
Register(unicornBetterRegex{})
|
|
27
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// unicorn/catch-error-name: a `catch (err)` clause whose binding is
|
|
2
|
+
// anything other than `error` violates the project convention. The
|
|
3
|
+
// stylistic point is that readers should see `catch (error)` and
|
|
4
|
+
// immediately know the binding is the thrown value — bespoke names
|
|
5
|
+
// like `e`, `err`, or `exception` create one-character variance for
|
|
6
|
+
// no semantic gain.
|
|
7
|
+
//
|
|
8
|
+
// AST-only: visit each `CatchClause`, skip clauses with no binding
|
|
9
|
+
// (optional catch-binding) and clauses whose binding is a destructuring
|
|
10
|
+
// pattern (out of scope — the rule cannot rename a pattern in place).
|
|
11
|
+
// When the binding identifier text is not `error`, report on the
|
|
12
|
+
// identifier node so the diagnostic underlines the offending name.
|
|
13
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/catch-error-name.md
|
|
14
|
+
package linthost
|
|
15
|
+
|
|
16
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
17
|
+
|
|
18
|
+
type unicornCatchErrorName struct{}
|
|
19
|
+
|
|
20
|
+
func (unicornCatchErrorName) Name() string { return "unicorn/catch-error-name" }
|
|
21
|
+
func (unicornCatchErrorName) Visits() []shimast.Kind {
|
|
22
|
+
return []shimast.Kind{shimast.KindCatchClause}
|
|
23
|
+
}
|
|
24
|
+
func (unicornCatchErrorName) Check(ctx *Context, node *shimast.Node) {
|
|
25
|
+
catch := node.AsCatchClause()
|
|
26
|
+
if catch == nil || catch.VariableDeclaration == nil {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
binding := catch.VariableDeclaration.Name()
|
|
30
|
+
if binding == nil {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
// Destructuring (object/array binding pattern) is out of scope.
|
|
34
|
+
if binding.Kind != shimast.KindIdentifier {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
name := identifierText(binding)
|
|
38
|
+
if name == "" || name == "error" {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
ctx.Report(binding, "The catch parameter should be named `error`.")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func init() {
|
|
45
|
+
Register(unicornCatchErrorName{})
|
|
46
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// unicorn/consistent-assert: `assert.equal` and `assert.notEqual` perform
|
|
2
|
+
// loose equality (`==` / `!=`) at the runtime layer. The Node `assert`
|
|
3
|
+
// module ships strict counterparts — `assert.strictEqual` and
|
|
4
|
+
// `assert.notStrictEqual` — that read identically at the call site but
|
|
5
|
+
// hold the stronger invariant. The rule pins the strict variants so
|
|
6
|
+
// equality assertions don't silently accept type coercions.
|
|
7
|
+
//
|
|
8
|
+
// AST-only: visit each `CallExpression`. Match a callee of the shape
|
|
9
|
+
// `assert.equal` or `assert.notEqual` (a `PropertyAccessExpression`
|
|
10
|
+
// whose object identifier is `assert` and method name is one of the
|
|
11
|
+
// loose-equality pair). The diagnostic anchors on the call so editors
|
|
12
|
+
// can offer a strict-variant fix at the same position.
|
|
13
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-assert.md
|
|
14
|
+
package linthost
|
|
15
|
+
|
|
16
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
17
|
+
|
|
18
|
+
type unicornConsistentAssert struct{}
|
|
19
|
+
|
|
20
|
+
func (unicornConsistentAssert) Name() string { return "unicorn/consistent-assert" }
|
|
21
|
+
func (unicornConsistentAssert) Visits() []shimast.Kind {
|
|
22
|
+
return []shimast.Kind{shimast.KindCallExpression}
|
|
23
|
+
}
|
|
24
|
+
func (unicornConsistentAssert) Check(ctx *Context, node *shimast.Node) {
|
|
25
|
+
call := node.AsCallExpression()
|
|
26
|
+
if call == nil || call.Expression == nil ||
|
|
27
|
+
call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
access := call.Expression.AsPropertyAccessExpression()
|
|
31
|
+
if access == nil || access.Expression == nil {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if identifierText(access.Expression) != "assert" {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
switch identifierText(access.Name()) {
|
|
38
|
+
case "equal", "notEqual":
|
|
39
|
+
default:
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
ctx.Report(node, "Use the strict variant — `assert.strictEqual` / `assert.notStrictEqual`.")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func init() {
|
|
46
|
+
Register(unicornConsistentAssert{})
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// unicorn/consistent-date-clone: the `Date` constructor copies a date
|
|
2
|
+
// directly when handed another `Date` instance, so the common
|
|
3
|
+
// `new Date(other.getTime())` shape adds a redundant `getTime()` hop
|
|
4
|
+
// that yields the same instant. The shorter `new Date(other)` form is
|
|
5
|
+
// the canonical clone.
|
|
6
|
+
//
|
|
7
|
+
// AST-only: visit each `NewExpression` whose callee identifier is
|
|
8
|
+
// `Date` and whose single argument is `<receiver>.getTime()` with no
|
|
9
|
+
// arguments. The receiver itself is opaque — only the call shape
|
|
10
|
+
// matters because any `Date`-valued expression yields the same clone
|
|
11
|
+
// when passed directly. The diagnostic anchors on the new-expression.
|
|
12
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-date-clone.md
|
|
13
|
+
package linthost
|
|
14
|
+
|
|
15
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
16
|
+
|
|
17
|
+
type unicornConsistentDateClone struct{}
|
|
18
|
+
|
|
19
|
+
func (unicornConsistentDateClone) Name() string {
|
|
20
|
+
return "unicorn/consistent-date-clone"
|
|
21
|
+
}
|
|
22
|
+
func (unicornConsistentDateClone) Visits() []shimast.Kind {
|
|
23
|
+
return []shimast.Kind{shimast.KindNewExpression}
|
|
24
|
+
}
|
|
25
|
+
func (unicornConsistentDateClone) Check(ctx *Context, node *shimast.Node) {
|
|
26
|
+
ne := node.AsNewExpression()
|
|
27
|
+
if ne == nil || identifierText(ne.Expression) != "Date" {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
if ne.Arguments == nil || len(ne.Arguments.Nodes) != 1 {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
arg := stripParens(ne.Arguments.Nodes[0])
|
|
34
|
+
if arg == nil || arg.Kind != shimast.KindCallExpression {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
call := arg.AsCallExpression()
|
|
38
|
+
if call == nil || call.Expression == nil ||
|
|
39
|
+
call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
access := call.Expression.AsPropertyAccessExpression()
|
|
43
|
+
if access == nil || identifierText(access.Name()) != "getTime" {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
if call.Arguments != nil && len(call.Arguments.Nodes) != 0 {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
ctx.Report(node, "Pass the `Date` directly to `new Date(...)` — `getTime()` is redundant.")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func init() {
|
|
53
|
+
Register(unicornConsistentDateClone{})
|
|
54
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// MVP: registers the rule so the typed-key parity test passes; the
|
|
2
|
+
// upstream behavior needs scope analysis (track each destructured
|
|
3
|
+
// binding through subsequent member-access sites in the same scope),
|
|
4
|
+
// which is out of scope for this AST-only port.
|
|
5
|
+
//
|
|
6
|
+
// unicorn/consistent-destructuring:
|
|
7
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-destructuring.md
|
|
8
|
+
package linthost
|
|
9
|
+
|
|
10
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
11
|
+
|
|
12
|
+
type unicornConsistentDestructuring struct{}
|
|
13
|
+
|
|
14
|
+
func (unicornConsistentDestructuring) Name() string {
|
|
15
|
+
return "unicorn/consistent-destructuring"
|
|
16
|
+
}
|
|
17
|
+
func (unicornConsistentDestructuring) Visits() []shimast.Kind {
|
|
18
|
+
return []shimast.Kind{shimast.KindSourceFile}
|
|
19
|
+
}
|
|
20
|
+
func (unicornConsistentDestructuring) Check(ctx *Context, node *shimast.Node) {
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func init() {
|
|
24
|
+
Register(unicornConsistentDestructuring{})
|
|
25
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// unicorn/consistent-empty-array-spread: when an array literal contains
|
|
2
|
+
// a spread of a ternary, both branches should be arrays so the runtime
|
|
3
|
+
// shape of the parent literal stays uniform. The lopsided form
|
|
4
|
+
// `[1, ...(cond ? [x] : 2)]` mixes "spread an array" and "spread a
|
|
5
|
+
// non-array" — only one branch yields the structural shape the reader
|
|
6
|
+
// expects.
|
|
7
|
+
//
|
|
8
|
+
// AST-only: visit each `ArrayLiteralExpression`. For every element
|
|
9
|
+
// that is a `SpreadElement` wrapping a `ConditionalExpression`, fire
|
|
10
|
+
// when exactly one of the two branches is itself an array literal
|
|
11
|
+
// (the other branch is anything else). The diagnostic anchors on the
|
|
12
|
+
// spread so the editor highlights both the `...` and the ternary.
|
|
13
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-empty-array-spread.md
|
|
14
|
+
package linthost
|
|
15
|
+
|
|
16
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
17
|
+
|
|
18
|
+
type unicornConsistentEmptyArraySpread struct{}
|
|
19
|
+
|
|
20
|
+
func (unicornConsistentEmptyArraySpread) Name() string {
|
|
21
|
+
return "unicorn/consistent-empty-array-spread"
|
|
22
|
+
}
|
|
23
|
+
func (unicornConsistentEmptyArraySpread) Visits() []shimast.Kind {
|
|
24
|
+
return []shimast.Kind{shimast.KindArrayLiteralExpression}
|
|
25
|
+
}
|
|
26
|
+
func (unicornConsistentEmptyArraySpread) Check(ctx *Context, node *shimast.Node) {
|
|
27
|
+
arr := node.AsArrayLiteralExpression()
|
|
28
|
+
if arr == nil || arr.Elements == nil {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
for _, el := range arr.Elements.Nodes {
|
|
32
|
+
if el == nil || el.Kind != shimast.KindSpreadElement {
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
spread := el.AsSpreadElement()
|
|
36
|
+
if spread == nil {
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
inner := stripParens(spread.Expression)
|
|
40
|
+
if inner == nil || inner.Kind != shimast.KindConditionalExpression {
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
cond := inner.AsConditionalExpression()
|
|
44
|
+
if cond == nil {
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
whenTrue := stripParens(cond.WhenTrue)
|
|
48
|
+
whenFalse := stripParens(cond.WhenFalse)
|
|
49
|
+
if whenTrue == nil || whenFalse == nil {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
trueIsArr := whenTrue.Kind == shimast.KindArrayLiteralExpression
|
|
53
|
+
falseIsArr := whenFalse.Kind == shimast.KindArrayLiteralExpression
|
|
54
|
+
if trueIsArr != falseIsArr {
|
|
55
|
+
ctx.Report(el, "When spreading a ternary in an array literal, both branches should be arrays.")
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func init() {
|
|
61
|
+
Register(unicornConsistentEmptyArraySpread{})
|
|
62
|
+
}
|