@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,967 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"regexp"
|
|
5
|
+
"strings"
|
|
6
|
+
|
|
7
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
type functionalParameters struct{}
|
|
11
|
+
type functionalImmutableData struct{}
|
|
12
|
+
type functionalNoClassInheritance struct{}
|
|
13
|
+
type functionalNoClasses struct{}
|
|
14
|
+
type functionalNoConditionalStatements struct{}
|
|
15
|
+
type functionalNoExpressionStatements struct{}
|
|
16
|
+
type functionalNoLet struct{}
|
|
17
|
+
type functionalNoLoopStatements struct{}
|
|
18
|
+
type functionalNoMixedTypes struct{}
|
|
19
|
+
type functionalNoPromiseReject struct{}
|
|
20
|
+
type functionalNoReturnVoid struct{}
|
|
21
|
+
type functionalNoThisExpressions struct{}
|
|
22
|
+
type functionalNoThrowStatements struct{}
|
|
23
|
+
type functionalNoTryStatements struct{}
|
|
24
|
+
type functionalPreferImmutableTypes struct{}
|
|
25
|
+
type functionalPreferPropertySignatures struct{}
|
|
26
|
+
type functionalPreferReadonlyType struct{}
|
|
27
|
+
type functionalPreferTacit struct{}
|
|
28
|
+
type functionalReadonlyType struct{}
|
|
29
|
+
type functionalTypeDeclarationImmutability struct{}
|
|
30
|
+
|
|
31
|
+
func (functionalParameters) Name() string { return "functional/functional-parameters" }
|
|
32
|
+
func (functionalImmutableData) Name() string { return "functional/immutable-data" }
|
|
33
|
+
func (functionalNoClassInheritance) Name() string { return "functional/no-class-inheritance" }
|
|
34
|
+
func (functionalNoClasses) Name() string { return "functional/no-classes" }
|
|
35
|
+
func (functionalNoConditionalStatements) Name() string { return "functional/no-conditional-statements" }
|
|
36
|
+
func (functionalNoExpressionStatements) Name() string { return "functional/no-expression-statements" }
|
|
37
|
+
func (functionalNoLet) Name() string { return "functional/no-let" }
|
|
38
|
+
func (functionalNoLoopStatements) Name() string { return "functional/no-loop-statements" }
|
|
39
|
+
func (functionalNoMixedTypes) Name() string { return "functional/no-mixed-types" }
|
|
40
|
+
func (functionalNoPromiseReject) Name() string { return "functional/no-promise-reject" }
|
|
41
|
+
func (functionalNoReturnVoid) Name() string { return "functional/no-return-void" }
|
|
42
|
+
func (functionalNoThisExpressions) Name() string { return "functional/no-this-expressions" }
|
|
43
|
+
func (functionalNoThrowStatements) Name() string { return "functional/no-throw-statements" }
|
|
44
|
+
func (functionalNoTryStatements) Name() string { return "functional/no-try-statements" }
|
|
45
|
+
func (functionalPreferImmutableTypes) Name() string { return "functional/prefer-immutable-types" }
|
|
46
|
+
func (functionalPreferPropertySignatures) Name() string {
|
|
47
|
+
return "functional/prefer-property-signatures"
|
|
48
|
+
}
|
|
49
|
+
func (functionalPreferReadonlyType) Name() string { return "functional/prefer-readonly-type" }
|
|
50
|
+
func (functionalPreferTacit) Name() string { return "functional/prefer-tacit" }
|
|
51
|
+
func (functionalReadonlyType) Name() string { return "functional/readonly-type" }
|
|
52
|
+
func (functionalTypeDeclarationImmutability) Name() string {
|
|
53
|
+
return "functional/type-declaration-immutability"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (functionalParameters) Visits() []shimast.Kind {
|
|
57
|
+
return []shimast.Kind{
|
|
58
|
+
shimast.KindParameter,
|
|
59
|
+
shimast.KindIdentifier,
|
|
60
|
+
shimast.KindFunctionDeclaration,
|
|
61
|
+
shimast.KindFunctionExpression,
|
|
62
|
+
shimast.KindArrowFunction,
|
|
63
|
+
shimast.KindMethodDeclaration,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
func (functionalImmutableData) Visits() []shimast.Kind {
|
|
67
|
+
return []shimast.Kind{
|
|
68
|
+
shimast.KindBinaryExpression,
|
|
69
|
+
shimast.KindPrefixUnaryExpression,
|
|
70
|
+
shimast.KindPostfixUnaryExpression,
|
|
71
|
+
shimast.KindDeleteExpression,
|
|
72
|
+
shimast.KindCallExpression,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
func (functionalNoClassInheritance) Visits() []shimast.Kind {
|
|
76
|
+
return []shimast.Kind{shimast.KindClassDeclaration, shimast.KindClassExpression}
|
|
77
|
+
}
|
|
78
|
+
func (functionalNoClasses) Visits() []shimast.Kind {
|
|
79
|
+
return []shimast.Kind{shimast.KindClassDeclaration, shimast.KindClassExpression}
|
|
80
|
+
}
|
|
81
|
+
func (functionalNoConditionalStatements) Visits() []shimast.Kind {
|
|
82
|
+
return []shimast.Kind{shimast.KindIfStatement, shimast.KindSwitchStatement}
|
|
83
|
+
}
|
|
84
|
+
func (functionalNoExpressionStatements) Visits() []shimast.Kind {
|
|
85
|
+
return []shimast.Kind{shimast.KindExpressionStatement}
|
|
86
|
+
}
|
|
87
|
+
func (functionalNoLet) Visits() []shimast.Kind {
|
|
88
|
+
return []shimast.Kind{shimast.KindVariableDeclarationList}
|
|
89
|
+
}
|
|
90
|
+
func (functionalNoLoopStatements) Visits() []shimast.Kind {
|
|
91
|
+
return []shimast.Kind{
|
|
92
|
+
shimast.KindForStatement,
|
|
93
|
+
shimast.KindForInStatement,
|
|
94
|
+
shimast.KindForOfStatement,
|
|
95
|
+
shimast.KindWhileStatement,
|
|
96
|
+
shimast.KindDoStatement,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
func (functionalNoMixedTypes) Visits() []shimast.Kind {
|
|
100
|
+
return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeLiteral}
|
|
101
|
+
}
|
|
102
|
+
func (functionalNoPromiseReject) Visits() []shimast.Kind {
|
|
103
|
+
return []shimast.Kind{shimast.KindCallExpression}
|
|
104
|
+
}
|
|
105
|
+
func (functionalNoReturnVoid) Visits() []shimast.Kind {
|
|
106
|
+
return []shimast.Kind{shimast.KindReturnStatement, shimast.KindFunctionDeclaration, shimast.KindFunctionExpression, shimast.KindArrowFunction, shimast.KindMethodDeclaration}
|
|
107
|
+
}
|
|
108
|
+
func (functionalNoThisExpressions) Visits() []shimast.Kind {
|
|
109
|
+
return []shimast.Kind{shimast.KindThisKeyword}
|
|
110
|
+
}
|
|
111
|
+
func (functionalNoThrowStatements) Visits() []shimast.Kind {
|
|
112
|
+
return []shimast.Kind{shimast.KindThrowStatement}
|
|
113
|
+
}
|
|
114
|
+
func (functionalNoTryStatements) Visits() []shimast.Kind {
|
|
115
|
+
return []shimast.Kind{shimast.KindTryStatement}
|
|
116
|
+
}
|
|
117
|
+
func (functionalPreferImmutableTypes) Visits() []shimast.Kind {
|
|
118
|
+
return []shimast.Kind{shimast.KindVariableDeclaration, shimast.KindParameter, shimast.KindPropertyDeclaration, shimast.KindPropertySignature}
|
|
119
|
+
}
|
|
120
|
+
func (functionalPreferPropertySignatures) Visits() []shimast.Kind {
|
|
121
|
+
return []shimast.Kind{shimast.KindMethodSignature}
|
|
122
|
+
}
|
|
123
|
+
func (functionalPreferReadonlyType) Visits() []shimast.Kind {
|
|
124
|
+
return []shimast.Kind{shimast.KindArrayType, shimast.KindTupleType, shimast.KindTypeReference, shimast.KindPropertySignature}
|
|
125
|
+
}
|
|
126
|
+
func (functionalPreferTacit) Visits() []shimast.Kind {
|
|
127
|
+
return []shimast.Kind{shimast.KindArrowFunction}
|
|
128
|
+
}
|
|
129
|
+
func (functionalReadonlyType) Visits() []shimast.Kind {
|
|
130
|
+
return []shimast.Kind{shimast.KindTypeReference, shimast.KindTypeOperator}
|
|
131
|
+
}
|
|
132
|
+
func (functionalTypeDeclarationImmutability) Visits() []shimast.Kind {
|
|
133
|
+
return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeAliasDeclaration}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
type functionalNoLetOptions struct {
|
|
137
|
+
functionalPatternOptions
|
|
138
|
+
AllowInForLoopInit bool `json:"allowInForLoopInit"`
|
|
139
|
+
AllowInFunctions bool `json:"allowInFunctions"`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
type functionalNoTryOptions struct {
|
|
143
|
+
AllowCatch bool `json:"allowCatch"`
|
|
144
|
+
AllowFinally bool `json:"allowFinally"`
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
type functionalImmutableDataOptions struct {
|
|
148
|
+
functionalPatternOptions
|
|
149
|
+
IgnoreMapsAndSets bool `json:"ignoreMapsAndSets"`
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
type functionalParametersOptions struct {
|
|
153
|
+
functionalPatternOptions
|
|
154
|
+
AllowRestParameter bool `json:"allowRestParameter"`
|
|
155
|
+
AllowArgumentsKeyword bool `json:"allowArgumentsKeyword"`
|
|
156
|
+
EnforceParameterCount interface{} `json:"enforceParameterCount"`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type functionalPreferImmutableTypesOptions struct {
|
|
160
|
+
functionalPatternOptions
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
type functionalPreferReadonlyTypeOptions struct {
|
|
164
|
+
functionalPatternOptions
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
type functionalReadonlyTypeOptions struct {
|
|
168
|
+
Prefer string `json:"prefer"`
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type functionalPatternOptions struct {
|
|
172
|
+
IgnoreIdentifierPattern interface{} `json:"ignoreIdentifierPattern"`
|
|
173
|
+
IgnoreCodePattern interface{} `json:"ignoreCodePattern"`
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
type functionalImmutabilityDeclarationOptions struct {
|
|
177
|
+
functionalPatternOptions
|
|
178
|
+
Rules []functionalImmutabilityRule `json:"rules"`
|
|
179
|
+
IgnoreInterfaces bool `json:"ignoreInterfaces"`
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
type functionalImmutabilityRule struct {
|
|
183
|
+
Identifiers interface{} `json:"identifiers"`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func (functionalParameters) Check(ctx *Context, node *shimast.Node) {
|
|
187
|
+
var opts functionalParametersOptions
|
|
188
|
+
_ = ctx.DecodeOptions(&opts)
|
|
189
|
+
switch node.Kind {
|
|
190
|
+
case shimast.KindParameter:
|
|
191
|
+
param := node.AsParameterDeclaration()
|
|
192
|
+
if param != nil && param.DotDotDotToken != nil && !opts.AllowRestParameter {
|
|
193
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, identifierText(param.Name())) {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
ctx.Report(node, "Unexpected rest parameter. Use a regular parameter of array type instead.")
|
|
197
|
+
}
|
|
198
|
+
case shimast.KindIdentifier:
|
|
199
|
+
if opts.AllowArgumentsKeyword || identifierText(node) != "arguments" || isDeclarationName(node) {
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, "arguments") {
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
ctx.Report(node, "Unexpected use of `arguments`. Use regular function arguments instead.")
|
|
206
|
+
default:
|
|
207
|
+
if opts.EnforceParameterCount == nil || opts.EnforceParameterCount == false {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
if len(node.Parameters()) == 0 {
|
|
211
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, functionalFunctionLikeName(node)) {
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
ctx.Report(node, "Functions must have at least one parameter.")
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
func (functionalImmutableData) Check(ctx *Context, node *shimast.Node) {
|
|
220
|
+
var opts functionalImmutableDataOptions
|
|
221
|
+
_ = ctx.DecodeOptions(&opts)
|
|
222
|
+
switch node.Kind {
|
|
223
|
+
case shimast.KindBinaryExpression:
|
|
224
|
+
expr := node.AsBinaryExpression()
|
|
225
|
+
if expr == nil || expr.OperatorToken == nil || !isAssignmentOperator(expr.OperatorToken.Kind) {
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
if isMemberMutationTarget(expr.Left) {
|
|
229
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, expr.Left, functionalIdentifierTexts(expr.Left)...) {
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
ctx.Report(expr.Left, "Modifying an existing object/array is not allowed.")
|
|
233
|
+
}
|
|
234
|
+
case shimast.KindPrefixUnaryExpression:
|
|
235
|
+
expr := node.AsPrefixUnaryExpression()
|
|
236
|
+
if expr != nil && isUpdateOperator(expr.Operator) && isMemberMutationTarget(expr.Operand) {
|
|
237
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, expr.Operand, functionalIdentifierTexts(expr.Operand)...) {
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
ctx.Report(expr.Operand, "Modifying an existing object/array is not allowed.")
|
|
241
|
+
}
|
|
242
|
+
case shimast.KindPostfixUnaryExpression:
|
|
243
|
+
expr := node.AsPostfixUnaryExpression()
|
|
244
|
+
if expr != nil && isUpdateOperator(expr.Operator) && isMemberMutationTarget(expr.Operand) {
|
|
245
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, expr.Operand, functionalIdentifierTexts(expr.Operand)...) {
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
ctx.Report(expr.Operand, "Modifying an existing object/array is not allowed.")
|
|
249
|
+
}
|
|
250
|
+
case shimast.KindDeleteExpression:
|
|
251
|
+
expr := node.AsDeleteExpression()
|
|
252
|
+
if expr != nil && isMemberMutationTarget(expr.Expression) {
|
|
253
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, expr.Expression, functionalIdentifierTexts(expr.Expression)...) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
ctx.Report(expr.Expression, "Modifying an existing object/array is not allowed.")
|
|
257
|
+
}
|
|
258
|
+
case shimast.KindCallExpression:
|
|
259
|
+
call := node.AsCallExpression()
|
|
260
|
+
if call == nil || call.Expression == nil || call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
access := call.Expression.AsPropertyAccessExpression()
|
|
264
|
+
if access == nil {
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
method := identifierText(access.Name())
|
|
268
|
+
if isMutableArrayMethod(method) || (!opts.IgnoreMapsAndSets && isMutableCollectionMethod(method)) {
|
|
269
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, call.Expression, functionalIdentifierTexts(call.Expression)...) {
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
ctx.Report(call.Expression, "Modifying an existing object/array is not allowed.")
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
func (functionalNoClassInheritance) Check(ctx *Context, node *shimast.Node) {
|
|
278
|
+
if hasModifier(node, shimast.KindAbstractKeyword) {
|
|
279
|
+
ctx.Report(node, "Unexpected abstract class.")
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
if classHasHeritage(node) {
|
|
283
|
+
ctx.Report(node, "Unexpected inheritance, use composition instead.")
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
func (functionalNoClasses) Check(ctx *Context, node *shimast.Node) {
|
|
288
|
+
ctx.Report(node, "Unexpected class, use functions not classes.")
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
func (functionalNoConditionalStatements) Check(ctx *Context, node *shimast.Node) {
|
|
292
|
+
switch node.Kind {
|
|
293
|
+
case shimast.KindIfStatement:
|
|
294
|
+
ctx.Report(node, "Unexpected if, use a conditional expression instead.")
|
|
295
|
+
case shimast.KindSwitchStatement:
|
|
296
|
+
ctx.Report(node, "Unexpected switch, use a conditional expression instead.")
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
func (functionalNoExpressionStatements) Check(ctx *Context, node *shimast.Node) {
|
|
301
|
+
text := strings.TrimSpace(nodeText(ctx.File, node))
|
|
302
|
+
if strings.HasPrefix(text, `"use `) || strings.HasPrefix(text, `'use `) {
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
ctx.Report(node, "Using expressions to cause side effects is not allowed.")
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
func (functionalNoLet) Check(ctx *Context, node *shimast.Node) {
|
|
309
|
+
if !shimast.IsLet(node) {
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
var opts functionalNoLetOptions
|
|
313
|
+
_ = ctx.DecodeOptions(&opts)
|
|
314
|
+
if opts.AllowInForLoopInit && node.Parent != nil && node.Parent.Kind == shimast.KindForStatement {
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
if opts.AllowInFunctions && hasAncestor(node, isFunctionLikeKind) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, functionalVariableDeclarationListNames(node)...) {
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
start := keywordStart(ctx.File, node, "let")
|
|
324
|
+
if start >= 0 {
|
|
325
|
+
ctx.ReportRange(start, start+len("let"), "Unexpected let, use const instead.")
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
ctx.Report(node, "Unexpected let, use const instead.")
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func (functionalNoLoopStatements) Check(ctx *Context, node *shimast.Node) {
|
|
332
|
+
ctx.Report(node, "Unexpected loop, use map or reduce instead.")
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
func (functionalNoMixedTypes) Check(ctx *Context, node *shimast.Node) {
|
|
336
|
+
members := containerMembers(node)
|
|
337
|
+
if len(members) < 2 {
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
first := ""
|
|
341
|
+
for _, member := range members {
|
|
342
|
+
kind := functionalMemberKind(member)
|
|
343
|
+
if kind == "" {
|
|
344
|
+
continue
|
|
345
|
+
}
|
|
346
|
+
if first == "" {
|
|
347
|
+
first = kind
|
|
348
|
+
continue
|
|
349
|
+
}
|
|
350
|
+
if first != kind {
|
|
351
|
+
ctx.Report(member, "Only the same kind of members are allowed in types.")
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
func (functionalNoPromiseReject) Check(ctx *Context, node *shimast.Node) {
|
|
358
|
+
call := node.AsCallExpression()
|
|
359
|
+
if call != nil && isMatchingPropertyAccess(call.Expression, "Promise", "reject") {
|
|
360
|
+
ctx.Report(node, "Unexpected rejection, resolve an error instead.")
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
func (functionalNoReturnVoid) Check(ctx *Context, node *shimast.Node) {
|
|
365
|
+
if node.Kind == shimast.KindReturnStatement {
|
|
366
|
+
ret := node.AsReturnStatement()
|
|
367
|
+
if ret != nil && ret.Expression == nil {
|
|
368
|
+
ctx.Report(node, "Function must return a value.")
|
|
369
|
+
}
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
if functionalReturnTypeText(ctx, node) == "void" {
|
|
373
|
+
ctx.Report(node, "Function must return a value.")
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func (functionalNoThisExpressions) Check(ctx *Context, node *shimast.Node) {
|
|
378
|
+
ctx.Report(node, "Unexpected this, use functions not classes.")
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
func (functionalNoThrowStatements) Check(ctx *Context, node *shimast.Node) {
|
|
382
|
+
ctx.Report(node, "Unexpected throw, throwing exceptions is not functional.")
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
func (functionalNoTryStatements) Check(ctx *Context, node *shimast.Node) {
|
|
386
|
+
var opts functionalNoTryOptions
|
|
387
|
+
_ = ctx.DecodeOptions(&opts)
|
|
388
|
+
try := node.AsTryStatement()
|
|
389
|
+
if try == nil {
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
if try.CatchClause != nil && !opts.AllowCatch {
|
|
393
|
+
ctx.Report(node, "Unexpected try-catch, this pattern is not functional.")
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
if try.FinallyBlock != nil && !opts.AllowFinally {
|
|
397
|
+
ctx.Report(node, "Unexpected try-finally, this pattern is not functional.")
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
func (functionalPreferImmutableTypes) Check(ctx *Context, node *shimast.Node) {
|
|
402
|
+
var opts functionalPreferImmutableTypesOptions
|
|
403
|
+
_ = ctx.DecodeOptions(&opts)
|
|
404
|
+
typeNode := declarationTypeNode(node)
|
|
405
|
+
if typeNode == nil || !functionalMutableType(typeNode) {
|
|
406
|
+
return
|
|
407
|
+
}
|
|
408
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, functionalDeclarationName(node)) ||
|
|
409
|
+
functionalShouldIgnore(ctx, opts.functionalPatternOptions, typeNode) {
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
ctx.Report(typeNode, "Type should be readonly or immutable.")
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
func (functionalPreferPropertySignatures) Check(ctx *Context, node *shimast.Node) {
|
|
416
|
+
ctx.Report(node, "Use a property signature instead of a method signature.")
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
func (functionalPreferReadonlyType) Check(ctx *Context, node *shimast.Node) {
|
|
420
|
+
var opts functionalPreferReadonlyTypeOptions
|
|
421
|
+
_ = ctx.DecodeOptions(&opts)
|
|
422
|
+
switch node.Kind {
|
|
423
|
+
case shimast.KindArrayType:
|
|
424
|
+
if !isReadonlyTypeNode(node) {
|
|
425
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node) {
|
|
426
|
+
return
|
|
427
|
+
}
|
|
428
|
+
ctx.Report(node, "Only readonly arrays allowed.")
|
|
429
|
+
}
|
|
430
|
+
case shimast.KindTupleType:
|
|
431
|
+
if !isReadonlyTypeNode(node) {
|
|
432
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node) {
|
|
433
|
+
return
|
|
434
|
+
}
|
|
435
|
+
ctx.Report(node, "Only readonly tuples allowed.")
|
|
436
|
+
}
|
|
437
|
+
case shimast.KindTypeReference:
|
|
438
|
+
if isMutableTypeReference(node) {
|
|
439
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, functionalIdentifierTexts(node)...) {
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
ctx.Report(node, "Only readonly types allowed.")
|
|
443
|
+
}
|
|
444
|
+
case shimast.KindPropertySignature:
|
|
445
|
+
if !hasModifier(node, shimast.KindReadonlyKeyword) {
|
|
446
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, functionalDeclarationName(node)) {
|
|
447
|
+
return
|
|
448
|
+
}
|
|
449
|
+
ctx.Report(node, "A readonly modifier is required.")
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
func (functionalPreferTacit) Check(ctx *Context, node *shimast.Node) {
|
|
455
|
+
text := compactFunctionalWhitespace(nodeText(ctx.File, node))
|
|
456
|
+
if isTacitWrapperText(text) {
|
|
457
|
+
ctx.Report(node, "Potentially unnecessary function wrapper.")
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
func (functionalReadonlyType) Check(ctx *Context, node *shimast.Node) {
|
|
462
|
+
var opts functionalReadonlyTypeOptions
|
|
463
|
+
_ = ctx.DecodeOptions(&opts)
|
|
464
|
+
prefer := opts.Prefer
|
|
465
|
+
if prefer == "" {
|
|
466
|
+
prefer = "keyword"
|
|
467
|
+
}
|
|
468
|
+
switch node.Kind {
|
|
469
|
+
case shimast.KindTypeReference:
|
|
470
|
+
if prefer == "keyword" && identifierText(node.AsTypeReferenceNode().TypeName) == "ReadonlyArray" {
|
|
471
|
+
ctx.Report(node, "Use the readonly keyword instead of ReadonlyArray<T>.")
|
|
472
|
+
}
|
|
473
|
+
case shimast.KindTypeOperator:
|
|
474
|
+
if prefer == "generic" && strings.HasPrefix(strings.TrimSpace(nodeText(ctx.File, node)), "readonly ") {
|
|
475
|
+
ctx.Report(node, "Use Readonly<T> instead of the readonly keyword.")
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
func (functionalTypeDeclarationImmutability) Check(ctx *Context, node *shimast.Node) {
|
|
481
|
+
var opts functionalImmutabilityDeclarationOptions
|
|
482
|
+
_ = ctx.DecodeOptions(&opts)
|
|
483
|
+
if opts.IgnoreInterfaces && node.Kind == shimast.KindInterfaceDeclaration {
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
name := functionalTypeDeclarationName(node)
|
|
487
|
+
if functionalShouldIgnore(ctx, opts.functionalPatternOptions, node, name) {
|
|
488
|
+
return
|
|
489
|
+
}
|
|
490
|
+
if !functionalDeclarationRuleApplies(opts, name) {
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
if functionalDeclarationIsMutable(node) {
|
|
494
|
+
ctx.Report(node, "Type declaration should only expose readonly members and immutable property types.")
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
func isMemberMutationTarget(node *shimast.Node) bool {
|
|
499
|
+
if node == nil {
|
|
500
|
+
return false
|
|
501
|
+
}
|
|
502
|
+
node = stripParens(node)
|
|
503
|
+
return node != nil && (node.Kind == shimast.KindPropertyAccessExpression || node.Kind == shimast.KindElementAccessExpression)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
func functionalShouldIgnore(ctx *Context, opts functionalPatternOptions, node *shimast.Node, identifiers ...string) bool {
|
|
507
|
+
if functionalPatternOptionMatches(opts.IgnoreCodePattern, strings.TrimSpace(nodeText(ctx.File, node))) {
|
|
508
|
+
return true
|
|
509
|
+
}
|
|
510
|
+
for _, name := range identifiers {
|
|
511
|
+
if functionalPatternOptionMatches(opts.IgnoreIdentifierPattern, name) {
|
|
512
|
+
return true
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return false
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
func isUpdateOperator(kind shimast.Kind) bool {
|
|
519
|
+
return kind == shimast.KindPlusPlusToken || kind == shimast.KindMinusMinusToken
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
func isMutableArrayMethod(name string) bool {
|
|
523
|
+
switch name {
|
|
524
|
+
case "copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift":
|
|
525
|
+
return true
|
|
526
|
+
default:
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
func isMutableCollectionMethod(name string) bool {
|
|
532
|
+
switch name {
|
|
533
|
+
case "add", "clear", "delete", "set":
|
|
534
|
+
return true
|
|
535
|
+
default:
|
|
536
|
+
return false
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
func classHasHeritage(node *shimast.Node) bool {
|
|
541
|
+
switch node.Kind {
|
|
542
|
+
case shimast.KindClassDeclaration:
|
|
543
|
+
decl := node.AsClassDeclaration()
|
|
544
|
+
return decl != nil && decl.HeritageClauses != nil && len(decl.HeritageClauses.Nodes) > 0
|
|
545
|
+
case shimast.KindClassExpression:
|
|
546
|
+
decl := node.AsClassExpression()
|
|
547
|
+
return decl != nil && decl.HeritageClauses != nil && len(decl.HeritageClauses.Nodes) > 0
|
|
548
|
+
default:
|
|
549
|
+
return false
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
func hasAncestor(node *shimast.Node, pred func(*shimast.Node) bool) bool {
|
|
554
|
+
for cur := node.Parent; cur != nil; cur = cur.Parent {
|
|
555
|
+
if pred(cur) {
|
|
556
|
+
return true
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return false
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
func functionalMemberKind(node *shimast.Node) string {
|
|
563
|
+
if node == nil {
|
|
564
|
+
return ""
|
|
565
|
+
}
|
|
566
|
+
switch node.Kind {
|
|
567
|
+
case shimast.KindPropertySignature, shimast.KindPropertyDeclaration:
|
|
568
|
+
return "property"
|
|
569
|
+
case shimast.KindMethodSignature, shimast.KindMethodDeclaration:
|
|
570
|
+
return "method"
|
|
571
|
+
case shimast.KindCallSignature:
|
|
572
|
+
return "call"
|
|
573
|
+
case shimast.KindIndexSignature:
|
|
574
|
+
return "index"
|
|
575
|
+
default:
|
|
576
|
+
return ""
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
func isDeclarationName(node *shimast.Node) bool {
|
|
581
|
+
if node == nil || node.Parent == nil {
|
|
582
|
+
return false
|
|
583
|
+
}
|
|
584
|
+
parent := node.Parent
|
|
585
|
+
switch parent.Kind {
|
|
586
|
+
case shimast.KindVariableDeclaration:
|
|
587
|
+
decl := parent.AsVariableDeclaration()
|
|
588
|
+
return decl != nil && decl.Name() == node
|
|
589
|
+
case shimast.KindParameter:
|
|
590
|
+
decl := parent.AsParameterDeclaration()
|
|
591
|
+
return decl != nil && decl.Name() == node
|
|
592
|
+
case shimast.KindFunctionDeclaration, shimast.KindClassDeclaration, shimast.KindInterfaceDeclaration, shimast.KindTypeAliasDeclaration:
|
|
593
|
+
return true
|
|
594
|
+
default:
|
|
595
|
+
return false
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
func functionalReturnTypeText(ctx *Context, node *shimast.Node) string {
|
|
600
|
+
var typeNode *shimast.Node
|
|
601
|
+
switch node.Kind {
|
|
602
|
+
case shimast.KindFunctionDeclaration:
|
|
603
|
+
if decl := node.AsFunctionDeclaration(); decl != nil {
|
|
604
|
+
typeNode = decl.Type
|
|
605
|
+
}
|
|
606
|
+
case shimast.KindFunctionExpression:
|
|
607
|
+
if decl := node.AsFunctionExpression(); decl != nil {
|
|
608
|
+
typeNode = decl.Type
|
|
609
|
+
}
|
|
610
|
+
case shimast.KindArrowFunction:
|
|
611
|
+
if decl := node.AsArrowFunction(); decl != nil {
|
|
612
|
+
typeNode = decl.Type
|
|
613
|
+
}
|
|
614
|
+
case shimast.KindMethodDeclaration:
|
|
615
|
+
if decl := node.AsMethodDeclaration(); decl != nil {
|
|
616
|
+
typeNode = decl.Type
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return strings.TrimSpace(nodeText(ctx.File, typeNode))
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
func functionalFunctionLikeName(node *shimast.Node) string {
|
|
623
|
+
if node == nil {
|
|
624
|
+
return ""
|
|
625
|
+
}
|
|
626
|
+
switch node.Kind {
|
|
627
|
+
case shimast.KindFunctionDeclaration:
|
|
628
|
+
if decl := node.AsFunctionDeclaration(); decl != nil {
|
|
629
|
+
return identifierText(decl.Name())
|
|
630
|
+
}
|
|
631
|
+
case shimast.KindMethodDeclaration:
|
|
632
|
+
if decl := node.AsMethodDeclaration(); decl != nil {
|
|
633
|
+
return identifierText(decl.Name())
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return ""
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
func declarationTypeNode(node *shimast.Node) *shimast.Node {
|
|
640
|
+
if node == nil {
|
|
641
|
+
return nil
|
|
642
|
+
}
|
|
643
|
+
switch node.Kind {
|
|
644
|
+
case shimast.KindVariableDeclaration:
|
|
645
|
+
if decl := node.AsVariableDeclaration(); decl != nil {
|
|
646
|
+
return decl.Type
|
|
647
|
+
}
|
|
648
|
+
case shimast.KindParameter:
|
|
649
|
+
if decl := node.AsParameterDeclaration(); decl != nil {
|
|
650
|
+
return decl.Type
|
|
651
|
+
}
|
|
652
|
+
case shimast.KindPropertyDeclaration:
|
|
653
|
+
if decl := node.AsPropertyDeclaration(); decl != nil {
|
|
654
|
+
return decl.Type
|
|
655
|
+
}
|
|
656
|
+
case shimast.KindPropertySignature:
|
|
657
|
+
if decl := node.AsPropertySignatureDeclaration(); decl != nil {
|
|
658
|
+
return decl.Type
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return nil
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
func functionalDeclarationName(node *shimast.Node) string {
|
|
665
|
+
if node == nil {
|
|
666
|
+
return ""
|
|
667
|
+
}
|
|
668
|
+
switch node.Kind {
|
|
669
|
+
case shimast.KindVariableDeclaration:
|
|
670
|
+
if decl := node.AsVariableDeclaration(); decl != nil {
|
|
671
|
+
return identifierText(decl.Name())
|
|
672
|
+
}
|
|
673
|
+
case shimast.KindParameter:
|
|
674
|
+
if decl := node.AsParameterDeclaration(); decl != nil {
|
|
675
|
+
return identifierText(decl.Name())
|
|
676
|
+
}
|
|
677
|
+
case shimast.KindPropertyDeclaration:
|
|
678
|
+
if decl := node.AsPropertyDeclaration(); decl != nil {
|
|
679
|
+
return identifierText(decl.Name())
|
|
680
|
+
}
|
|
681
|
+
case shimast.KindPropertySignature:
|
|
682
|
+
if decl := node.AsPropertySignatureDeclaration(); decl != nil {
|
|
683
|
+
return identifierText(decl.Name())
|
|
684
|
+
}
|
|
685
|
+
case shimast.KindFunctionDeclaration:
|
|
686
|
+
if decl := node.AsFunctionDeclaration(); decl != nil {
|
|
687
|
+
return identifierText(decl.Name())
|
|
688
|
+
}
|
|
689
|
+
case shimast.KindMethodDeclaration:
|
|
690
|
+
if decl := node.AsMethodDeclaration(); decl != nil {
|
|
691
|
+
return identifierText(decl.Name())
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return ""
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
func functionalVariableDeclarationListNames(node *shimast.Node) []string {
|
|
698
|
+
list := node.AsVariableDeclarationList()
|
|
699
|
+
if list == nil || list.Declarations == nil {
|
|
700
|
+
return nil
|
|
701
|
+
}
|
|
702
|
+
names := make([]string, 0, len(list.Declarations.Nodes))
|
|
703
|
+
for _, child := range list.Declarations.Nodes {
|
|
704
|
+
if name := functionalDeclarationName(child); name != "" {
|
|
705
|
+
names = append(names, name)
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return names
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
func functionalIdentifierTexts(node *shimast.Node) []string {
|
|
712
|
+
var names []string
|
|
713
|
+
walkDescendants(node, func(child *shimast.Node) {
|
|
714
|
+
if name := identifierText(child); name != "" {
|
|
715
|
+
names = append(names, name)
|
|
716
|
+
}
|
|
717
|
+
})
|
|
718
|
+
return names
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
func functionalMutableType(node *shimast.Node) bool {
|
|
722
|
+
if node == nil || isReadonlyTypeNode(node) {
|
|
723
|
+
return false
|
|
724
|
+
}
|
|
725
|
+
switch node.Kind {
|
|
726
|
+
case shimast.KindArrayType, shimast.KindTupleType:
|
|
727
|
+
return true
|
|
728
|
+
case shimast.KindTypeReference:
|
|
729
|
+
return isMutableTypeReference(node)
|
|
730
|
+
case shimast.KindTypeLiteral:
|
|
731
|
+
return functionalTypeLiteralIsMutable(node)
|
|
732
|
+
default:
|
|
733
|
+
found := false
|
|
734
|
+
walkDescendants(node, func(child *shimast.Node) {
|
|
735
|
+
if child != node && functionalMutableType(child) {
|
|
736
|
+
found = true
|
|
737
|
+
}
|
|
738
|
+
})
|
|
739
|
+
return found
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
func isReadonlyTypeNode(node *shimast.Node) bool {
|
|
744
|
+
if node == nil {
|
|
745
|
+
return false
|
|
746
|
+
}
|
|
747
|
+
return node.Kind == shimast.KindTypeOperator ||
|
|
748
|
+
(node.Parent != nil && node.Parent.Kind == shimast.KindTypeOperator &&
|
|
749
|
+
strings.HasPrefix(strings.TrimSpace(nodeText(nilSafeFile(node), node.Parent)), "readonly "))
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
func nilSafeFile(node *shimast.Node) *shimast.SourceFile {
|
|
753
|
+
for cur := node; cur != nil; cur = cur.Parent {
|
|
754
|
+
if cur.Kind == shimast.KindSourceFile {
|
|
755
|
+
return cur.AsSourceFile()
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return nil
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
func isMutableTypeReference(node *shimast.Node) bool {
|
|
762
|
+
ref := node.AsTypeReferenceNode()
|
|
763
|
+
if ref == nil || ref.TypeName == nil {
|
|
764
|
+
return false
|
|
765
|
+
}
|
|
766
|
+
switch identifierText(ref.TypeName) {
|
|
767
|
+
case "Array", "Map", "Set", "WeakMap", "WeakSet":
|
|
768
|
+
return true
|
|
769
|
+
default:
|
|
770
|
+
return false
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
func functionalTypeLiteralIsMutable(node *shimast.Node) bool {
|
|
775
|
+
lit := node.AsTypeLiteralNode()
|
|
776
|
+
if lit == nil || lit.Members == nil {
|
|
777
|
+
return false
|
|
778
|
+
}
|
|
779
|
+
for _, member := range lit.Members.Nodes {
|
|
780
|
+
if member == nil {
|
|
781
|
+
continue
|
|
782
|
+
}
|
|
783
|
+
if member.Kind == shimast.KindPropertySignature && !hasModifier(member, shimast.KindReadonlyKeyword) {
|
|
784
|
+
return true
|
|
785
|
+
}
|
|
786
|
+
if functionalMutableType(declarationTypeNode(member)) {
|
|
787
|
+
return true
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return false
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
func compactFunctionalWhitespace(text string) string {
|
|
794
|
+
text = strings.TrimSpace(text)
|
|
795
|
+
text = strings.Join(strings.Fields(text), "")
|
|
796
|
+
return strings.TrimSuffix(text, ";")
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
func isTacitWrapperText(text string) bool {
|
|
800
|
+
parts := strings.Split(text, "=>")
|
|
801
|
+
if len(parts) != 2 {
|
|
802
|
+
return false
|
|
803
|
+
}
|
|
804
|
+
param := tacitWrapperParameterName(strings.Trim(parts[0], "()"))
|
|
805
|
+
if param == "" {
|
|
806
|
+
return false
|
|
807
|
+
}
|
|
808
|
+
call := parts[1]
|
|
809
|
+
if !strings.HasSuffix(call, ")") {
|
|
810
|
+
return false
|
|
811
|
+
}
|
|
812
|
+
open := strings.LastIndex(call, "(")
|
|
813
|
+
if open <= 0 {
|
|
814
|
+
return false
|
|
815
|
+
}
|
|
816
|
+
callee := call[:open]
|
|
817
|
+
arg := strings.TrimSuffix(call[open+1:], ")")
|
|
818
|
+
return arg == param && isFunctionalMemberChain(callee)
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// tacitWrapperParameterName returns the bare identifier of a single
|
|
822
|
+
// arrow-function parameter, peeling off a `: Type` annotation and an
|
|
823
|
+
// optional `?` so the rule still matches typed wrappers like
|
|
824
|
+
// `(value: number) => transform(value)`. Returns "" when the parameter
|
|
825
|
+
// list is empty, contains a destructured pattern, or has more than one
|
|
826
|
+
// parameter (a multi-arg list is split by `=>` further upstream).
|
|
827
|
+
func tacitWrapperParameterName(param string) string {
|
|
828
|
+
if param == "" {
|
|
829
|
+
return ""
|
|
830
|
+
}
|
|
831
|
+
// Strip default value: `x = 0`. The default would re-evaluate
|
|
832
|
+
// inside the wrapper, so the tacit form would lose it — bail.
|
|
833
|
+
if strings.ContainsRune(param, '=') {
|
|
834
|
+
return ""
|
|
835
|
+
}
|
|
836
|
+
// Strip optional marker and type annotation.
|
|
837
|
+
if i := strings.IndexRune(param, ':'); i >= 0 {
|
|
838
|
+
param = param[:i]
|
|
839
|
+
}
|
|
840
|
+
param = strings.TrimSuffix(param, "?")
|
|
841
|
+
param = strings.TrimSpace(param)
|
|
842
|
+
if !isFunctionalIdentifier(param) {
|
|
843
|
+
return ""
|
|
844
|
+
}
|
|
845
|
+
return param
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
func isFunctionalMemberChain(text string) bool {
|
|
849
|
+
parts := strings.Split(text, ".")
|
|
850
|
+
for _, part := range parts {
|
|
851
|
+
if !isFunctionalIdentifier(part) {
|
|
852
|
+
return false
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return true
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
func isFunctionalIdentifier(text string) bool {
|
|
859
|
+
if text == "" {
|
|
860
|
+
return false
|
|
861
|
+
}
|
|
862
|
+
for i, r := range text {
|
|
863
|
+
if i == 0 {
|
|
864
|
+
if r == '_' || r == '$' || ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') {
|
|
865
|
+
continue
|
|
866
|
+
}
|
|
867
|
+
return false
|
|
868
|
+
}
|
|
869
|
+
if r == '_' || r == '$' || ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') {
|
|
870
|
+
continue
|
|
871
|
+
}
|
|
872
|
+
return false
|
|
873
|
+
}
|
|
874
|
+
return true
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
func functionalTypeDeclarationName(node *shimast.Node) string {
|
|
878
|
+
switch node.Kind {
|
|
879
|
+
case shimast.KindInterfaceDeclaration:
|
|
880
|
+
if decl := node.AsInterfaceDeclaration(); decl != nil {
|
|
881
|
+
return identifierText(decl.Name())
|
|
882
|
+
}
|
|
883
|
+
case shimast.KindTypeAliasDeclaration:
|
|
884
|
+
if decl := node.AsTypeAliasDeclaration(); decl != nil {
|
|
885
|
+
return identifierText(decl.Name())
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return ""
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
func functionalDeclarationRuleApplies(opts functionalImmutabilityDeclarationOptions, name string) bool {
|
|
892
|
+
if len(opts.Rules) == 0 {
|
|
893
|
+
return true
|
|
894
|
+
}
|
|
895
|
+
for _, rule := range opts.Rules {
|
|
896
|
+
if functionalPatternOptionMatches(rule.Identifiers, name) {
|
|
897
|
+
return true
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return false
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
func functionalPatternOptionMatches(raw interface{}, text string) bool {
|
|
904
|
+
switch value := raw.(type) {
|
|
905
|
+
case string:
|
|
906
|
+
return functionalPatternMatches(value, text)
|
|
907
|
+
case []interface{}:
|
|
908
|
+
for _, item := range value {
|
|
909
|
+
if pattern, ok := item.(string); ok && functionalPatternMatches(pattern, text) {
|
|
910
|
+
return true
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return false
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
func functionalPatternMatches(pattern, name string) bool {
|
|
918
|
+
if pattern == "" || name == "" {
|
|
919
|
+
return false
|
|
920
|
+
}
|
|
921
|
+
if pattern == name {
|
|
922
|
+
return true
|
|
923
|
+
}
|
|
924
|
+
re, err := regexp.Compile(pattern)
|
|
925
|
+
return err == nil && re.MatchString(name)
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
func functionalDeclarationIsMutable(node *shimast.Node) bool {
|
|
929
|
+
switch node.Kind {
|
|
930
|
+
case shimast.KindInterfaceDeclaration:
|
|
931
|
+
for _, member := range containerMembers(node) {
|
|
932
|
+
if member.Kind == shimast.KindPropertySignature && !hasModifier(member, shimast.KindReadonlyKeyword) {
|
|
933
|
+
return true
|
|
934
|
+
}
|
|
935
|
+
if functionalMutableType(declarationTypeNode(member)) {
|
|
936
|
+
return true
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
case shimast.KindTypeAliasDeclaration:
|
|
940
|
+
decl := node.AsTypeAliasDeclaration()
|
|
941
|
+
return decl != nil && functionalMutableType(decl.Type)
|
|
942
|
+
}
|
|
943
|
+
return false
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
func init() {
|
|
947
|
+
Register(functionalParameters{})
|
|
948
|
+
Register(functionalImmutableData{})
|
|
949
|
+
Register(functionalNoClassInheritance{})
|
|
950
|
+
Register(functionalNoClasses{})
|
|
951
|
+
Register(functionalNoConditionalStatements{})
|
|
952
|
+
Register(functionalNoExpressionStatements{})
|
|
953
|
+
Register(functionalNoLet{})
|
|
954
|
+
Register(functionalNoLoopStatements{})
|
|
955
|
+
Register(functionalNoMixedTypes{})
|
|
956
|
+
Register(functionalNoPromiseReject{})
|
|
957
|
+
Register(functionalNoReturnVoid{})
|
|
958
|
+
Register(functionalNoThisExpressions{})
|
|
959
|
+
Register(functionalNoThrowStatements{})
|
|
960
|
+
Register(functionalNoTryStatements{})
|
|
961
|
+
Register(functionalPreferImmutableTypes{})
|
|
962
|
+
Register(functionalPreferPropertySignatures{})
|
|
963
|
+
Register(functionalPreferReadonlyType{})
|
|
964
|
+
Register(functionalPreferTacit{})
|
|
965
|
+
Register(functionalReadonlyType{})
|
|
966
|
+
Register(functionalTypeDeclarationImmutability{})
|
|
967
|
+
}
|