@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
package/linthost/directives.go
CHANGED
|
@@ -34,8 +34,9 @@ const (
|
|
|
34
34
|
|
|
35
35
|
// lintDirective is the parsed representation of one directive comment.
|
|
36
36
|
type lintDirective struct {
|
|
37
|
-
kind
|
|
38
|
-
rules
|
|
37
|
+
kind lintDirectiveKind
|
|
38
|
+
rules lintDirectiveRules
|
|
39
|
+
ruleList []string
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// lintDirectiveRules holds the rule scope of a directive. When `all` is
|
|
@@ -55,13 +56,22 @@ type lintDirectiveEvent struct {
|
|
|
55
56
|
on bool
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
// lintDirectiveRecord preserves the per-directive rule list so the engine
|
|
60
|
+
// can surface unknown rule names referenced by `// eslint-disable*` comments
|
|
61
|
+
// (see `Engine.collectUnknownDirectiveRules`). The suppression path itself
|
|
62
|
+
// only needs `rules`; this record exists purely for that diagnostic pass.
|
|
63
|
+
type lintDirectiveRecord struct {
|
|
64
|
+
ruleList []string
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
// lintInlineDirectives accumulates the per-file directive information
|
|
59
68
|
// extracted by parseLintInlineDirectives. `lines` maps a zero-based line
|
|
60
69
|
// number to any disable-line / disable-next-line directives on that line.
|
|
61
70
|
// `events` is the ordered list of range-style disable/enable transitions.
|
|
62
71
|
type lintInlineDirectives struct {
|
|
63
|
-
lines
|
|
64
|
-
events
|
|
72
|
+
lines map[int][]lintDirectiveRules
|
|
73
|
+
events []lintDirectiveEvent
|
|
74
|
+
records []lintDirectiveRecord
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
// lintDisableState tracks the cumulative suppress/allow state as
|
|
@@ -75,11 +85,14 @@ type lintDisableState struct {
|
|
|
75
85
|
enabledInAll map[string]struct{}
|
|
76
86
|
}
|
|
77
87
|
|
|
78
|
-
|
|
88
|
+
// filterInlineDisabledFindingsWithDirectives drops findings suppressed by
|
|
89
|
+
// the directive set passed in. The caller is expected to have parsed
|
|
90
|
+
// directives once via parseLintInlineDirectives and reuse them for any
|
|
91
|
+
// other passes (e.g., Engine.collectUnknownDirectiveRules).
|
|
92
|
+
func filterInlineDisabledFindingsWithDirectives(file *shimast.SourceFile, findings []*Finding, directives *lintInlineDirectives) []*Finding {
|
|
79
93
|
if len(findings) == 0 || file == nil {
|
|
80
94
|
return findings
|
|
81
95
|
}
|
|
82
|
-
directives := parseLintInlineDirectives(file)
|
|
83
96
|
if directives.empty() {
|
|
84
97
|
return findings
|
|
85
98
|
}
|
|
@@ -111,8 +124,19 @@ func parseLintInlineDirectives(file *shimast.SourceFile) *lintInlineDirectives {
|
|
|
111
124
|
directives := &lintInlineDirectives{
|
|
112
125
|
lines: make(map[int][]lintDirectiveRules),
|
|
113
126
|
}
|
|
127
|
+
// Cheap byte-scan short-circuit: a file with no `-disable` or
|
|
128
|
+
// `-enable` substring cannot contain any recognized directive
|
|
129
|
+
// (`eslint-disable`, `lint-disable`, `eslint-enable`, `lint-enable`
|
|
130
|
+
// all match one of these). Running the full TypeScript scanner over
|
|
131
|
+
// every file just to find comments accounts for ~15 % of the lint
|
|
132
|
+
// engine's CPU on directive-free codebases; the substring check
|
|
133
|
+
// turns that into ~50 ns regardless of file size.
|
|
134
|
+
text := file.Text()
|
|
135
|
+
if !strings.Contains(text, "-disable") && !strings.Contains(text, "-enable") {
|
|
136
|
+
return directives
|
|
137
|
+
}
|
|
114
138
|
scanner := shimscanner.NewScanner()
|
|
115
|
-
scanner.SetText(
|
|
139
|
+
scanner.SetText(text)
|
|
116
140
|
scanner.SetSkipTrivia(false)
|
|
117
141
|
|
|
118
142
|
// templateBraceDepth tracks `{` nesting inside each open template
|
|
@@ -186,6 +210,9 @@ scan:
|
|
|
186
210
|
on: false,
|
|
187
211
|
})
|
|
188
212
|
}
|
|
213
|
+
directives.records = append(directives.records, lintDirectiveRecord{
|
|
214
|
+
ruleList: append([]string(nil), directive.ruleList...),
|
|
215
|
+
})
|
|
189
216
|
}
|
|
190
217
|
return directives
|
|
191
218
|
}
|
|
@@ -193,7 +220,7 @@ scan:
|
|
|
193
220
|
// empty reports whether no directives were found in the file, allowing the
|
|
194
221
|
// caller to skip the filtering step entirely.
|
|
195
222
|
func (d *lintInlineDirectives) empty() bool {
|
|
196
|
-
return d == nil || (len(d.lines) == 0 && len(d.events) == 0)
|
|
223
|
+
return d == nil || (len(d.lines) == 0 && len(d.events) == 0 && len(d.records) == 0)
|
|
197
224
|
}
|
|
198
225
|
|
|
199
226
|
// suppresses reports whether the directive set causes `finding` to be
|
|
@@ -259,8 +286,11 @@ func (s *lintDisableState) apply(event lintDirectiveEvent) {
|
|
|
259
286
|
}
|
|
260
287
|
|
|
261
288
|
// matches reports whether `rule` is currently suppressed given this state.
|
|
262
|
-
// The name is normalized before lookup so
|
|
263
|
-
//
|
|
289
|
+
// The name is normalized before lookup so an optional `eslint/` prefix in
|
|
290
|
+
// the directive does not prevent a match against the canonical rule id.
|
|
291
|
+
// Legacy `@typescript-eslint/` / `react-hooks/` / `tsdoc/syntax` prefixes
|
|
292
|
+
// are NOT normalized — they fall through to the unknown-rule path and
|
|
293
|
+
// surface as `Engine.UnknownRules()` so users see the migration loudly.
|
|
264
294
|
func (s lintDisableState) matches(rule string) bool {
|
|
265
295
|
normalized := normalizeDirectiveRuleName(rule)
|
|
266
296
|
if _, ok := s.rules[normalized]; ok {
|
|
@@ -336,9 +366,11 @@ func parseLintDirectiveLine(text string) (lintDirective, bool) {
|
|
|
336
366
|
if !ok {
|
|
337
367
|
continue
|
|
338
368
|
}
|
|
369
|
+
parsed := parseDirectivePayload(payload)
|
|
339
370
|
return lintDirective{
|
|
340
|
-
kind:
|
|
341
|
-
rules:
|
|
371
|
+
kind: form.kind,
|
|
372
|
+
rules: parsed.rules,
|
|
373
|
+
ruleList: parsed.ruleList,
|
|
342
374
|
}, true
|
|
343
375
|
}
|
|
344
376
|
}
|
|
@@ -363,10 +395,20 @@ func directivePayload(text, marker string) (string, bool) {
|
|
|
363
395
|
// marker) into a lintDirectiveRules value. The `--` separator strips an
|
|
364
396
|
// optional human-readable description. An empty rule list means "all rules".
|
|
365
397
|
func parseDirectiveRules(payload string) lintDirectiveRules {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
398
|
+
return parseDirectivePayload(payload).rules
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
type parsedDirectivePayload struct {
|
|
402
|
+
rules lintDirectiveRules
|
|
403
|
+
ruleList []string
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
func parseDirectivePayload(payload string) parsedDirectivePayload {
|
|
407
|
+
ruleText := stripDirectiveDescription(payload)
|
|
408
|
+
ruleText = strings.ReplaceAll(ruleText, ",", " ")
|
|
409
|
+
fields := strings.Fields(ruleText)
|
|
369
410
|
rules := make(map[string]struct{}, len(fields))
|
|
411
|
+
ruleList := make([]string, 0, len(fields))
|
|
370
412
|
for _, field := range fields {
|
|
371
413
|
if strings.HasPrefix(field, "--") {
|
|
372
414
|
break
|
|
@@ -374,12 +416,16 @@ func parseDirectiveRules(payload string) lintDirectiveRules {
|
|
|
374
416
|
name := normalizeDirectiveRuleName(field)
|
|
375
417
|
if name != "" {
|
|
376
418
|
rules[name] = struct{}{}
|
|
419
|
+
ruleList = append(ruleList, name)
|
|
377
420
|
}
|
|
378
421
|
}
|
|
379
422
|
if len(rules) == 0 {
|
|
380
|
-
return lintDirectiveRules{all: true}
|
|
423
|
+
return parsedDirectivePayload{rules: lintDirectiveRules{all: true}}
|
|
424
|
+
}
|
|
425
|
+
return parsedDirectivePayload{
|
|
426
|
+
rules: lintDirectiveRules{rules: rules},
|
|
427
|
+
ruleList: ruleList,
|
|
381
428
|
}
|
|
382
|
-
return lintDirectiveRules{rules: rules}
|
|
383
429
|
}
|
|
384
430
|
|
|
385
431
|
// stripDirectiveDescription returns the portion of `payload` before the
|
|
@@ -398,15 +444,11 @@ func stripDirectiveDescription(payload string) string {
|
|
|
398
444
|
return strings.TrimSpace(payload[:i])
|
|
399
445
|
}
|
|
400
446
|
}
|
|
401
|
-
return payload
|
|
447
|
+
return strings.TrimSpace(payload)
|
|
402
448
|
}
|
|
403
449
|
|
|
404
|
-
// normalizeDirectiveRuleName strips
|
|
405
|
-
//
|
|
450
|
+
// normalizeDirectiveRuleName strips common ESLint namespace prefixes while
|
|
451
|
+
// preserving the kebab/slash rule IDs diagnostics use.
|
|
406
452
|
func normalizeDirectiveRuleName(name string) string {
|
|
407
|
-
|
|
408
|
-
name = strings.TrimPrefix(name, "@typescript-eslint/")
|
|
409
|
-
name = strings.TrimPrefix(name, "typescript-eslint/")
|
|
410
|
-
name = strings.TrimPrefix(name, "eslint/")
|
|
411
|
-
return name
|
|
453
|
+
return normalizeBuiltinRuleName(name)
|
|
412
454
|
}
|
package/linthost/dispatch.go
CHANGED
|
@@ -23,7 +23,8 @@ var Version = "dev"
|
|
|
23
23
|
// caller should propagate to the OS (`os.Exit`) or the host (`Plugin.Run`).
|
|
24
24
|
//
|
|
25
25
|
// Recognized verbs: `version` / `-v` / `--version`, `check`, `fix`, `format`,
|
|
26
|
-
// `build`, `transform
|
|
26
|
+
// `build`, `transform`, and the `lsp-*` protocol commands consumed by
|
|
27
|
+
// ttscserver. Anything else is a usage error (exit code 2).
|
|
27
28
|
func Main(args []string) int {
|
|
28
29
|
return run(args)
|
|
29
30
|
}
|
|
@@ -33,7 +34,7 @@ func Main(args []string) int {
|
|
|
33
34
|
// the same entry point the CLI uses.
|
|
34
35
|
func run(args []string) int {
|
|
35
36
|
if len(args) == 0 {
|
|
36
|
-
fmt.Fprintln(os.Stderr, "@ttsc/lint: command required (expected check|fix|format|build|transform|version)")
|
|
37
|
+
fmt.Fprintln(os.Stderr, "@ttsc/lint: command required (expected check|fix|format|build|transform|lsp-*|version)")
|
|
37
38
|
return 2
|
|
38
39
|
}
|
|
39
40
|
switch args[0] {
|
|
@@ -41,7 +42,7 @@ func run(args []string) int {
|
|
|
41
42
|
// Don't pay contributor-registration cost for the version banner.
|
|
42
43
|
fmt.Fprintf(os.Stdout, "@ttsc/lint %s\n", Version)
|
|
43
44
|
return 0
|
|
44
|
-
case "check", "fix", "format", "build", "transform":
|
|
45
|
+
case "check", "fix", "format", "build", "transform", "lsp-command-ids", "lsp-code-action-kinds", "lsp-diagnostics", "lsp-code-actions", "lsp-execute-command":
|
|
45
46
|
default:
|
|
46
47
|
fmt.Fprintf(os.Stderr, "@ttsc/lint: unknown command %q\n", args[0])
|
|
47
48
|
return 2
|
|
@@ -60,6 +61,16 @@ func run(args []string) int {
|
|
|
60
61
|
return RunBuild(args[1:])
|
|
61
62
|
case "transform":
|
|
62
63
|
return RunTransform(args[1:])
|
|
64
|
+
case "lsp-command-ids":
|
|
65
|
+
return RunLSPCommandIDs(args[1:])
|
|
66
|
+
case "lsp-code-action-kinds":
|
|
67
|
+
return RunLSPCodeActionKinds(args[1:])
|
|
68
|
+
case "lsp-diagnostics":
|
|
69
|
+
return RunLSPDiagnostics(args[1:])
|
|
70
|
+
case "lsp-code-actions":
|
|
71
|
+
return RunLSPCodeActions(args[1:])
|
|
72
|
+
case "lsp-execute-command":
|
|
73
|
+
return RunLSPExecuteCommand(args[1:])
|
|
63
74
|
}
|
|
64
75
|
return 2
|
|
65
76
|
}
|
package/linthost/engine.go
CHANGED
|
@@ -246,13 +246,22 @@ func AllRuleNames() []string {
|
|
|
246
246
|
|
|
247
247
|
// Engine binds a rule configuration to a Program and walks the AST once
|
|
248
248
|
// per source file, dispatching each visited node to its interested rules.
|
|
249
|
+
//
|
|
250
|
+
// `rules` is a fixed-size slice indexed by `shimast.Kind` value rather
|
|
251
|
+
// than a map. `KindCount` (~350) is small and bounded, and the slice
|
|
252
|
+
// removes a per-node map hash from the hot path — a `walk(node)` over a
|
|
253
|
+
// 50k-node file performs 50k dispatch lookups. The conversion is
|
|
254
|
+
// equivalent in semantics; entries for unused kinds are nil and the
|
|
255
|
+
// per-rule slice still grows by append.
|
|
249
256
|
type Engine struct {
|
|
250
|
-
config
|
|
251
|
-
rules
|
|
252
|
-
enabled
|
|
253
|
-
unknown
|
|
254
|
-
|
|
255
|
-
|
|
257
|
+
config RuleResolver
|
|
258
|
+
rules [][]Rule
|
|
259
|
+
enabled map[string]Severity
|
|
260
|
+
unknown []string
|
|
261
|
+
unknownDirectives map[string]struct{}
|
|
262
|
+
unknownDirectiveMu sync.Mutex
|
|
263
|
+
needsTypeChecker bool
|
|
264
|
+
serial bool
|
|
256
265
|
}
|
|
257
266
|
|
|
258
267
|
// SetSerial forces Engine.Run to walk files one at a time. The host calls
|
|
@@ -290,9 +299,10 @@ func NewEngineWithResolver(config RuleResolver) *Engine {
|
|
|
290
299
|
config = RuleConfig{}
|
|
291
300
|
}
|
|
292
301
|
eng := &Engine{
|
|
293
|
-
config:
|
|
294
|
-
rules:
|
|
295
|
-
enabled:
|
|
302
|
+
config: config,
|
|
303
|
+
rules: make([][]Rule, int(shimast.KindCount)),
|
|
304
|
+
enabled: make(map[string]Severity),
|
|
305
|
+
unknownDirectives: make(map[string]struct{}),
|
|
296
306
|
}
|
|
297
307
|
displaySeverities := config.EnabledRuleConfig()
|
|
298
308
|
for _, name := range config.ActiveRuleNames() {
|
|
@@ -313,16 +323,91 @@ func NewEngineWithResolver(config RuleResolver) *Engine {
|
|
|
313
323
|
continue
|
|
314
324
|
}
|
|
315
325
|
seen[kind] = struct{}{}
|
|
316
|
-
|
|
326
|
+
idx := int(kind)
|
|
327
|
+
if idx < 0 || idx >= len(eng.rules) {
|
|
328
|
+
// Defensive: a contributor returning a Kind beyond the
|
|
329
|
+
// shim's KindCount would otherwise panic on dispatch.
|
|
330
|
+
continue
|
|
331
|
+
}
|
|
332
|
+
eng.rules[idx] = append(eng.rules[idx], rule)
|
|
317
333
|
}
|
|
318
334
|
}
|
|
319
335
|
sort.Strings(eng.unknown)
|
|
320
336
|
return eng
|
|
321
337
|
}
|
|
322
338
|
|
|
323
|
-
// UnknownRules returns the names of rules that appeared in the
|
|
324
|
-
// have no
|
|
325
|
-
|
|
339
|
+
// UnknownRules returns the names of rules that appeared either in the
|
|
340
|
+
// config or in an inline `eslint-disable*` directive but have no
|
|
341
|
+
// registered implementation. Directive-side unknowns are deduped so the
|
|
342
|
+
// same misspelling on every page doesn't flood the warning channel.
|
|
343
|
+
func (e *Engine) UnknownRules() []string {
|
|
344
|
+
if e == nil {
|
|
345
|
+
return nil
|
|
346
|
+
}
|
|
347
|
+
e.unknownDirectiveMu.Lock()
|
|
348
|
+
extras := make([]string, 0, len(e.unknownDirectives))
|
|
349
|
+
for name := range e.unknownDirectives {
|
|
350
|
+
extras = append(extras, name)
|
|
351
|
+
}
|
|
352
|
+
e.unknownDirectiveMu.Unlock()
|
|
353
|
+
if len(extras) == 0 {
|
|
354
|
+
return e.unknown
|
|
355
|
+
}
|
|
356
|
+
seen := make(map[string]struct{}, len(e.unknown)+len(extras))
|
|
357
|
+
out := make([]string, 0, len(e.unknown)+len(extras))
|
|
358
|
+
for _, name := range e.unknown {
|
|
359
|
+
if _, dup := seen[name]; dup {
|
|
360
|
+
continue
|
|
361
|
+
}
|
|
362
|
+
seen[name] = struct{}{}
|
|
363
|
+
out = append(out, name)
|
|
364
|
+
}
|
|
365
|
+
for _, name := range extras {
|
|
366
|
+
if _, dup := seen[name]; dup {
|
|
367
|
+
continue
|
|
368
|
+
}
|
|
369
|
+
seen[name] = struct{}{}
|
|
370
|
+
out = append(out, name)
|
|
371
|
+
}
|
|
372
|
+
sort.Strings(out)
|
|
373
|
+
return out
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// collectUnknownDirectiveRules walks every directive's rule list and
|
|
377
|
+
// records names that don't resolve to a registered rule. Called once
|
|
378
|
+
// per file after `parseLintInlineDirectives`.
|
|
379
|
+
func (e *Engine) collectUnknownDirectiveRules(directives *lintInlineDirectives) {
|
|
380
|
+
if e == nil || directives == nil {
|
|
381
|
+
return
|
|
382
|
+
}
|
|
383
|
+
for _, rec := range directives.records {
|
|
384
|
+
for _, raw := range rec.ruleList {
|
|
385
|
+
name := normalizeDirectiveRuleName(raw)
|
|
386
|
+
if name == "" {
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
if _, ok := registered.rules[name]; ok {
|
|
390
|
+
continue
|
|
391
|
+
}
|
|
392
|
+
e.recordUnknownDirectiveRule(name)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// recordUnknownDirectiveRule remembers a rule name referenced by an
|
|
398
|
+
// `// eslint-disable*` directive that does not resolve to a registered
|
|
399
|
+
// rule. Each unique name is recorded once across the engine run.
|
|
400
|
+
func (e *Engine) recordUnknownDirectiveRule(name string) {
|
|
401
|
+
if e == nil || name == "" {
|
|
402
|
+
return
|
|
403
|
+
}
|
|
404
|
+
e.unknownDirectiveMu.Lock()
|
|
405
|
+
defer e.unknownDirectiveMu.Unlock()
|
|
406
|
+
if e.unknownDirectives == nil {
|
|
407
|
+
e.unknownDirectives = make(map[string]struct{})
|
|
408
|
+
}
|
|
409
|
+
e.unknownDirectives[name] = struct{}{}
|
|
410
|
+
}
|
|
326
411
|
|
|
327
412
|
// NeedsTypeChecker reports whether any active rule requires Context.Checker.
|
|
328
413
|
func (e *Engine) NeedsTypeChecker() bool {
|
|
@@ -333,18 +418,17 @@ func (e *Engine) NeedsTypeChecker() bool {
|
|
|
333
418
|
// tests + introspection.
|
|
334
419
|
func (e *Engine) EnabledRules() map[string]Severity { return e.enabled }
|
|
335
420
|
|
|
336
|
-
// Run walks
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
// order.
|
|
421
|
+
// Run walks the source files supplied by the caller and returns the collected
|
|
422
|
+
// findings. By default files are processed in parallel, bounded by
|
|
423
|
+
// `runtime.NumCPU()`; the engine falls back to a serial walk when
|
|
424
|
+
// SetSerial(true) was called or when a type-aware rule is active. Findings are
|
|
425
|
+
// merged in source-file order so the diagnostic stream is deterministic across
|
|
426
|
+
// runs even when the per-file work happens out of order.
|
|
343
427
|
func (e *Engine) Run(files []*shimast.SourceFile, checker *shimchecker.Checker) []*Finding {
|
|
344
428
|
if e.runsSerial() {
|
|
345
429
|
var findings []*Finding
|
|
346
430
|
for _, file := range files {
|
|
347
|
-
if file == nil
|
|
431
|
+
if file == nil {
|
|
348
432
|
continue
|
|
349
433
|
}
|
|
350
434
|
findings = append(findings, e.runFile(file, checker)...)
|
|
@@ -360,7 +444,7 @@ func (e *Engine) Run(files []*shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
360
444
|
}
|
|
361
445
|
sem := make(chan struct{}, workers)
|
|
362
446
|
for i, file := range files {
|
|
363
|
-
if file == nil
|
|
447
|
+
if file == nil {
|
|
364
448
|
continue
|
|
365
449
|
}
|
|
366
450
|
wg.Add(1)
|
|
@@ -394,6 +478,40 @@ type boundRule struct {
|
|
|
394
478
|
ctx *Context
|
|
395
479
|
}
|
|
396
480
|
|
|
481
|
+
// lintFileWalker drives the per-file AST traversal. The struct exists so
|
|
482
|
+
// the `ForEachChild` callback can be a method value cached in
|
|
483
|
+
// `childCB`. A naive nested-closure walker re-allocates one callback
|
|
484
|
+
// per recursive call (it captures the walking function variable),
|
|
485
|
+
// which on a 50 k-node file is 50 k throwaway closure allocations.
|
|
486
|
+
// Caching the method value reduces that to one allocation per file.
|
|
487
|
+
type lintFileWalker struct {
|
|
488
|
+
byKind [][]boundRule
|
|
489
|
+
collect func(*Finding)
|
|
490
|
+
childCB func(*shimast.Node) bool
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// walk dispatches a single node to every rule that registered for the
|
|
494
|
+
// node's Kind, then recurses into children via the cached childCB.
|
|
495
|
+
func (w *lintFileWalker) walk(node *shimast.Node) {
|
|
496
|
+
if node == nil {
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
if k := int(node.Kind); k >= 0 && k < len(w.byKind) {
|
|
500
|
+
for _, bound := range w.byKind[k] {
|
|
501
|
+
runRuleCheck(bound.rule, bound.ctx, node, w.collect)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
node.ForEachChild(w.childCB)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// visitChild is the cached ForEachChild callback. It is the method
|
|
508
|
+
// value stored in `childCB` so per-recursion closure allocation is
|
|
509
|
+
// avoided.
|
|
510
|
+
func (w *lintFileWalker) visitChild(child *shimast.Node) bool {
|
|
511
|
+
w.walk(child)
|
|
512
|
+
return false
|
|
513
|
+
}
|
|
514
|
+
|
|
397
515
|
// runFile is the per-file driver. The visitor is allocated once per file
|
|
398
516
|
// to keep the per-node hot path branch-free; it visits children
|
|
399
517
|
// post-order so parents see their already-checked subtrees.
|
|
@@ -416,9 +534,12 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
416
534
|
// Context for every (node, rule) pair, which on a large program meant
|
|
417
535
|
// millions of short-lived heap allocations and the GC pressure they
|
|
418
536
|
// carry. Rules never mutate their Context, so reuse is safe.
|
|
419
|
-
byKind := make(
|
|
537
|
+
byKind := make([][]boundRule, len(e.rules))
|
|
420
538
|
ctxByRule := make(map[string]*Context, len(e.enabled))
|
|
421
539
|
for kind, rules := range e.rules {
|
|
540
|
+
if len(rules) == 0 {
|
|
541
|
+
continue
|
|
542
|
+
}
|
|
422
543
|
for _, rule := range rules {
|
|
423
544
|
name := rule.Name()
|
|
424
545
|
ctx, built := ctxByRule[name]
|
|
@@ -445,40 +566,39 @@ func (e *Engine) runFile(file *shimast.SourceFile, checker *shimchecker.Checker)
|
|
|
445
566
|
}
|
|
446
567
|
}
|
|
447
568
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
node.ForEachChild(func(child *shimast.Node) bool {
|
|
457
|
-
walk(child)
|
|
458
|
-
return false // visit every child
|
|
459
|
-
})
|
|
460
|
-
}
|
|
569
|
+
// Use a struct-based walker so the per-node ForEachChild callback is
|
|
570
|
+
// allocated once (stored as `w.childCB`) instead of once per Walk
|
|
571
|
+
// call. Closures that capture a recursive local function escape to
|
|
572
|
+
// the heap on every invocation; converting to a method value with a
|
|
573
|
+
// cached function field removes that allocation from the hot path —
|
|
574
|
+
// ~38 % of pre-Opt-4 CPU was in the inner ForEachChild closure.
|
|
575
|
+
w := &lintFileWalker{byKind: byKind, collect: collect}
|
|
576
|
+
w.childCB = w.visitChild
|
|
461
577
|
|
|
462
578
|
// SourceFile dispatches into its statement list directly; we walk
|
|
463
579
|
// statements explicitly so the file node itself can be inspected by
|
|
464
580
|
// rules (e.g., `ban-ts-comment` reads CommentDirectives off the
|
|
465
581
|
// SourceFile).
|
|
466
|
-
|
|
467
|
-
|
|
582
|
+
if k := int(shimast.KindSourceFile); k >= 0 && k < len(byKind) {
|
|
583
|
+
for _, bound := range byKind[k] {
|
|
584
|
+
runRuleCheck(bound.rule, bound.ctx, file.AsNode(), collect)
|
|
585
|
+
}
|
|
468
586
|
}
|
|
469
587
|
|
|
470
588
|
statements := file.Statements
|
|
471
589
|
if statements != nil {
|
|
472
590
|
for _, stmt := range statements.Nodes {
|
|
473
|
-
walk(stmt)
|
|
591
|
+
w.walk(stmt)
|
|
474
592
|
}
|
|
475
593
|
}
|
|
594
|
+
directives := parseLintInlineDirectives(file)
|
|
595
|
+
e.collectUnknownDirectiveRules(directives)
|
|
476
596
|
// Apply inline-disable filtering even for files with no statement
|
|
477
597
|
// list. A SourceFile-level rule that fires on a `// ttsc-lint-disable`
|
|
478
598
|
// comment must still honor the directive; early-returning before
|
|
479
599
|
// the filter would silently leak those findings into the diagnostic
|
|
480
600
|
// stream.
|
|
481
|
-
return
|
|
601
|
+
return filterInlineDisabledFindingsWithDirectives(file, collected, directives)
|
|
482
602
|
}
|
|
483
603
|
|
|
484
604
|
func hasEnabledFileRules(rules RuleConfig) bool {
|
package/linthost/fix.go
CHANGED
|
@@ -16,8 +16,8 @@ import (
|
|
|
16
16
|
shimdw "github.com/microsoft/typescript-go/shim/diagnosticwriter"
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
// maxFixPasses bounds the native fix cascade. Real-world cascades (
|
|
20
|
-
//
|
|
19
|
+
// maxFixPasses bounds the native fix cascade. Real-world cascades (noVar →
|
|
20
|
+
// preferConst → eqeqeq …) settle in a handful of passes; the cap exists so
|
|
21
21
|
// a buggy rule that re-reports its own edit cannot loop forever.
|
|
22
22
|
const maxFixPasses = 10
|
|
23
23
|
|
package/linthost/flags_gen.go
CHANGED
|
@@ -13,19 +13,21 @@ package linthost
|
|
|
13
13
|
// Generated from packages/ttsc/src/flags/schema.ts. Edit the schema, not
|
|
14
14
|
// this file.
|
|
15
15
|
var LintFlagAllowList = map[string]bool{
|
|
16
|
-
"checkers":
|
|
17
|
-
"cwd":
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
16
|
+
"checkers": true,
|
|
17
|
+
"cwd": true,
|
|
18
|
+
"diagnostics": false,
|
|
19
|
+
"emit": false,
|
|
20
|
+
"extendedDiagnostics": false,
|
|
21
|
+
"file": true,
|
|
22
|
+
"noEmit": false,
|
|
23
|
+
"out": true,
|
|
24
|
+
"outDir": true,
|
|
25
|
+
"p": true,
|
|
26
|
+
"plugins-json": true,
|
|
27
|
+
"project": true,
|
|
28
|
+
"quiet": false,
|
|
29
|
+
"singleThreaded": false,
|
|
30
|
+
"tsconfig": true,
|
|
31
|
+
"tsgo-args": true,
|
|
32
|
+
"verbose": false,
|
|
31
33
|
}
|
package/linthost/host.go
CHANGED
|
@@ -14,6 +14,7 @@ import (
|
|
|
14
14
|
"errors"
|
|
15
15
|
"fmt"
|
|
16
16
|
"path/filepath"
|
|
17
|
+
"strings"
|
|
17
18
|
|
|
18
19
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
19
20
|
"github.com/microsoft/typescript-go/shim/bundled"
|
|
@@ -157,12 +158,19 @@ func (p *program) close() {
|
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
|
|
160
|
-
// userSourceFiles returns the
|
|
161
|
-
//
|
|
161
|
+
// userSourceFiles returns the tsconfig-selected source files the lint engine
|
|
162
|
+
// owns. The tsconfig file list is the boundary: imported libraries, generated
|
|
163
|
+
// output, and JSON modules may still appear in Program.SourceFiles(), but lint
|
|
164
|
+
// and format should not walk them unless the project selected them as TS/JS
|
|
165
|
+
// source roots.
|
|
162
166
|
func (p *program) userSourceFiles() []*shimast.SourceFile {
|
|
167
|
+
roots := p.userSourceFileNames()
|
|
163
168
|
out := make([]*shimast.SourceFile, 0)
|
|
164
169
|
for _, f := range p.tsProgram.SourceFiles() {
|
|
165
|
-
if f == nil
|
|
170
|
+
if f == nil {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
if _, ok := roots[canonicalProjectPath(p.cwd, f.FileName())]; !ok {
|
|
166
174
|
continue
|
|
167
175
|
}
|
|
168
176
|
out = append(out, f)
|
|
@@ -170,6 +178,35 @@ func (p *program) userSourceFiles() []*shimast.SourceFile {
|
|
|
170
178
|
return out
|
|
171
179
|
}
|
|
172
180
|
|
|
181
|
+
func (p *program) userSourceFileNames() map[string]struct{} {
|
|
182
|
+
out := make(map[string]struct{})
|
|
183
|
+
if p == nil || p.parsed == nil || p.parsed.ParsedConfig == nil {
|
|
184
|
+
return out
|
|
185
|
+
}
|
|
186
|
+
for _, fileName := range p.parsed.ParsedConfig.FileNames {
|
|
187
|
+
if isLintSourceFileName(fileName) {
|
|
188
|
+
out[canonicalProjectPath(p.cwd, fileName)] = struct{}{}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return out
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
func canonicalProjectPath(cwd, fileName string) string {
|
|
195
|
+
if !filepath.IsAbs(fileName) {
|
|
196
|
+
fileName = filepath.Join(cwd, fileName)
|
|
197
|
+
}
|
|
198
|
+
return filepath.ToSlash(filepath.Clean(fileName))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
func isLintSourceFileName(fileName string) bool {
|
|
202
|
+
switch strings.ToLower(filepath.Ext(fileName)) {
|
|
203
|
+
case ".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs":
|
|
204
|
+
return true
|
|
205
|
+
default:
|
|
206
|
+
return false
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
173
210
|
// programDiagnostics returns the bind + semantic diagnostics for the
|
|
174
211
|
// loaded program. Same surface tsgo's CLI prints when you run a regular
|
|
175
212
|
// `tsgo --noEmit`.
|