@ttsc/lint 0.13.0 → 0.14.0-dev.20250530.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +955 -85
- package/lib/defaultFormat.d.ts +15 -8
- package/lib/defaultFormat.js +15 -8
- package/lib/defaultFormat.js.map +1 -1
- package/lib/index.d.ts +46 -0
- package/lib/index.js +60 -37
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +12 -6
- package/lib/structures/TtscLintRuleSetting.d.ts +49 -0
- package/lib/structures/{TtscLintRuleOptions.js → TtscLintRuleSetting.js} +1 -1
- package/lib/structures/TtscLintRuleSetting.js.map +1 -0
- package/lib/structures/format/ITtscLintFormat.d.ts +128 -0
- package/lib/structures/{TtscLintRuleMap.js → format/ITtscLintFormat.js} +1 -1
- package/lib/structures/format/ITtscLintFormat.js.map +1 -0
- package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +17 -0
- package/lib/structures/{ITtscLintFormatConfig.js → format/ITtscLintFormatJsDoc.js} +1 -1
- package/lib/structures/format/ITtscLintFormatJsDoc.js.map +1 -0
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +54 -0
- package/lib/structures/format/ITtscLintFormatSortImports.js +3 -0
- package/lib/structures/format/ITtscLintFormatSortImports.js.map +1 -0
- package/lib/structures/format/index.d.ts +3 -0
- package/lib/structures/format/index.js +20 -0
- package/lib/structures/format/index.js.map +1 -0
- package/lib/structures/index.d.ts +3 -4
- package/lib/structures/index.js +3 -4
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +95 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +13 -0
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +75 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.js +3 -0
- package/lib/structures/rules/ITtscLintBoundariesRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +16 -0
- package/lib/structures/rules/ITtscLintContributorRules.js +3 -0
- package/lib/structures/rules/ITtscLintContributorRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +1229 -0
- package/lib/structures/rules/ITtscLintCoreRules.js +3 -0
- package/lib/structures/rules/ITtscLintCoreRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +17 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js +10 -0
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCypressRules.d.ts +125 -0
- package/lib/structures/rules/ITtscLintCypressRules.js +3 -0
- package/lib/structures/rules/ITtscLintCypressRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +154 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +13 -0
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +162 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.js +3 -0
- package/lib/structures/rules/ITtscLintFunctionalRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJestRules.d.ts +190 -0
- package/lib/structures/rules/ITtscLintJestRules.js +3 -0
- package/lib/structures/rules/ITtscLintJestRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJsdocRules.d.ts +133 -0
- package/lib/structures/rules/ITtscLintJsdocRules.js +3 -0
- package/lib/structures/rules/ITtscLintJsdocRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +294 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.js +3 -0
- package/lib/structures/rules/ITtscLintJsxA11yRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintNextjsRules.d.ts +180 -0
- package/lib/structures/rules/ITtscLintNextjsRules.js +3 -0
- package/lib/structures/rules/ITtscLintNextjsRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +312 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.js +3 -0
- package/lib/structures/rules/ITtscLintPlaywrightRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintPromiseRules.d.ts +179 -0
- package/lib/structures/rules/ITtscLintPromiseRules.js +3 -0
- package/lib/structures/rules/ITtscLintPromiseRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +18 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +49 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.js +3 -0
- package/lib/structures/rules/ITtscLintReactPerfRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +37 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js +11 -0
- package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintReactRules.d.ts +276 -0
- package/lib/structures/rules/ITtscLintReactRules.js +3 -0
- package/lib/structures/rules/ITtscLintReactRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +182 -0
- package/lib/structures/rules/ITtscLintRegexpRules.js +3 -0
- package/lib/structures/rules/ITtscLintRegexpRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +65 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.js +3 -0
- package/lib/structures/rules/ITtscLintRuleOptionsMap.js.map +1 -0
- package/lib/structures/rules/ITtscLintRules.d.ts +55 -0
- package/lib/structures/{TtscLintRule.js → rules/ITtscLintRules.js} +1 -1
- package/lib/structures/rules/ITtscLintRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintSecurityRules.d.ts +141 -0
- package/lib/structures/rules/ITtscLintSecurityRules.js +3 -0
- package/lib/structures/rules/ITtscLintSecurityRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +178 -0
- package/lib/structures/rules/ITtscLintSolidRules.js +3 -0
- package/lib/structures/rules/ITtscLintSolidRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +20 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.js +9 -0
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintStorybookRules.d.ts +165 -0
- package/lib/structures/rules/ITtscLintStorybookRules.js +3 -0
- package/lib/structures/rules/ITtscLintStorybookRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +88 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.js +3 -0
- package/lib/structures/rules/ITtscLintTanstackQueryRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +21 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js +10 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +293 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.js +3 -0
- package/lib/structures/rules/ITtscLintTestingLibraryRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +1074 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.js +3 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +997 -0
- package/lib/structures/rules/ITtscLintUnicornRules.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRules.js.map +1 -0
- package/lib/structures/rules/ITtscLintVitestRules.d.ts +133 -0
- package/lib/structures/rules/ITtscLintVitestRules.js +3 -0
- package/lib/structures/rules/ITtscLintVitestRules.js.map +1 -0
- package/lib/structures/rules/index.d.ts +31 -0
- package/lib/structures/rules/index.js +48 -0
- package/lib/structures/rules/index.js.map +1 -0
- package/linthost/ast_helpers.go +30 -5
- package/linthost/compile.go +33 -2
- package/linthost/config.go +118 -71
- package/linthost/config_format.go +270 -104
- package/linthost/directives.go +67 -25
- package/linthost/dispatch.go +14 -3
- package/linthost/engine.go +160 -40
- package/linthost/fix.go +10 -3
- package/linthost/flags_gen.go +17 -15
- package/linthost/format.go +222 -25
- package/linthost/format_editor_settings.go +262 -0
- package/linthost/host.go +40 -3
- package/linthost/lsp.go +898 -0
- package/linthost/print_dispatch.go +5 -2
- package/linthost/print_engine.go +5 -5
- package/linthost/print_nodes_call.go +149 -67
- package/linthost/print_nodes_function.go +27 -2
- package/linthost/print_nodes_list.go +40 -5
- package/linthost/print_nodes_object.go +1 -1
- package/linthost/print_nodes_ternary.go +86 -0
- package/linthost/rule_names.go +14 -0
- package/linthost/rules_arrays.go +2 -2
- package/linthost/rules_boundaries.go +532 -0
- package/linthost/rules_camelcase.go +133 -0
- package/linthost/rules_complexity.go +95 -0
- package/linthost/rules_consistent_return.go +130 -0
- package/linthost/rules_console.go +1 -1
- package/linthost/rules_core_extra.go +739 -0
- package/linthost/rules_curly.go +70 -0
- package/linthost/rules_cypress.go +608 -0
- package/linthost/rules_debugger.go +2 -2
- package/linthost/rules_default_case.go +39 -0
- package/linthost/rules_default_case_last.go +46 -0
- package/linthost/rules_dupes.go +3 -3
- package/linthost/rules_empty.go +3 -3
- package/linthost/rules_escape.go +44 -24
- package/linthost/rules_eval.go +2 -2
- package/linthost/rules_finally.go +2 -2
- package/linthost/rules_format_arrow_parens.go +167 -0
- package/linthost/rules_format_bracket_spacing.go +117 -0
- package/linthost/rules_format_clause_join.go +172 -0
- package/linthost/rules_format_declaration_header.go +595 -0
- package/linthost/rules_format_indent.go +372 -0
- package/linthost/rules_format_jsdoc.go +5 -5
- package/linthost/rules_format_orphan_semi.go +106 -0
- package/linthost/rules_format_parameter_properties.go +175 -0
- package/linthost/rules_format_print_width.go +66 -55
- package/linthost/rules_format_quote_props.go +169 -0
- package/linthost/rules_format_quotes.go +76 -30
- package/linthost/rules_format_semi.go +186 -11
- package/linthost/rules_format_sort_imports.go +437 -118
- package/linthost/rules_format_statement_split.go +343 -0
- package/linthost/rules_format_ternary_nullish_parens.go +77 -0
- package/linthost/rules_format_trailing_comma.go +22 -3
- package/linthost/rules_format_whitespace.go +336 -0
- package/linthost/rules_functional.go +967 -0
- package/linthost/rules_gap.go +24 -45
- package/linthost/rules_grouped_accessor_pairs.go +87 -0
- package/linthost/rules_guard_for_in.go +198 -0
- package/linthost/rules_id_length.go +77 -0
- package/linthost/rules_imports.go +70 -2
- package/linthost/rules_init_declarations.go +53 -0
- package/linthost/rules_jest.go +618 -0
- package/linthost/rules_jsdoc.go +502 -0
- package/linthost/rules_jsx_a11y.go +1264 -0
- package/linthost/rules_logic.go +12 -12
- package/linthost/rules_loops.go +1 -1
- package/linthost/rules_max_classes_per_file.go +58 -0
- package/linthost/rules_max_depth.go +83 -0
- package/linthost/rules_max_lines.go +101 -0
- package/linthost/rules_max_lines_per_function.go +57 -0
- package/linthost/rules_max_nested_callbacks.go +61 -0
- package/linthost/rules_max_params.go +49 -0
- package/linthost/rules_max_statements.go +59 -0
- package/linthost/rules_misc.go +1 -1
- package/linthost/rules_nextjs.go +697 -0
- package/linthost/rules_no_else_return.go +75 -0
- package/linthost/rules_no_empty_named_blocks.go +91 -0
- package/linthost/rules_no_extend_native.go +51 -0
- package/linthost/rules_no_import_assign.go +140 -0
- package/linthost/rules_no_invalid_this.go +52 -0
- package/linthost/rules_no_loop_func.go +85 -0
- package/linthost/rules_no_magic_numbers.go +123 -0
- package/linthost/rules_no_mixed_operators.go +80 -0
- package/linthost/rules_no_param_reassign.go +98 -0
- package/linthost/rules_no_redeclare.go +270 -0
- package/linthost/rules_no_restricted_imports.go +105 -0
- package/linthost/rules_no_restricted_syntax.go +49 -0
- package/linthost/rules_no_shadow.go +147 -0
- package/linthost/rules_no_unreachable.go +77 -0
- package/linthost/rules_no_useless_assignment.go +120 -0
- package/linthost/rules_no_useless_constructor.go +245 -0
- package/linthost/rules_no_useless_return.go +51 -0
- package/linthost/rules_params.go +1 -1
- package/linthost/rules_playwright.go +788 -0
- package/linthost/rules_prefer_arrow_callback.go +124 -0
- package/linthost/rules_prefer_destructuring.go +98 -0
- package/linthost/rules_prefer_named_capture_group.go +129 -0
- package/linthost/rules_prefer_rest_params.go +94 -0
- package/linthost/rules_problems.go +41 -30
- package/linthost/rules_promise.go +1057 -2
- package/linthost/rules_protos.go +2 -2
- package/linthost/rules_react.go +773 -0
- package/linthost/rules_react_extras.go +227 -0
- package/linthost/rules_react_hooks.go +848 -0
- package/linthost/rules_react_perf.go +293 -0
- package/linthost/rules_react_refresh.go +583 -0
- package/linthost/rules_regexp.go +410 -0
- package/linthost/rules_security.go +830 -0
- package/linthost/rules_self.go +2 -2
- package/linthost/rules_solid.go +1073 -0
- package/linthost/rules_sort_imports.go +69 -0
- package/linthost/rules_sort_keys.go +134 -0
- package/linthost/rules_storybook.go +1176 -0
- package/linthost/rules_strings.go +4 -4
- package/linthost/rules_suggestions.go +38 -38
- package/linthost/rules_tanstack_query.go +844 -0
- package/linthost/rules_testing_library.go +1204 -0
- package/linthost/rules_throw.go +1 -1
- package/linthost/rules_ts.go +19 -19
- package/linthost/rules_ts_async.go +399 -0
- package/linthost/rules_ts_class_literal_property_style.go +115 -0
- package/linthost/rules_ts_consistent_type_exports.go +256 -0
- package/linthost/rules_ts_explicit_function_return_type.go +67 -0
- package/linthost/rules_ts_extra.go +159 -36
- package/linthost/rules_ts_no_base_to_string.go +218 -0
- package/linthost/rules_ts_no_confusing_void.go +72 -0
- package/linthost/rules_ts_no_deprecated.go +195 -0
- package/linthost/rules_ts_no_for_in_array.go +84 -0
- package/linthost/rules_ts_no_magic_numbers.go +124 -0
- package/linthost/rules_ts_no_meaningless_void_operator.go +84 -0
- package/linthost/rules_ts_no_misused_spread.go +104 -0
- package/linthost/rules_ts_no_redundant_type_constituents.go +113 -0
- package/linthost/rules_ts_no_restricted_types.go +62 -0
- package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +129 -0
- package/linthost/rules_ts_no_unnecessary_condition.go +250 -0
- package/linthost/rules_ts_no_unnecessary_qualifier.go +118 -0
- package/linthost/rules_ts_no_unnecessary_template_expression.go +178 -0
- package/linthost/rules_ts_no_unnecessary_type_arguments.go +234 -0
- package/linthost/rules_ts_no_unnecessary_type_assertion.go +157 -0
- package/linthost/rules_ts_no_unsafe_argument.go +82 -0
- package/linthost/rules_ts_no_unsafe_assignment.go +102 -0
- package/linthost/rules_ts_no_unsafe_call.go +82 -0
- package/linthost/rules_ts_no_unsafe_enum_comparison.go +235 -0
- package/linthost/rules_ts_no_unsafe_member_access.go +58 -0
- package/linthost/rules_ts_no_unsafe_return.go +126 -0
- package/linthost/rules_ts_no_unsafe_unary_minus.go +81 -0
- package/linthost/rules_ts_no_useless_constructor.go +52 -0
- package/linthost/rules_ts_non_nullable_type_assertion_style.go +108 -0
- package/linthost/rules_ts_parameter_properties.go +38 -0
- package/linthost/rules_ts_prefer_find.go +178 -0
- package/linthost/rules_ts_prefer_includes.go +255 -0
- package/linthost/rules_ts_prefer_nullish_coalescing.go +104 -0
- package/linthost/rules_ts_prefer_optional_chain.go +228 -0
- package/linthost/rules_ts_prefer_promise_reject_errors.go +176 -0
- package/linthost/rules_ts_prefer_readonly.go +73 -0
- package/linthost/rules_ts_prefer_reduce_type_parameter.go +116 -0
- package/linthost/rules_ts_prefer_regexp_exec.go +122 -0
- package/linthost/rules_ts_prefer_return_this_type.go +135 -0
- package/linthost/rules_ts_prefer_string_starts_ends_with.go +360 -0
- package/linthost/rules_ts_promise_function_async.go +77 -0
- package/linthost/rules_ts_related_getter_setter_pairs.go +113 -0
- package/linthost/rules_ts_require_array_sort_compare.go +96 -0
- package/linthost/rules_ts_restrict_plus_operands.go +127 -0
- package/linthost/rules_ts_restrict_template_expressions.go +95 -0
- package/linthost/rules_ts_simple.go +387 -0
- package/linthost/rules_ts_sort_type_constituents.go +144 -0
- package/linthost/rules_ts_strict_boolean_expressions.go +157 -0
- package/linthost/rules_ts_switch_exhaustiveness_check.go +147 -0
- package/linthost/rules_ts_unbound_method.go +126 -0
- package/linthost/rules_tsdoc.go +136 -0
- package/linthost/rules_unicorn_better_regex.go +27 -0
- package/linthost/rules_unicorn_catch_error_name.go +46 -0
- package/linthost/rules_unicorn_consistent_assert.go +47 -0
- package/linthost/rules_unicorn_consistent_date_clone.go +54 -0
- package/linthost/rules_unicorn_consistent_destructuring.go +25 -0
- package/linthost/rules_unicorn_consistent_empty_array_spread.go +62 -0
- package/linthost/rules_unicorn_consistent_existence_index_check.go +80 -0
- package/linthost/rules_unicorn_consistent_function_scoping.go +25 -0
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +25 -0
- package/linthost/rules_unicorn_custom_error_definition.go +138 -0
- package/linthost/rules_unicorn_empty_brace_spaces.go +73 -0
- package/linthost/rules_unicorn_error_message.go +64 -0
- package/linthost/rules_unicorn_escape_case.go +51 -0
- package/linthost/rules_unicorn_expiring_todo_comments.go +78 -0
- package/linthost/rules_unicorn_explicit_length_check.go +88 -0
- package/linthost/rules_unicorn_filename_case.go +29 -0
- package/linthost/rules_unicorn_import_style.go +28 -0
- package/linthost/rules_unicorn_isolated_functions.go +29 -0
- package/linthost/rules_unicorn_new_for_builtins.go +87 -0
- package/linthost/rules_unicorn_no_abusive_eslint_disable.go +63 -0
- package/linthost/rules_unicorn_no_accessor_recursion.go +115 -0
- package/linthost/rules_unicorn_no_anonymous_default_export.go +74 -0
- package/linthost/rules_unicorn_no_array_callback_reference.go +65 -0
- package/linthost/rules_unicorn_no_array_for_each.go +40 -0
- package/linthost/rules_unicorn_no_array_method_this_argument.go +61 -0
- package/linthost/rules_unicorn_no_array_reduce.go +41 -0
- package/linthost/rules_unicorn_no_array_reverse.go +44 -0
- package/linthost/rules_unicorn_no_array_sort.go +43 -0
- package/linthost/rules_unicorn_no_await_expression_member.go +40 -0
- package/linthost/rules_unicorn_no_await_in_promise_methods.go +69 -0
- package/linthost/rules_unicorn_no_console_spaces.go +61 -0
- package/linthost/rules_unicorn_no_document_cookie.go +41 -0
- package/linthost/rules_unicorn_no_empty_file.go +44 -0
- package/linthost/rules_unicorn_no_for_loop.go +102 -0
- package/linthost/rules_unicorn_no_hex_escape.go +42 -0
- package/linthost/rules_unicorn_no_immediate_mutation.go +90 -0
- package/linthost/rules_unicorn_no_instanceof_builtins.go +66 -0
- package/linthost/rules_unicorn_no_invalid_fetch_options.go +79 -0
- package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +51 -0
- package/linthost/rules_unicorn_no_keyword_prefix.go +101 -0
- package/linthost/rules_unicorn_no_lonely_if.go +44 -0
- package/linthost/rules_unicorn_no_magic_array_flat_depth.go +51 -0
- package/linthost/rules_unicorn_no_named_default.go +54 -0
- package/linthost/rules_unicorn_no_negated_condition.go +85 -0
- package/linthost/rules_unicorn_no_negation_in_equality_check.go +50 -0
- package/linthost/rules_unicorn_no_nested_ternary.go +43 -0
- package/linthost/rules_unicorn_no_new_array.go +30 -0
- package/linthost/rules_unicorn_no_new_buffer.go +31 -0
- package/linthost/rules_unicorn_no_null.go +24 -0
- package/linthost/rules_unicorn_no_object_as_default_parameter.go +48 -0
- package/linthost/rules_unicorn_no_process_exit.go +43 -0
- package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +59 -0
- package/linthost/rules_unicorn_no_static_only_class.go +56 -0
- package/linthost/rules_unicorn_no_thenable.go +74 -0
- package/linthost/rules_unicorn_no_this_assignment.go +35 -0
- package/linthost/rules_unicorn_no_typeof_undefined.go +58 -0
- package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +49 -0
- package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +70 -0
- package/linthost/rules_unicorn_no_unnecessary_await.go +51 -0
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +32 -0
- package/linthost/rules_unicorn_no_unnecessary_slice_end.go +48 -0
- package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +115 -0
- package/linthost/rules_unicorn_no_unreadable_iife.go +50 -0
- package/linthost/rules_unicorn_no_unused_properties.go +30 -0
- package/linthost/rules_unicorn_no_useless_collection_argument.go +63 -0
- package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +46 -0
- package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +81 -0
- package/linthost/rules_unicorn_no_useless_iterator_to_array.go +72 -0
- package/linthost/rules_unicorn_no_useless_length_check.go +144 -0
- package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +81 -0
- package/linthost/rules_unicorn_no_useless_spread.go +67 -0
- package/linthost/rules_unicorn_no_useless_switch_case.go +55 -0
- package/linthost/rules_unicorn_no_useless_undefined.go +55 -0
- package/linthost/rules_unicorn_no_zero_fractions.go +52 -0
- package/linthost/rules_unicorn_number_literal_case.go +69 -0
- package/linthost/rules_unicorn_numeric_separators_style.go +75 -0
- package/linthost/rules_unicorn_prefer_add_event_listener.go +66 -0
- package/linthost/rules_unicorn_prefer_array_find.go +51 -0
- package/linthost/rules_unicorn_prefer_array_flat.go +52 -0
- package/linthost/rules_unicorn_prefer_array_flat_map.go +63 -0
- package/linthost/rules_unicorn_prefer_array_index_of.go +147 -0
- package/linthost/rules_unicorn_prefer_array_some.go +67 -0
- package/linthost/rules_unicorn_prefer_at.go +77 -0
- package/linthost/rules_unicorn_prefer_bigint_literals.go +77 -0
- package/linthost/rules_unicorn_prefer_blob_reading_methods.go +44 -0
- package/linthost/rules_unicorn_prefer_class_fields.go +93 -0
- package/linthost/rules_unicorn_prefer_classlist_toggle.go +120 -0
- package/linthost/rules_unicorn_prefer_code_point.go +49 -0
- package/linthost/rules_unicorn_prefer_date_now.go +78 -0
- package/linthost/rules_unicorn_prefer_default_parameters.go +144 -0
- package/linthost/rules_unicorn_prefer_dom_node_append.go +40 -0
- package/linthost/rules_unicorn_prefer_dom_node_dataset.go +55 -0
- package/linthost/rules_unicorn_prefer_dom_node_remove.go +41 -0
- package/linthost/rules_unicorn_prefer_dom_node_text_content.go +37 -0
- package/linthost/rules_unicorn_prefer_event_target.go +36 -0
- package/linthost/rules_unicorn_prefer_export_from.go +129 -0
- package/linthost/rules_unicorn_prefer_global_this.go +127 -0
- package/linthost/rules_unicorn_prefer_import_meta_properties.go +66 -0
- package/linthost/rules_unicorn_prefer_includes.go +149 -0
- package/linthost/rules_unicorn_prefer_json_parse_buffer.go +65 -0
- package/linthost/rules_unicorn_prefer_keyboard_event_key.go +38 -0
- package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +48 -0
- package/linthost/rules_unicorn_prefer_math_min_max.go +60 -0
- package/linthost/rules_unicorn_prefer_math_trunc.go +62 -0
- package/linthost/rules_unicorn_prefer_modern_dom_apis.go +47 -0
- package/linthost/rules_unicorn_prefer_modern_math_apis.go +70 -0
- package/linthost/rules_unicorn_prefer_module.go +50 -0
- package/linthost/rules_unicorn_prefer_native_coercion_functions.go +114 -0
- package/linthost/rules_unicorn_prefer_negative_index.go +74 -0
- package/linthost/rules_unicorn_prefer_node_protocol.go +122 -0
- package/linthost/rules_unicorn_prefer_number_properties.go +88 -0
- package/linthost/rules_unicorn_prefer_object_from_entries.go +55 -0
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +62 -0
- package/linthost/rules_unicorn_prefer_prototype_methods.go +54 -0
- package/linthost/rules_unicorn_prefer_query_selector.go +56 -0
- package/linthost/rules_unicorn_prefer_reflect_apply.go +36 -0
- package/linthost/rules_unicorn_prefer_regexp_test_rule.go +80 -0
- package/linthost/rules_unicorn_prefer_response_static_json.go +53 -0
- package/linthost/rules_unicorn_prefer_set_has.go +42 -0
- package/linthost/rules_unicorn_prefer_set_size.go +48 -0
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +34 -0
- package/linthost/rules_unicorn_prefer_single_call.go +96 -0
- package/linthost/rules_unicorn_prefer_spread.go +51 -0
- package/linthost/rules_unicorn_prefer_string_raw.go +43 -0
- package/linthost/rules_unicorn_prefer_string_replace_all.go +95 -0
- package/linthost/rules_unicorn_prefer_string_slice.go +43 -0
- package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +148 -0
- package/linthost/rules_unicorn_prefer_string_trim_start_end.go +48 -0
- package/linthost/rules_unicorn_prefer_structured_clone.go +71 -0
- package/linthost/rules_unicorn_prefer_switch.go +103 -0
- package/linthost/rules_unicorn_prefer_ternary.go +71 -0
- package/linthost/rules_unicorn_prefer_top_level_await.go +72 -0
- package/linthost/rules_unicorn_prefer_type_error.go +127 -0
- package/linthost/rules_unicorn_prevent_abbreviations.go +127 -0
- package/linthost/rules_unicorn_relative_url_style.go +49 -0
- package/linthost/rules_unicorn_require_array_join_separator.go +45 -0
- package/linthost/rules_unicorn_require_module_attributes.go +54 -0
- package/linthost/rules_unicorn_require_module_specifiers.go +55 -0
- package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +43 -0
- package/linthost/rules_unicorn_require_post_message_target_origin.go +44 -0
- package/linthost/rules_unicorn_string_content.go +27 -0
- package/linthost/rules_unicorn_switch_case_braces.go +42 -0
- package/linthost/rules_unicorn_switch_case_break_position.go +31 -0
- package/linthost/rules_unicorn_template_indent.go +28 -0
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +72 -0
- package/linthost/rules_unicorn_throw_new_error.go +61 -0
- package/linthost/rules_var.go +10 -4
- package/linthost/rules_vitest.go +568 -0
- package/package.json +3 -3
- package/plugin/main.go +49 -3
- package/rule/astutil/astutil.go +10 -3
- package/rule/rule.go +12 -10
- package/src/defaultFormat.ts +17 -10
- package/src/index.ts +65 -43
- package/src/structures/ITtscLintConfig.ts +14 -8
- package/src/structures/TtscLintRuleSetting.ts +55 -0
- package/src/structures/format/ITtscLintFormat.ts +141 -0
- package/src/structures/format/ITtscLintFormatJsDoc.ts +18 -0
- package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
- package/src/structures/format/index.ts +3 -0
- package/src/structures/index.ts +3 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +117 -0
- package/src/structures/rules/ITtscLintBoundariesRules.ts +91 -0
- package/src/structures/rules/ITtscLintContributorRules.ts +20 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +1378 -0
- package/src/structures/rules/ITtscLintCypressRuleOptions.ts +18 -0
- package/src/structures/rules/ITtscLintCypressRules.ts +141 -0
- package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +200 -0
- package/src/structures/rules/ITtscLintFunctionalRules.ts +197 -0
- package/src/structures/rules/ITtscLintJestRules.ts +209 -0
- package/src/structures/rules/ITtscLintJsdocRules.ts +146 -0
- package/src/structures/rules/ITtscLintJsxA11yRules.ts +331 -0
- package/src/structures/rules/ITtscLintNextjsRules.ts +201 -0
- package/src/structures/rules/ITtscLintPlaywrightRules.ts +343 -0
- package/src/structures/rules/ITtscLintPromiseRules.ts +196 -0
- package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +19 -0
- package/src/structures/rules/ITtscLintReactPerfRules.ts +53 -0
- package/src/structures/rules/ITtscLintReactRuleOptions.ts +41 -0
- package/src/structures/rules/ITtscLintReactRules.ts +308 -0
- package/src/structures/rules/ITtscLintRegexpRules.ts +204 -0
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -0
- package/src/structures/rules/ITtscLintRules.ts +77 -0
- package/src/structures/rules/ITtscLintSecurityRules.ts +155 -0
- package/src/structures/rules/ITtscLintSolidRules.ts +199 -0
- package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +22 -0
- package/src/structures/rules/ITtscLintStorybookRules.ts +184 -0
- package/src/structures/rules/ITtscLintTanstackQueryRules.ts +96 -0
- package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +23 -0
- package/src/structures/rules/ITtscLintTestingLibraryRules.ts +325 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +1172 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +1143 -0
- package/src/structures/rules/ITtscLintVitestRules.ts +146 -0
- package/src/structures/rules/index.ts +31 -0
- package/lib/structures/ITtscLintFormatConfig.d.ts +0 -153
- package/lib/structures/ITtscLintFormatConfig.js.map +0 -1
- package/lib/structures/TtscLintRule.d.ts +0 -17
- package/lib/structures/TtscLintRule.js.map +0 -1
- package/lib/structures/TtscLintRuleMap.d.ts +0 -46
- package/lib/structures/TtscLintRuleMap.js.map +0 -1
- package/lib/structures/TtscLintRuleOptions.d.ts +0 -137
- package/lib/structures/TtscLintRuleOptions.js.map +0 -1
- package/src/structures/ITtscLintFormatConfig.ts +0 -168
- package/src/structures/TtscLintRule.ts +0 -160
- package/src/structures/TtscLintRuleMap.ts +0 -55
- package/src/structures/TtscLintRuleOptions.ts +0 -151
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// unicorn/no-useless-fallback-in-spread: spreading `null` or `undefined`
|
|
2
|
+
// into an object literal is already a runtime no-op, so the common
|
|
3
|
+
// defensive shape `...(x ?? {})` adds an allocation and a comparison
|
|
4
|
+
// that change nothing. The same goes for `...(x || {})` and the
|
|
5
|
+
// array-literal variants. Drop the fallback and spread the value
|
|
6
|
+
// directly.
|
|
7
|
+
//
|
|
8
|
+
// AST-only: visit each spread node — `SpreadElement` only when its
|
|
9
|
+
// parent is an `ArrayLiteralExpression` (call-argument spread is
|
|
10
|
+
// excluded because spreading `null` / `undefined` into a function call
|
|
11
|
+
// throws a `TypeError`, so the fallback is load-bearing there) and
|
|
12
|
+
// `SpreadAssignment` for object spread. After stripping parentheses,
|
|
13
|
+
// the spread's operand must be a binary expression using `??` or `||`
|
|
14
|
+
// whose right-hand side is an empty object or array literal. The
|
|
15
|
+
// diagnostic anchors on the spread node so editors highlight the
|
|
16
|
+
// redundant fallback together with its `...`.
|
|
17
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-fallback-in-spread.md
|
|
18
|
+
package linthost
|
|
19
|
+
|
|
20
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
21
|
+
|
|
22
|
+
type unicornNoUselessFallbackInSpread struct{}
|
|
23
|
+
|
|
24
|
+
func (unicornNoUselessFallbackInSpread) Name() string {
|
|
25
|
+
return "unicorn/no-useless-fallback-in-spread"
|
|
26
|
+
}
|
|
27
|
+
func (unicornNoUselessFallbackInSpread) Visits() []shimast.Kind {
|
|
28
|
+
return []shimast.Kind{shimast.KindSpreadElement, shimast.KindSpreadAssignment}
|
|
29
|
+
}
|
|
30
|
+
func (unicornNoUselessFallbackInSpread) Check(ctx *Context, node *shimast.Node) {
|
|
31
|
+
var operand *shimast.Node
|
|
32
|
+
switch node.Kind {
|
|
33
|
+
case shimast.KindSpreadElement:
|
|
34
|
+
// Only flag array-literal spread. Call-argument spread of a
|
|
35
|
+
// null/undefined operand throws TypeError at runtime, so the
|
|
36
|
+
// `?? []` / `|| []` fallback is load-bearing in that position.
|
|
37
|
+
if node.Parent == nil || node.Parent.Kind != shimast.KindArrayLiteralExpression {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
if spread := node.AsSpreadElement(); spread != nil {
|
|
41
|
+
operand = spread.Expression
|
|
42
|
+
}
|
|
43
|
+
case shimast.KindSpreadAssignment:
|
|
44
|
+
if spread := node.AsSpreadAssignment(); spread != nil {
|
|
45
|
+
operand = spread.Expression
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
inner := stripParens(operand)
|
|
49
|
+
if inner == nil || inner.Kind != shimast.KindBinaryExpression {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
bin := inner.AsBinaryExpression()
|
|
53
|
+
if bin == nil || bin.OperatorToken == nil {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
switch bin.OperatorToken.Kind {
|
|
57
|
+
case shimast.KindQuestionQuestionToken, shimast.KindBarBarToken:
|
|
58
|
+
default:
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
right := stripParens(bin.Right)
|
|
62
|
+
if right == nil {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
switch right.Kind {
|
|
66
|
+
case shimast.KindObjectLiteralExpression:
|
|
67
|
+
if obj := right.AsObjectLiteralExpression(); obj != nil &&
|
|
68
|
+
(obj.Properties == nil || len(obj.Properties.Nodes) == 0) {
|
|
69
|
+
ctx.Report(node, "Don't use a useless `?? {}` or `?? []` fallback when spreading — `...null` and `...undefined` are no-ops.")
|
|
70
|
+
}
|
|
71
|
+
case shimast.KindArrayLiteralExpression:
|
|
72
|
+
if arr := right.AsArrayLiteralExpression(); arr != nil &&
|
|
73
|
+
(arr.Elements == nil || len(arr.Elements.Nodes) == 0) {
|
|
74
|
+
ctx.Report(node, "Don't use a useless `?? {}` or `?? []` fallback when spreading — `...null` and `...undefined` are no-ops.")
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func init() {
|
|
80
|
+
Register(unicornNoUselessFallbackInSpread{})
|
|
81
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// unicorn/no-useless-iterator-to-array: wrapping an iterator-producing
|
|
2
|
+
// call in an array-literal spread (`[...arr.entries()]`) materializes
|
|
3
|
+
// every element into a throwaway array just to walk through it again.
|
|
4
|
+
// The wrappers `.entries()`, `.keys()`, `.values()` already return
|
|
5
|
+
// iterables that any iteration consumer can consume directly.
|
|
6
|
+
//
|
|
7
|
+
// AST-only and conservative: visit each `ArrayLiteralExpression` whose
|
|
8
|
+
// only element is a `SpreadElement` wrapping a `CallExpression` whose
|
|
9
|
+
// callee is `PropertyAccess(_, name)` with `name ∈ {entries, keys,
|
|
10
|
+
// values}`. The array literal must also be in an iterator-consuming
|
|
11
|
+
// parent position — currently only `for…of` is matched, since materializing
|
|
12
|
+
// an array to iterate it once is the rule's textbook case. Using the
|
|
13
|
+
// materialized array as data (`[...iter][0]`, `[...iter].map(…)`,
|
|
14
|
+
// `const arr = [...iter]`) is NOT flagged because the author wants the
|
|
15
|
+
// array, not a fresh walk.
|
|
16
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-iterator-to-array.md
|
|
17
|
+
package linthost
|
|
18
|
+
|
|
19
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
20
|
+
|
|
21
|
+
type unicornNoUselessIteratorToArray struct{}
|
|
22
|
+
|
|
23
|
+
func (unicornNoUselessIteratorToArray) Name() string {
|
|
24
|
+
return "unicorn/no-useless-iterator-to-array"
|
|
25
|
+
}
|
|
26
|
+
func (unicornNoUselessIteratorToArray) Visits() []shimast.Kind {
|
|
27
|
+
return []shimast.Kind{shimast.KindArrayLiteralExpression}
|
|
28
|
+
}
|
|
29
|
+
func (unicornNoUselessIteratorToArray) Check(ctx *Context, node *shimast.Node) {
|
|
30
|
+
// Only fire when the array literal is the iterable of a `for…of`
|
|
31
|
+
// loop — that's the canonical wasted-materialization shape. Other
|
|
32
|
+
// uses (`[...iter][0]`, `const arr = [...iter]`, `.map`, etc.)
|
|
33
|
+
// genuinely need the materialized array.
|
|
34
|
+
if node.Parent == nil || node.Parent.Kind != shimast.KindForOfStatement {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
arr := node.AsArrayLiteralExpression()
|
|
38
|
+
if arr == nil || arr.Elements == nil || len(arr.Elements.Nodes) != 1 {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
only := arr.Elements.Nodes[0]
|
|
42
|
+
if only == nil || only.Kind != shimast.KindSpreadElement {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
spread := only.AsSpreadElement()
|
|
46
|
+
if spread == nil {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
inner := stripParens(spread.Expression)
|
|
50
|
+
if inner == nil || inner.Kind != shimast.KindCallExpression {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
call := inner.AsCallExpression()
|
|
54
|
+
if call == nil || call.Expression == nil ||
|
|
55
|
+
call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
access := call.Expression.AsPropertyAccessExpression()
|
|
59
|
+
if access == nil {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
switch identifierText(access.Name()) {
|
|
63
|
+
case "entries", "keys", "values":
|
|
64
|
+
default:
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
ctx.Report(node, "Don't wrap an iterator with `[...iter]` when iterating directly works.")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func init() {
|
|
71
|
+
Register(unicornNoUselessIteratorToArray{})
|
|
72
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// unicorn/no-useless-length-check: `arr.length > 0 && arr.some(…)`
|
|
2
|
+
// short-circuits on an empty array, but `arr.some(…)` already returns
|
|
3
|
+
// `false` for the empty case, so the leading length check is redundant.
|
|
4
|
+
// The same goes for `forEach` (returns `undefined` either way). The
|
|
5
|
+
// rule prunes the dead check so the call shape is the only thing the
|
|
6
|
+
// reader has to track.
|
|
7
|
+
//
|
|
8
|
+
// `every`, `map`, and `filter` are deliberately excluded from the `&&`
|
|
9
|
+
// set: `every` returns `true` on an empty array (so the length check
|
|
10
|
+
// IS load-bearing); `map` and `filter` return `[]` which is truthy
|
|
11
|
+
// (the length check changes the truthiness of the whole expression).
|
|
12
|
+
// Upstream covers those under the complementary `||` pattern
|
|
13
|
+
// (`array.length === 0 || array.every(…)`), which this MVP does not
|
|
14
|
+
// implement yet.
|
|
15
|
+
//
|
|
16
|
+
// AST-only MVP: visit each `BinaryExpression`, match operator `&&`,
|
|
17
|
+
// require LHS to be `PropertyAccess(X, length) > 0` or `!== 0`, and
|
|
18
|
+
// require RHS to be `CallExpression(PropertyAccess(X, name), …)` where
|
|
19
|
+
// `name` is one of `some` / `forEach` and the textual form of the two
|
|
20
|
+
// `X` expressions matches. Fire on the binary expression.
|
|
21
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-length-check.md
|
|
22
|
+
package linthost
|
|
23
|
+
|
|
24
|
+
import (
|
|
25
|
+
"fmt"
|
|
26
|
+
|
|
27
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
var unicornNoUselessLengthCheckMethods = map[string]struct{}{
|
|
31
|
+
"some": {},
|
|
32
|
+
"forEach": {},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type unicornNoUselessLengthCheck struct{}
|
|
36
|
+
|
|
37
|
+
func (unicornNoUselessLengthCheck) Name() string { return "unicorn/no-useless-length-check" }
|
|
38
|
+
func (unicornNoUselessLengthCheck) Visits() []shimast.Kind {
|
|
39
|
+
return []shimast.Kind{shimast.KindBinaryExpression}
|
|
40
|
+
}
|
|
41
|
+
func (unicornNoUselessLengthCheck) Check(ctx *Context, node *shimast.Node) {
|
|
42
|
+
bin := node.AsBinaryExpression()
|
|
43
|
+
if bin == nil || bin.OperatorToken == nil ||
|
|
44
|
+
bin.OperatorToken.Kind != shimast.KindAmpersandAmpersandToken {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
left := stripParens(bin.Left)
|
|
48
|
+
right := stripParens(bin.Right)
|
|
49
|
+
if left == nil || right == nil {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
lengthReceiver := unicornUselessLengthCheckLHSReceiver(ctx, left)
|
|
53
|
+
if lengthReceiver == "" {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
method, callReceiver := unicornUselessLengthCheckRHSReceiver(ctx, right)
|
|
57
|
+
if method == "" || callReceiver == "" {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
if lengthReceiver != callReceiver {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
ctx.Report(node, fmt.Sprintf("Useless `.length` check — `%s` already handles empty arrays correctly.", method))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// unicornUselessLengthCheckLHSReceiver returns the textual form of `X`
|
|
67
|
+
// when `node` is `X.length > 0` or `X.length !== 0` (either operand
|
|
68
|
+
// order); returns "" otherwise.
|
|
69
|
+
func unicornUselessLengthCheckLHSReceiver(ctx *Context, node *shimast.Node) string {
|
|
70
|
+
bin := node.AsBinaryExpression()
|
|
71
|
+
if bin == nil || bin.OperatorToken == nil || bin.Left == nil || bin.Right == nil {
|
|
72
|
+
return ""
|
|
73
|
+
}
|
|
74
|
+
op := bin.OperatorToken.Kind
|
|
75
|
+
if op != shimast.KindGreaterThanToken &&
|
|
76
|
+
op != shimast.KindExclamationEqualsEqualsToken &&
|
|
77
|
+
op != shimast.KindExclamationEqualsToken {
|
|
78
|
+
return ""
|
|
79
|
+
}
|
|
80
|
+
lhs := stripParens(bin.Left)
|
|
81
|
+
rhs := stripParens(bin.Right)
|
|
82
|
+
// Try `X.length <op> 0`.
|
|
83
|
+
if recv := unicornUselessLengthCheckMatchLengthAccess(ctx, lhs); recv != "" &&
|
|
84
|
+
unicornUselessLengthCheckIsZero(rhs) {
|
|
85
|
+
return recv
|
|
86
|
+
}
|
|
87
|
+
// `0 !== X.length` (no `<` form because `>` is asymmetric — covered
|
|
88
|
+
// by the LHS branch above).
|
|
89
|
+
if op == shimast.KindExclamationEqualsEqualsToken || op == shimast.KindExclamationEqualsToken {
|
|
90
|
+
if recv := unicornUselessLengthCheckMatchLengthAccess(ctx, rhs); recv != "" &&
|
|
91
|
+
unicornUselessLengthCheckIsZero(lhs) {
|
|
92
|
+
return recv
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return ""
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// unicornUselessLengthCheckMatchLengthAccess returns the receiver text
|
|
99
|
+
// when `node` is a `PropertyAccessExpression(X, length)`; "" otherwise.
|
|
100
|
+
func unicornUselessLengthCheckMatchLengthAccess(ctx *Context, node *shimast.Node) string {
|
|
101
|
+
if node == nil || node.Kind != shimast.KindPropertyAccessExpression {
|
|
102
|
+
return ""
|
|
103
|
+
}
|
|
104
|
+
access := node.AsPropertyAccessExpression()
|
|
105
|
+
if access == nil || identifierText(access.Name()) != "length" {
|
|
106
|
+
return ""
|
|
107
|
+
}
|
|
108
|
+
return nodeText(ctx.File, access.Expression)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// unicornUselessLengthCheckIsZero reports whether `node` is the numeric
|
|
112
|
+
// literal `0`.
|
|
113
|
+
func unicornUselessLengthCheckIsZero(node *shimast.Node) bool {
|
|
114
|
+
return node != nil &&
|
|
115
|
+
node.Kind == shimast.KindNumericLiteral &&
|
|
116
|
+
numericLiteralText(node) == "0"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// unicornUselessLengthCheckRHSReceiver returns (method, receiverText)
|
|
120
|
+
// when `node` is `X.method(...)` for one of the empty-safe iteration
|
|
121
|
+
// methods; ("", "") otherwise.
|
|
122
|
+
func unicornUselessLengthCheckRHSReceiver(ctx *Context, node *shimast.Node) (string, string) {
|
|
123
|
+
if node == nil || node.Kind != shimast.KindCallExpression {
|
|
124
|
+
return "", ""
|
|
125
|
+
}
|
|
126
|
+
call := node.AsCallExpression()
|
|
127
|
+
if call == nil || call.Expression == nil ||
|
|
128
|
+
call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
129
|
+
return "", ""
|
|
130
|
+
}
|
|
131
|
+
access := call.Expression.AsPropertyAccessExpression()
|
|
132
|
+
if access == nil {
|
|
133
|
+
return "", ""
|
|
134
|
+
}
|
|
135
|
+
method := identifierText(access.Name())
|
|
136
|
+
if _, ok := unicornNoUselessLengthCheckMethods[method]; !ok {
|
|
137
|
+
return "", ""
|
|
138
|
+
}
|
|
139
|
+
return method, nodeText(ctx.File, access.Expression)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
func init() {
|
|
143
|
+
Register(unicornNoUselessLengthCheck{})
|
|
144
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// unicorn/no-useless-promise-resolve-reject: inside an `async`
|
|
2
|
+
// function, `return x` already wraps the value in a resolved promise
|
|
3
|
+
// and `throw err` already produces a rejected one. Writing `return
|
|
4
|
+
// Promise.resolve(x)` or `return Promise.reject(err)` rebuilds the
|
|
5
|
+
// wrapper the runtime would build anyway, hides the actual value
|
|
6
|
+
// behind an extra call, and reads as if the author forgot the
|
|
7
|
+
// function was async.
|
|
8
|
+
//
|
|
9
|
+
// AST-only and parent-walking: visit each `ReturnStatement`, find the
|
|
10
|
+
// nearest enclosing function-like ancestor, and require it to carry
|
|
11
|
+
// the `async` modifier. The return's expression after `stripParens`
|
|
12
|
+
// must be a `CallExpression` whose callee is
|
|
13
|
+
// `PropertyAccess(Identifier("Promise"), name)` with
|
|
14
|
+
// `name ∈ {resolve, reject}`. Arrow concise bodies are not handled
|
|
15
|
+
// here because the rule's contract anchors on the `return` keyword.
|
|
16
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-promise-resolve-reject.md
|
|
17
|
+
package linthost
|
|
18
|
+
|
|
19
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
20
|
+
|
|
21
|
+
type unicornNoUselessPromiseResolveReject struct{}
|
|
22
|
+
|
|
23
|
+
func (unicornNoUselessPromiseResolveReject) Name() string {
|
|
24
|
+
return "unicorn/no-useless-promise-resolve-reject"
|
|
25
|
+
}
|
|
26
|
+
func (unicornNoUselessPromiseResolveReject) Visits() []shimast.Kind {
|
|
27
|
+
return []shimast.Kind{shimast.KindReturnStatement}
|
|
28
|
+
}
|
|
29
|
+
func (unicornNoUselessPromiseResolveReject) Check(ctx *Context, node *shimast.Node) {
|
|
30
|
+
ret := node.AsReturnStatement()
|
|
31
|
+
if ret == nil || ret.Expression == nil {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if !unicornNoUselessPromiseResolveRejectInAsync(node) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
expr := stripParens(ret.Expression)
|
|
38
|
+
if expr == nil || expr.Kind != shimast.KindCallExpression {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
call := expr.AsCallExpression()
|
|
42
|
+
if call == nil || call.Expression == nil ||
|
|
43
|
+
call.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
access := call.Expression.AsPropertyAccessExpression()
|
|
47
|
+
if access == nil {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
if identifierText(access.Expression) != "Promise" {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
switch identifierText(access.Name()) {
|
|
54
|
+
case "resolve", "reject":
|
|
55
|
+
default:
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
ctx.Report(node, "In `async` functions, `return x` and `throw e` work identically — drop the `Promise.<method>` wrapper.")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// unicornNoUselessPromiseResolveRejectInAsync walks ancestors of `node`
|
|
62
|
+
// and reports whether the nearest enclosing function-like declaration
|
|
63
|
+
// is marked `async`. Crossing a function boundary that is not async
|
|
64
|
+
// stops the walk — a nested non-async function shadows the outer
|
|
65
|
+
// async context.
|
|
66
|
+
func unicornNoUselessPromiseResolveRejectInAsync(node *shimast.Node) bool {
|
|
67
|
+
for cur := node.Parent; cur != nil; cur = cur.Parent {
|
|
68
|
+
switch cur.Kind {
|
|
69
|
+
case shimast.KindFunctionDeclaration,
|
|
70
|
+
shimast.KindFunctionExpression,
|
|
71
|
+
shimast.KindArrowFunction,
|
|
72
|
+
shimast.KindMethodDeclaration:
|
|
73
|
+
return hasModifier(cur, shimast.KindAsyncKeyword)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func init() {
|
|
80
|
+
Register(unicornNoUselessPromiseResolveReject{})
|
|
81
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// unicorn/no-useless-spread: wrapping an already-spread literal in
|
|
2
|
+
// another spread produces the original literal — `[...[1, 2, 3]]` is
|
|
3
|
+
// just `[1, 2, 3]`, and `{...{a: 1}}` is `{a: 1}`. The extra spread
|
|
4
|
+
// reads like an attempted clone but allocates an intermediate literal
|
|
5
|
+
// for no observable effect.
|
|
6
|
+
//
|
|
7
|
+
// AST-only and conservative: visit every `ArrayLiteralExpression` and
|
|
8
|
+
// `ObjectLiteralExpression`; match when the literal contains exactly
|
|
9
|
+
// one element that is a `SpreadElement` / `SpreadAssignment` whose
|
|
10
|
+
// operand (after `stripParens`) is itself a literal of the same kind.
|
|
11
|
+
// The receiver expressions are not inspected — the syntactic shape
|
|
12
|
+
// `[...[…]]` / `{...{…}}` is the signal regardless of the inner
|
|
13
|
+
// literal's contents. Mirrors the conservative dispatch used by
|
|
14
|
+
// `unicorn/prefer-set-size`.
|
|
15
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-spread.md
|
|
16
|
+
package linthost
|
|
17
|
+
|
|
18
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
19
|
+
|
|
20
|
+
type unicornNoUselessSpread struct{}
|
|
21
|
+
|
|
22
|
+
func (unicornNoUselessSpread) Name() string { return "unicorn/no-useless-spread" }
|
|
23
|
+
func (unicornNoUselessSpread) Visits() []shimast.Kind {
|
|
24
|
+
return []shimast.Kind{shimast.KindArrayLiteralExpression, shimast.KindObjectLiteralExpression}
|
|
25
|
+
}
|
|
26
|
+
func (unicornNoUselessSpread) Check(ctx *Context, node *shimast.Node) {
|
|
27
|
+
switch node.Kind {
|
|
28
|
+
case shimast.KindArrayLiteralExpression:
|
|
29
|
+
arr := node.AsArrayLiteralExpression()
|
|
30
|
+
if arr == nil || arr.Elements == nil || len(arr.Elements.Nodes) != 1 {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
only := arr.Elements.Nodes[0]
|
|
34
|
+
if only == nil || only.Kind != shimast.KindSpreadElement {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
spread := only.AsSpreadElement()
|
|
38
|
+
if spread == nil {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
inner := stripParens(spread.Expression)
|
|
42
|
+
if inner != nil && inner.Kind == shimast.KindArrayLiteralExpression {
|
|
43
|
+
ctx.Report(node, "Don't wrap an already-spread literal in another spread.")
|
|
44
|
+
}
|
|
45
|
+
case shimast.KindObjectLiteralExpression:
|
|
46
|
+
obj := node.AsObjectLiteralExpression()
|
|
47
|
+
if obj == nil || obj.Properties == nil || len(obj.Properties.Nodes) != 1 {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
only := obj.Properties.Nodes[0]
|
|
51
|
+
if only == nil || only.Kind != shimast.KindSpreadAssignment {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
spread := only.AsSpreadAssignment()
|
|
55
|
+
if spread == nil {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
inner := stripParens(spread.Expression)
|
|
59
|
+
if inner != nil && inner.Kind == shimast.KindObjectLiteralExpression {
|
|
60
|
+
ctx.Report(node, "Don't wrap an already-spread literal in another spread.")
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func init() {
|
|
66
|
+
Register(unicornNoUselessSpread{})
|
|
67
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// unicorn/no-useless-switch-case: a `case` clause with no statements
|
|
2
|
+
// that sits immediately above the `default` clause falls through to
|
|
3
|
+
// `default` for free — the value it matches would have hit `default`
|
|
4
|
+
// regardless. Keeping the empty case adds noise without changing the
|
|
5
|
+
// switch's behavior, so the rule reports it.
|
|
6
|
+
//
|
|
7
|
+
// AST-only: visit each `SwitchStatement`, walk its case-block clauses,
|
|
8
|
+
// and report any non-default `CaseClause` whose statements list is
|
|
9
|
+
// empty and whose immediate next sibling is the `DefaultClause`. The
|
|
10
|
+
// empty-body check distinguishes intentional grouped cases that share a
|
|
11
|
+
// non-trivial body from the redundant fall-into-default shape.
|
|
12
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-switch-case.md
|
|
13
|
+
package linthost
|
|
14
|
+
|
|
15
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
16
|
+
|
|
17
|
+
type unicornNoUselessSwitchCase struct{}
|
|
18
|
+
|
|
19
|
+
func (unicornNoUselessSwitchCase) Name() string { return "unicorn/no-useless-switch-case" }
|
|
20
|
+
func (unicornNoUselessSwitchCase) Visits() []shimast.Kind {
|
|
21
|
+
return []shimast.Kind{shimast.KindSwitchStatement}
|
|
22
|
+
}
|
|
23
|
+
func (unicornNoUselessSwitchCase) Check(ctx *Context, node *shimast.Node) {
|
|
24
|
+
sw := node.AsSwitchStatement()
|
|
25
|
+
if sw == nil || sw.CaseBlock == nil {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
block := sw.CaseBlock.AsCaseBlock()
|
|
29
|
+
if block == nil || block.Clauses == nil {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
clauses := block.Clauses.Nodes
|
|
33
|
+
for i := 0; i < len(clauses)-1; i++ {
|
|
34
|
+
clause := clauses[i]
|
|
35
|
+
if clause == nil || clause.Kind != shimast.KindCaseClause {
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
next := clauses[i+1]
|
|
39
|
+
if next == nil || next.Kind != shimast.KindDefaultClause {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
cd := clause.AsCaseOrDefaultClause()
|
|
43
|
+
if cd == nil {
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
if cd.Statements != nil && len(cd.Statements.Nodes) != 0 {
|
|
47
|
+
continue
|
|
48
|
+
}
|
|
49
|
+
ctx.Report(clause, "Useless `case` clause — it falls through to the `default` which would handle this value anyway.")
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func init() {
|
|
54
|
+
Register(unicornNoUselessSwitchCase{})
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// unicorn/no-useless-undefined: `return undefined;` produces the same
|
|
2
|
+
// value as the bare `return;` form, and `void 0` is the canonical
|
|
3
|
+
// roundabout spelling of the same constant. Writing the keyword
|
|
4
|
+
// explicitly adds noise without changing behavior.
|
|
5
|
+
//
|
|
6
|
+
// Minimum-viable port: only the `return` statement shape fires. The
|
|
7
|
+
// call-argument case is intentionally skipped because matching it
|
|
8
|
+
// safely requires knowing which callees accept omission, and the
|
|
9
|
+
// parameter-default case (`function f(x = undefined)`) is deferred.
|
|
10
|
+
//
|
|
11
|
+
// AST-only: visit each `ReturnStatement` whose expression after
|
|
12
|
+
// `stripParens` is either the `undefined` keyword/identifier or the
|
|
13
|
+
// `void 0` shape (a `VoidExpression` whose operand is the numeric
|
|
14
|
+
// literal `0`). Reports on the return statement itself so the
|
|
15
|
+
// diagnostic anchors on the keyword rather than the operand.
|
|
16
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-undefined.md
|
|
17
|
+
package linthost
|
|
18
|
+
|
|
19
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
20
|
+
|
|
21
|
+
type unicornNoUselessUndefined struct{}
|
|
22
|
+
|
|
23
|
+
func (unicornNoUselessUndefined) Name() string { return "unicorn/no-useless-undefined" }
|
|
24
|
+
func (unicornNoUselessUndefined) Visits() []shimast.Kind {
|
|
25
|
+
return []shimast.Kind{shimast.KindReturnStatement}
|
|
26
|
+
}
|
|
27
|
+
func (unicornNoUselessUndefined) Check(ctx *Context, node *shimast.Node) {
|
|
28
|
+
ret := node.AsReturnStatement()
|
|
29
|
+
if ret == nil || ret.Expression == nil {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
expr := stripParens(ret.Expression)
|
|
33
|
+
if expr == nil {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
if expr.Kind == shimast.KindUndefinedKeyword || identifierText(expr) == "undefined" {
|
|
37
|
+
ctx.Report(node, "Don't `return undefined;` — `return undefined;` and bare `return;` have the same effect.")
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
if expr.Kind == shimast.KindVoidExpression {
|
|
41
|
+
void := expr.AsVoidExpression()
|
|
42
|
+
if void == nil || void.Expression == nil {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
operand := stripParens(void.Expression)
|
|
46
|
+
if operand != nil && operand.Kind == shimast.KindNumericLiteral &&
|
|
47
|
+
numericLiteralText(operand) == "0" {
|
|
48
|
+
ctx.Report(node, "Don't `return undefined;` — `return undefined;` and bare `return;` have the same effect.")
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func init() {
|
|
54
|
+
Register(unicornNoUselessUndefined{})
|
|
55
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// unicorn/no-zero-fractions: numeric literals such as `1.0`, `1.`, and
|
|
2
|
+
// `.0` carry a redundant fractional component or a trailing dot whose
|
|
3
|
+
// only visual effect is to mislead the reader into thinking the value
|
|
4
|
+
// needs floating-point semantics. The rule rewrites the source pattern
|
|
5
|
+
// out by reporting on the literal node.
|
|
6
|
+
//
|
|
7
|
+
// AST-only: visit `KindNumericLiteral`, read the raw source text via
|
|
8
|
+
// `nodeText` so the parser-normalized `.Text` (which drops the trailing
|
|
9
|
+
// dot and zero-pad) doesn't mask the smell, and match three regular
|
|
10
|
+
// expressions covering the canonical shapes:
|
|
11
|
+
//
|
|
12
|
+
// - `1.0`, `1.00`, `-1.0e5` — fractional part is all zeros.
|
|
13
|
+
// - `1.` — trailing dot with no fractional digits.
|
|
14
|
+
// - `.0`, `-.0e3` — leading dot with an all-zero fractional part.
|
|
15
|
+
//
|
|
16
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-zero-fractions.md
|
|
17
|
+
package linthost
|
|
18
|
+
|
|
19
|
+
import (
|
|
20
|
+
"regexp"
|
|
21
|
+
"strings"
|
|
22
|
+
|
|
23
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
var (
|
|
27
|
+
unicornNoZeroFractionsTrailingZero = regexp.MustCompile(`^-?\d+\.0+([eE][+-]?\d+)?$`)
|
|
28
|
+
unicornNoZeroFractionsTrailingDot = regexp.MustCompile(`^-?\d+\.$`)
|
|
29
|
+
unicornNoZeroFractionsLeadingDot = regexp.MustCompile(`^-?\.0+([eE][+-]?\d+)?$`)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
type unicornNoZeroFractions struct{}
|
|
33
|
+
|
|
34
|
+
func (unicornNoZeroFractions) Name() string { return "unicorn/no-zero-fractions" }
|
|
35
|
+
func (unicornNoZeroFractions) Visits() []shimast.Kind {
|
|
36
|
+
return []shimast.Kind{shimast.KindNumericLiteral}
|
|
37
|
+
}
|
|
38
|
+
func (unicornNoZeroFractions) Check(ctx *Context, node *shimast.Node) {
|
|
39
|
+
source := strings.TrimSpace(nodeText(ctx.File, node))
|
|
40
|
+
if source == "" {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if unicornNoZeroFractionsTrailingZero.MatchString(source) ||
|
|
44
|
+
unicornNoZeroFractionsTrailingDot.MatchString(source) ||
|
|
45
|
+
unicornNoZeroFractionsLeadingDot.MatchString(source) {
|
|
46
|
+
ctx.Report(node, "Don't use a redundant `.0` fraction or trailing dot on a number literal.")
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func init() {
|
|
51
|
+
Register(unicornNoZeroFractions{})
|
|
52
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// unicorn/number-literal-case: hex / binary / octal numeric literals
|
|
2
|
+
// have a conventional canonical form — the radix prefix is lowercase
|
|
3
|
+
// (`0x`, `0b`, `0o`) while hex digits are uppercase (`0xFF`). Mixed-case
|
|
4
|
+
// alternatives (`0Xff`, `0xff`, `0xFf`, `0XFF`) read inconsistently and
|
|
5
|
+
// the rule normalizes the codebase by reporting every literal that
|
|
6
|
+
// isn't already canonical.
|
|
7
|
+
//
|
|
8
|
+
// AST-only: visit `KindNumericLiteral` and `KindBigIntLiteral`, read
|
|
9
|
+
// the raw source text via `nodeText` so the prefix and digit casing
|
|
10
|
+
// survive the parser's normalization, and report when the prefix letter
|
|
11
|
+
// is uppercase OR any hex digit is lowercase. Decimal literals lack a
|
|
12
|
+
// prefix and never fire.
|
|
13
|
+
//
|
|
14
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/number-literal-case.md
|
|
15
|
+
package linthost
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"strings"
|
|
19
|
+
|
|
20
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
type unicornNumberLiteralCase struct{}
|
|
24
|
+
|
|
25
|
+
func (unicornNumberLiteralCase) Name() string { return "unicorn/number-literal-case" }
|
|
26
|
+
func (unicornNumberLiteralCase) Visits() []shimast.Kind {
|
|
27
|
+
return []shimast.Kind{shimast.KindNumericLiteral, shimast.KindBigIntLiteral}
|
|
28
|
+
}
|
|
29
|
+
func (unicornNumberLiteralCase) Check(ctx *Context, node *shimast.Node) {
|
|
30
|
+
source := strings.TrimSpace(nodeText(ctx.File, node))
|
|
31
|
+
if len(source) < 2 || source[0] != '0' {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
prefix := source[1]
|
|
35
|
+
switch prefix {
|
|
36
|
+
case 'x', 'X':
|
|
37
|
+
if unicornNumberLiteralCaseHexNeedsFix(source, prefix) {
|
|
38
|
+
ctx.Report(node, "Number literals should use a lowercase prefix and uppercase digits — e.g. `0xFF` instead of `0xff`.")
|
|
39
|
+
}
|
|
40
|
+
case 'b', 'B', 'o', 'O':
|
|
41
|
+
// Binary / octal digits are 0-1 / 0-7; only the prefix letter
|
|
42
|
+
// can be in the wrong case.
|
|
43
|
+
if prefix == 'B' || prefix == 'O' {
|
|
44
|
+
ctx.Report(node, "Number literals should use a lowercase prefix and uppercase digits — e.g. `0xFF` instead of `0xff`.")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// unicornNumberLiteralCaseHexNeedsFix reports whether the hex literal's
|
|
50
|
+
// raw source text deviates from `0x` + uppercase digits. The trailing
|
|
51
|
+
// `n` of a hex bigint literal (`0xFFn`) is allowed.
|
|
52
|
+
func unicornNumberLiteralCaseHexNeedsFix(source string, prefix byte) bool {
|
|
53
|
+
if prefix != 'x' {
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
digits := source[2:]
|
|
57
|
+
digits = strings.TrimSuffix(digits, "n")
|
|
58
|
+
for i := 0; i < len(digits); i++ {
|
|
59
|
+
ch := digits[i]
|
|
60
|
+
if ch >= 'a' && ch <= 'f' {
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func init() {
|
|
68
|
+
Register(unicornNumberLiteralCase{})
|
|
69
|
+
}
|