@ttsc/lint 0.14.0-dev.20260529.2 → 0.14.0
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 +21 -23
- package/lib/defaultFormat.d.ts +10 -6
- package/lib/defaultFormat.js +10 -6
- package/lib/defaultFormat.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +5 -5
- package/lib/structures/TtscLintRuleSetting.d.ts +19 -19
- package/lib/structures/{ITtscLintFormat.d.ts → format/ITtscLintFormat.d.ts} +45 -70
- package/lib/structures/format/ITtscLintFormat.js.map +1 -0
- package/lib/structures/format/ITtscLintFormatJsDoc.d.ts +15 -0
- package/lib/structures/format/ITtscLintFormatJsDoc.js +3 -0
- 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 +1 -1
- package/lib/structures/index.js +1 -1
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +18 -27
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js +4 -5
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.js.map +1 -1
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +29 -33
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +6 -7
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +380 -408
- package/lib/structures/rules/ITtscLintCypressRuleOptions.d.ts +5 -5
- package/lib/structures/rules/ITtscLintCypressRuleOptions.js +2 -2
- package/lib/structures/rules/ITtscLintCypressRules.d.ts +34 -38
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.d.ts +21 -24
- package/lib/structures/rules/ITtscLintFunctionalRuleOptions.js +2 -2
- package/lib/structures/rules/ITtscLintFunctionalRules.d.ts +51 -56
- package/lib/structures/rules/ITtscLintJestRules.d.ts +63 -74
- package/lib/structures/rules/ITtscLintJsdocRules.d.ts +47 -54
- package/lib/structures/rules/ITtscLintJsxA11yRules.d.ts +94 -102
- package/lib/structures/rules/ITtscLintNextjsRules.d.ts +50 -55
- package/lib/structures/rules/ITtscLintPlaywrightRules.d.ts +104 -122
- package/lib/structures/rules/ITtscLintPromiseRules.d.ts +62 -71
- package/lib/structures/rules/ITtscLintReactPerfRuleOptions.d.ts +4 -4
- package/lib/structures/rules/ITtscLintReactPerfRules.d.ts +17 -19
- package/lib/structures/rules/ITtscLintReactRuleOptions.d.ts +8 -10
- package/lib/structures/rules/ITtscLintReactRuleOptions.js +1 -2
- package/lib/structures/rules/ITtscLintReactRuleOptions.js.map +1 -1
- package/lib/structures/rules/ITtscLintReactRules.d.ts +91 -104
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +46 -54
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +7 -8
- package/lib/structures/rules/ITtscLintSecurityRules.d.ts +45 -52
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +53 -56
- package/lib/structures/rules/ITtscLintStorybookRuleOptions.d.ts +3 -4
- package/lib/structures/rules/ITtscLintStorybookRules.d.ts +60 -69
- package/lib/structures/rules/ITtscLintTanstackQueryRules.d.ts +31 -37
- package/lib/structures/rules/ITtscLintTestingLibraryRuleOptions.d.ts +2 -3
- package/lib/structures/rules/ITtscLintTestingLibraryRules.d.ts +102 -119
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +460 -518
- package/lib/structures/rules/ITtscLintVitestRules.d.ts +43 -51
- package/linthost/config.go +89 -72
- package/linthost/config_format.go +265 -99
- package/linthost/directives.go +5 -1
- package/linthost/display_width.go +72 -0
- package/linthost/fix.go +26 -1
- package/linthost/format.go +222 -25
- package/linthost/format_editor_settings.go +262 -0
- package/linthost/lsp.go +8 -4
- package/linthost/print_dispatch.go +3 -0
- package/linthost/print_doc.go +13 -0
- package/linthost/print_engine.go +68 -11
- package/linthost/print_nodes_array.go +108 -5
- package/linthost/print_nodes_call.go +515 -74
- package/linthost/print_nodes_function.go +25 -0
- package/linthost/print_nodes_imports.go +47 -14
- package/linthost/print_nodes_list.go +191 -8
- package/linthost/print_nodes_object.go +12 -6
- package/linthost/print_nodes_ternary.go +106 -0
- package/linthost/rules_boundaries.go +400 -400
- package/linthost/rules_camelcase.go +81 -81
- package/linthost/rules_complexity.go +52 -51
- package/linthost/rules_consistent_return.go +81 -81
- package/linthost/rules_core_extra.go +472 -472
- package/linthost/rules_curly.go +43 -43
- package/linthost/rules_default_case.go +15 -15
- package/linthost/rules_default_case_last.go +22 -22
- 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 +664 -0
- package/linthost/rules_format_indent.go +616 -0
- package/linthost/rules_format_jsdoc.go +4 -4
- 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 +185 -69
- package/linthost/rules_format_quote_props.go +169 -0
- package/linthost/rules_format_quotes.go +75 -29
- package/linthost/rules_format_semi.go +185 -10
- package/linthost/rules_format_sort_imports.go +456 -121
- package/linthost/rules_format_statement_split.go +368 -0
- package/linthost/rules_format_ternary_nullish_parens.go +77 -0
- package/linthost/rules_format_trailing_comma.go +21 -2
- package/linthost/rules_format_whitespace.go +336 -0
- package/linthost/rules_functional.go +688 -688
- package/linthost/rules_gap.go +146 -11
- package/linthost/rules_grouped_accessor_pairs.go +60 -60
- package/linthost/rules_guard_for_in.go +122 -122
- package/linthost/rules_id_length.go +44 -44
- package/linthost/rules_imports.go +11 -3
- package/linthost/rules_init_declarations.go +25 -25
- package/linthost/rules_jsx_a11y.go +844 -844
- package/linthost/rules_max_classes_per_file.go +24 -24
- package/linthost/rules_max_depth.go +43 -43
- package/linthost/rules_max_lines.go +53 -53
- package/linthost/rules_max_lines_per_function.go +28 -28
- package/linthost/rules_max_nested_callbacks.go +26 -26
- package/linthost/rules_max_params.go +20 -20
- package/linthost/rules_max_statements.go +29 -29
- package/linthost/rules_nextjs.go +5 -5
- package/linthost/rules_no_else_return.go +38 -38
- package/linthost/rules_no_empty_named_blocks.go +45 -45
- package/linthost/rules_no_import_assign.go +96 -96
- package/linthost/rules_no_invalid_this.go +19 -19
- package/linthost/rules_no_loop_func.go +43 -43
- package/linthost/rules_no_magic_numbers.go +60 -60
- package/linthost/rules_no_mixed_operators.go +44 -44
- package/linthost/rules_no_param_reassign.go +64 -64
- package/linthost/rules_no_restricted_imports.go +45 -45
- package/linthost/rules_no_restricted_syntax.go +17 -17
- package/linthost/rules_no_shadow.go +112 -112
- package/linthost/rules_no_unreachable.go +36 -36
- package/linthost/rules_no_useless_assignment.go +79 -79
- package/linthost/rules_no_useless_constructor.go +167 -167
- package/linthost/rules_no_useless_return.go +28 -28
- package/linthost/rules_prefer_arrow_callback.go +74 -74
- package/linthost/rules_prefer_destructuring.go +61 -61
- package/linthost/rules_prefer_named_capture_group.go +87 -87
- package/linthost/rules_prefer_rest_params.go +61 -61
- package/linthost/rules_promise.go +800 -800
- package/linthost/rules_react.go +600 -600
- package/linthost/rules_react_extras.go +162 -162
- package/linthost/rules_react_hooks.go +2 -2
- package/linthost/rules_react_perf.go +231 -231
- package/linthost/rules_regexp.go +293 -293
- package/linthost/rules_security.go +613 -613
- package/linthost/rules_sort_imports.go +37 -37
- package/linthost/rules_sort_keys.go +88 -88
- package/linthost/rules_suggestions.go +13 -0
- package/linthost/rules_ts.go +2 -2
- package/linthost/rules_ts_async.go +247 -247
- package/linthost/rules_ts_class_literal_property_style.go +64 -64
- package/linthost/rules_ts_consistent_type_exports.go +209 -209
- package/linthost/rules_ts_explicit_function_return_type.go +35 -35
- package/linthost/rules_ts_extra.go +6 -2
- package/linthost/rules_ts_no_base_to_string.go +140 -140
- package/linthost/rules_ts_no_confusing_void.go +28 -28
- package/linthost/rules_ts_no_deprecated.go +110 -110
- package/linthost/rules_ts_no_for_in_array.go +45 -45
- package/linthost/rules_ts_no_magic_numbers.go +67 -67
- package/linthost/rules_ts_no_meaningless_void_operator.go +44 -44
- package/linthost/rules_ts_no_misused_spread.go +64 -64
- package/linthost/rules_ts_no_redundant_type_constituents.go +72 -72
- package/linthost/rules_ts_no_restricted_types.go +28 -28
- package/linthost/rules_ts_no_unnecessary_boolean_literal_compare.go +87 -87
- package/linthost/rules_ts_no_unnecessary_condition.go +184 -184
- package/linthost/rules_ts_no_unnecessary_qualifier.go +72 -72
- package/linthost/rules_ts_no_unnecessary_template_expression.go +120 -120
- package/linthost/rules_ts_no_unnecessary_type_arguments.go +171 -171
- package/linthost/rules_ts_no_unnecessary_type_assertion.go +90 -90
- package/linthost/rules_ts_no_unsafe_argument.go +50 -50
- package/linthost/rules_ts_no_unsafe_assignment.go +60 -60
- package/linthost/rules_ts_no_unsafe_call.go +40 -40
- package/linthost/rules_ts_no_unsafe_enum_comparison.go +147 -147
- package/linthost/rules_ts_no_unsafe_member_access.go +29 -29
- package/linthost/rules_ts_no_unsafe_return.go +64 -64
- package/linthost/rules_ts_no_unsafe_unary_minus.go +42 -42
- package/linthost/rules_ts_no_useless_constructor.go +20 -20
- package/linthost/rules_ts_non_nullable_type_assertion_style.go +60 -60
- package/linthost/rules_ts_parameter_properties.go +10 -10
- package/linthost/rules_ts_prefer_find.go +116 -116
- package/linthost/rules_ts_prefer_includes.go +173 -173
- package/linthost/rules_ts_prefer_nullish_coalescing.go +54 -54
- package/linthost/rules_ts_prefer_optional_chain.go +156 -156
- package/linthost/rules_ts_prefer_promise_reject_errors.go +104 -104
- package/linthost/rules_ts_prefer_readonly.go +31 -31
- package/linthost/rules_ts_prefer_reduce_type_parameter.go +74 -74
- package/linthost/rules_ts_prefer_regexp_exec.go +69 -69
- package/linthost/rules_ts_prefer_return_this_type.go +79 -79
- package/linthost/rules_ts_prefer_string_starts_ends_with.go +237 -237
- package/linthost/rules_ts_promise_function_async.go +41 -41
- package/linthost/rules_ts_related_getter_setter_pairs.go +61 -61
- package/linthost/rules_ts_require_array_sort_compare.go +58 -58
- package/linthost/rules_ts_restrict_plus_operands.go +82 -82
- package/linthost/rules_ts_restrict_template_expressions.go +50 -50
- package/linthost/rules_ts_simple.go +249 -249
- package/linthost/rules_ts_sort_type_constituents.go +88 -88
- package/linthost/rules_ts_strict_boolean_expressions.go +103 -103
- package/linthost/rules_ts_switch_exhaustiveness_check.go +90 -90
- package/linthost/rules_ts_unbound_method.go +65 -65
- package/linthost/rules_tsdoc.go +88 -88
- package/linthost/rules_unicorn_better_regex.go +1 -1
- package/linthost/rules_unicorn_catch_error_name.go +19 -19
- package/linthost/rules_unicorn_consistent_assert.go +20 -20
- package/linthost/rules_unicorn_consistent_date_clone.go +27 -27
- package/linthost/rules_unicorn_consistent_destructuring.go +3 -3
- package/linthost/rules_unicorn_consistent_empty_array_spread.go +34 -34
- package/linthost/rules_unicorn_consistent_existence_index_check.go +42 -42
- package/linthost/rules_unicorn_consistent_function_scoping.go +3 -3
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +3 -3
- package/linthost/rules_unicorn_custom_error_definition.go +92 -92
- package/linthost/rules_unicorn_empty_brace_spaces.go +47 -47
- package/linthost/rules_unicorn_error_message.go +34 -34
- package/linthost/rules_unicorn_escape_case.go +14 -14
- package/linthost/rules_unicorn_expiring_todo_comments.go +36 -36
- package/linthost/rules_unicorn_explicit_length_check.go +51 -51
- package/linthost/rules_unicorn_filename_case.go +1 -1
- package/linthost/rules_unicorn_import_style.go +1 -1
- package/linthost/rules_unicorn_isolated_functions.go +2 -2
- package/linthost/rules_unicorn_new_for_builtins.go +49 -49
- package/linthost/rules_unicorn_no_abusive_eslint_disable.go +31 -31
- package/linthost/rules_unicorn_no_accessor_recursion.go +69 -69
- package/linthost/rules_unicorn_no_anonymous_default_export.go +40 -40
- package/linthost/rules_unicorn_no_array_callback_reference.go +33 -33
- package/linthost/rules_unicorn_no_array_for_each.go +14 -14
- package/linthost/rules_unicorn_no_array_method_this_argument.go +29 -29
- package/linthost/rules_unicorn_no_array_reduce.go +15 -15
- package/linthost/rules_unicorn_no_array_reverse.go +17 -17
- package/linthost/rules_unicorn_no_array_sort.go +17 -17
- package/linthost/rules_unicorn_no_await_expression_member.go +12 -12
- package/linthost/rules_unicorn_no_await_in_promise_methods.go +38 -38
- package/linthost/rules_unicorn_no_console_spaces.go +30 -30
- package/linthost/rules_unicorn_no_document_cookie.go +13 -13
- package/linthost/rules_unicorn_no_empty_file.go +16 -16
- package/linthost/rules_unicorn_no_for_loop.go +58 -58
- package/linthost/rules_unicorn_no_hex_escape.go +11 -11
- package/linthost/rules_unicorn_no_immediate_mutation.go +55 -55
- package/linthost/rules_unicorn_no_instanceof_builtins.go +34 -34
- package/linthost/rules_unicorn_no_invalid_fetch_options.go +52 -52
- package/linthost/rules_unicorn_no_invalid_remove_event_listener.go +25 -25
- package/linthost/rules_unicorn_no_keyword_prefix.go +56 -56
- package/linthost/rules_unicorn_no_lonely_if.go +21 -21
- package/linthost/rules_unicorn_no_magic_array_flat_depth.go +22 -22
- package/linthost/rules_unicorn_no_named_default.go +25 -25
- package/linthost/rules_unicorn_no_negated_condition.go +54 -54
- package/linthost/rules_unicorn_no_negation_in_equality_check.go +23 -23
- package/linthost/rules_unicorn_no_nested_ternary.go +15 -15
- package/linthost/rules_unicorn_no_new_array.go +5 -5
- package/linthost/rules_unicorn_no_new_buffer.go +5 -5
- package/linthost/rules_unicorn_no_null.go +2 -2
- package/linthost/rules_unicorn_no_object_as_default_parameter.go +19 -19
- package/linthost/rules_unicorn_no_process_exit.go +17 -17
- package/linthost/rules_unicorn_no_single_promise_in_promise_methods.go +29 -29
- package/linthost/rules_unicorn_no_static_only_class.go +27 -27
- package/linthost/rules_unicorn_no_thenable.go +47 -47
- package/linthost/rules_unicorn_no_this_assignment.go +10 -10
- package/linthost/rules_unicorn_no_typeof_undefined.go +31 -31
- package/linthost/rules_unicorn_no_unnecessary_array_flat_depth.go +23 -23
- package/linthost/rules_unicorn_no_unnecessary_array_splice_count.go +37 -37
- package/linthost/rules_unicorn_no_unnecessary_await.go +24 -24
- package/linthost/rules_unicorn_no_unnecessary_slice_end.go +22 -22
- package/linthost/rules_unicorn_no_unreadable_array_destructuring.go +67 -67
- package/linthost/rules_unicorn_no_unreadable_iife.go +22 -22
- package/linthost/rules_unicorn_no_useless_collection_argument.go +34 -34
- package/linthost/rules_unicorn_no_useless_error_capture_stack_trace.go +18 -18
- package/linthost/rules_unicorn_no_useless_fallback_in_spread.go +49 -49
- package/linthost/rules_unicorn_no_useless_iterator_to_array.go +41 -41
- package/linthost/rules_unicorn_no_useless_length_check.go +82 -82
- package/linthost/rules_unicorn_no_useless_promise_resolve_reject.go +42 -42
- package/linthost/rules_unicorn_no_useless_spread.go +38 -38
- package/linthost/rules_unicorn_no_useless_switch_case.go +29 -29
- package/linthost/rules_unicorn_no_useless_undefined.go +25 -25
- package/linthost/rules_unicorn_no_zero_fractions.go +17 -17
- package/linthost/rules_unicorn_number_literal_case.go +33 -33
- package/linthost/rules_unicorn_numeric_separators_style.go +36 -36
- package/linthost/rules_unicorn_prefer_add_event_listener.go +30 -30
- package/linthost/rules_unicorn_prefer_array_find.go +27 -27
- package/linthost/rules_unicorn_prefer_array_flat.go +25 -25
- package/linthost/rules_unicorn_prefer_array_flat_map.go +37 -37
- package/linthost/rules_unicorn_prefer_array_index_of.go +108 -108
- package/linthost/rules_unicorn_prefer_array_some.go +43 -43
- package/linthost/rules_unicorn_prefer_at.go +42 -42
- package/linthost/rules_unicorn_prefer_bigint_literals.go +42 -42
- package/linthost/rules_unicorn_prefer_blob_reading_methods.go +15 -15
- package/linthost/rules_unicorn_prefer_class_fields.go +57 -57
- package/linthost/rules_unicorn_prefer_classlist_toggle.go +76 -76
- package/linthost/rules_unicorn_prefer_code_point.go +21 -21
- package/linthost/rules_unicorn_prefer_date_now.go +47 -47
- package/linthost/rules_unicorn_prefer_default_parameters.go +96 -96
- package/linthost/rules_unicorn_prefer_dom_node_append.go +14 -14
- package/linthost/rules_unicorn_prefer_dom_node_dataset.go +27 -27
- package/linthost/rules_unicorn_prefer_dom_node_remove.go +17 -17
- package/linthost/rules_unicorn_prefer_dom_node_text_content.go +11 -11
- package/linthost/rules_unicorn_prefer_event_target.go +10 -10
- package/linthost/rules_unicorn_prefer_export_from.go +99 -99
- package/linthost/rules_unicorn_prefer_global_this.go +91 -91
- package/linthost/rules_unicorn_prefer_import_meta_properties.go +38 -38
- package/linthost/rules_unicorn_prefer_includes.go +108 -108
- package/linthost/rules_unicorn_prefer_json_parse_buffer.go +37 -37
- package/linthost/rules_unicorn_prefer_keyboard_event_key.go +10 -10
- package/linthost/rules_unicorn_prefer_logical_operator_over_ternary.go +20 -20
- package/linthost/rules_unicorn_prefer_math_min_max.go +33 -33
- package/linthost/rules_unicorn_prefer_math_trunc.go +32 -32
- package/linthost/rules_unicorn_prefer_modern_dom_apis.go +18 -18
- package/linthost/rules_unicorn_prefer_modern_math_apis.go +38 -38
- package/linthost/rules_unicorn_prefer_module.go +22 -22
- package/linthost/rules_unicorn_prefer_native_coercion_functions.go +73 -73
- package/linthost/rules_unicorn_prefer_negative_index.go +46 -46
- package/linthost/rules_unicorn_prefer_node_protocol.go +90 -90
- package/linthost/rules_unicorn_prefer_number_properties.go +57 -57
- package/linthost/rules_unicorn_prefer_object_from_entries.go +26 -26
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
- package/linthost/rules_unicorn_prefer_prototype_methods.go +25 -25
- package/linthost/rules_unicorn_prefer_query_selector.go +25 -25
- package/linthost/rules_unicorn_prefer_reflect_apply.go +10 -10
- package/linthost/rules_unicorn_prefer_regexp_test_rule.go +47 -47
- package/linthost/rules_unicorn_prefer_response_static_json.go +26 -26
- package/linthost/rules_unicorn_prefer_set_has.go +16 -16
- package/linthost/rules_unicorn_prefer_set_size.go +22 -22
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +3 -3
- package/linthost/rules_unicorn_prefer_single_call.go +59 -59
- package/linthost/rules_unicorn_prefer_spread.go +24 -24
- package/linthost/rules_unicorn_prefer_string_raw.go +12 -12
- package/linthost/rules_unicorn_prefer_string_replace_all.go +46 -46
- package/linthost/rules_unicorn_prefer_string_slice.go +17 -17
- package/linthost/rules_unicorn_prefer_string_starts_ends_with.go +104 -104
- package/linthost/rules_unicorn_prefer_string_trim_start_end.go +21 -21
- package/linthost/rules_unicorn_prefer_structured_clone.go +36 -36
- package/linthost/rules_unicorn_prefer_switch.go +65 -65
- package/linthost/rules_unicorn_prefer_ternary.go +35 -35
- package/linthost/rules_unicorn_prefer_top_level_await.go +34 -34
- package/linthost/rules_unicorn_prefer_type_error.go +84 -84
- package/linthost/rules_unicorn_prevent_abbreviations.go +68 -68
- package/linthost/rules_unicorn_relative_url_style.go +19 -19
- package/linthost/rules_unicorn_require_array_join_separator.go +18 -18
- package/linthost/rules_unicorn_require_module_attributes.go +25 -25
- package/linthost/rules_unicorn_require_module_specifiers.go +28 -28
- package/linthost/rules_unicorn_require_number_to_fixed_digits_argument.go +18 -18
- package/linthost/rules_unicorn_require_post_message_target_origin.go +18 -18
- package/linthost/rules_unicorn_string_content.go +1 -1
- package/linthost/rules_unicorn_switch_case_braces.go +16 -16
- package/linthost/rules_unicorn_switch_case_break_position.go +3 -3
- package/linthost/rules_unicorn_template_indent.go +1 -1
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +36 -36
- package/linthost/rules_unicorn_throw_new_error.go +30 -30
- package/linthost/rules_var.go +273 -3
- package/package.json +3 -3
- package/src/defaultFormat.ts +11 -7
- package/src/structures/ITtscLintConfig.ts +5 -5
- package/src/structures/TtscLintRuleSetting.ts +19 -19
- package/src/structures/{ITtscLintFormat.ts → format/ITtscLintFormat.ts} +48 -75
- package/src/structures/format/ITtscLintFormatJsDoc.ts +16 -0
- package/src/structures/format/ITtscLintFormatSortImports.ts +62 -0
- package/src/structures/format/index.ts +3 -0
- package/src/structures/index.ts +1 -1
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +20 -31
- package/src/structures/rules/ITtscLintBoundariesRules.ts +32 -36
- package/src/structures/rules/ITtscLintContributorRules.ts +6 -7
- package/src/structures/rules/ITtscLintCoreRules.ts +380 -408
- package/src/structures/rules/ITtscLintCypressRuleOptions.ts +5 -5
- package/src/structures/rules/ITtscLintCypressRules.ts +34 -38
- package/src/structures/rules/ITtscLintFunctionalRuleOptions.ts +28 -41
- package/src/structures/rules/ITtscLintFunctionalRules.ts +51 -56
- package/src/structures/rules/ITtscLintJestRules.ts +63 -74
- package/src/structures/rules/ITtscLintJsdocRules.ts +47 -54
- package/src/structures/rules/ITtscLintJsxA11yRules.ts +94 -102
- package/src/structures/rules/ITtscLintNextjsRules.ts +50 -55
- package/src/structures/rules/ITtscLintPlaywrightRules.ts +104 -122
- package/src/structures/rules/ITtscLintPromiseRules.ts +62 -71
- package/src/structures/rules/ITtscLintReactPerfRuleOptions.ts +4 -4
- package/src/structures/rules/ITtscLintReactPerfRules.ts +17 -19
- package/src/structures/rules/ITtscLintReactRuleOptions.ts +8 -10
- package/src/structures/rules/ITtscLintReactRules.ts +91 -104
- package/src/structures/rules/ITtscLintRegexpRules.ts +46 -54
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +7 -8
- package/src/structures/rules/ITtscLintSecurityRules.ts +45 -52
- package/src/structures/rules/ITtscLintSolidRules.ts +53 -56
- package/src/structures/rules/ITtscLintStorybookRuleOptions.ts +3 -4
- package/src/structures/rules/ITtscLintStorybookRules.ts +60 -69
- package/src/structures/rules/ITtscLintTanstackQueryRules.ts +31 -37
- package/src/structures/rules/ITtscLintTestingLibraryRuleOptions.ts +2 -3
- package/src/structures/rules/ITtscLintTestingLibraryRules.ts +102 -119
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +460 -518
- package/src/structures/rules/ITtscLintVitestRules.ts +43 -51
- package/lib/structures/ITtscLintFormat.js.map +0 -1
- /package/lib/structures/{ITtscLintFormat.js → format/ITtscLintFormat.js} +0 -0
|
@@ -1,584 +1,584 @@
|
|
|
1
1
|
package linthost
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
"strconv"
|
|
5
|
+
"strings"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
type jsxAttr struct {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
node *shimast.Node
|
|
12
|
+
value string
|
|
13
|
+
known bool
|
|
14
|
+
boolean bool
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
type jsxElementInfo struct {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
opening *shimast.Node
|
|
19
|
+
tag string
|
|
20
|
+
attrs map[string]jsxAttr
|
|
21
|
+
children *shimast.NodeList
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
func jsxElementFromNode(node *shimast.Node) jsxElementInfo {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
25
|
+
info := jsxElementInfo{attrs: map[string]jsxAttr{}}
|
|
26
|
+
if node == nil {
|
|
27
|
+
return info
|
|
28
|
+
}
|
|
29
|
+
switch node.Kind {
|
|
30
|
+
case shimast.KindJsxElement:
|
|
31
|
+
el := node.AsJsxElement()
|
|
32
|
+
if el == nil || el.OpeningElement == nil {
|
|
33
|
+
return info
|
|
34
|
+
}
|
|
35
|
+
info.opening = el.OpeningElement.AsNode()
|
|
36
|
+
info.tag = jsxTagName(el.OpeningElement.TagName())
|
|
37
|
+
info.attrs = jsxAttrs(el.OpeningElement.Attributes())
|
|
38
|
+
info.children = el.Children
|
|
39
|
+
case shimast.KindJsxSelfClosingElement:
|
|
40
|
+
el := node.AsJsxSelfClosingElement()
|
|
41
|
+
if el == nil {
|
|
42
|
+
return info
|
|
43
|
+
}
|
|
44
|
+
info.opening = node
|
|
45
|
+
info.tag = jsxTagName(el.TagName)
|
|
46
|
+
info.attrs = jsxAttrs(el.Attributes)
|
|
47
|
+
case shimast.KindJsxOpeningElement:
|
|
48
|
+
el := node.AsJsxOpeningElement()
|
|
49
|
+
if el == nil {
|
|
50
|
+
return info
|
|
51
|
+
}
|
|
52
|
+
info.opening = node
|
|
53
|
+
info.tag = jsxTagName(el.TagName)
|
|
54
|
+
info.attrs = jsxAttrs(el.Attributes)
|
|
55
|
+
}
|
|
56
|
+
return info
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
func jsxTagName(node *shimast.Node) string {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
if node == nil {
|
|
61
|
+
return ""
|
|
62
|
+
}
|
|
63
|
+
if name := identifierText(node); name != "" {
|
|
64
|
+
return name
|
|
65
|
+
}
|
|
66
|
+
return ""
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
func jsxAttrs(node *shimast.Node) map[string]jsxAttr {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
70
|
+
out := map[string]jsxAttr{}
|
|
71
|
+
if node == nil {
|
|
72
|
+
return out
|
|
73
|
+
}
|
|
74
|
+
attrs := node.AsJsxAttributes()
|
|
75
|
+
if attrs == nil || attrs.Properties == nil {
|
|
76
|
+
return out
|
|
77
|
+
}
|
|
78
|
+
for _, prop := range attrs.Properties.Nodes {
|
|
79
|
+
attr := prop.AsJsxAttribute()
|
|
80
|
+
if attr == nil || attr.Name() == nil {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
name := jsxAttrName(attr.Name())
|
|
84
|
+
if name == "" {
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
out[name] = jsxAttr{
|
|
88
|
+
node: prop,
|
|
89
|
+
value: jsxAttrValue(attr.Initializer),
|
|
90
|
+
known: jsxAttrValueKnown(attr.Initializer),
|
|
91
|
+
boolean: attr.Initializer == nil,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return out
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
func jsxAttrName(node *shimast.Node) string {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
98
|
+
if node == nil {
|
|
99
|
+
return ""
|
|
100
|
+
}
|
|
101
|
+
if name := identifierText(node); name != "" {
|
|
102
|
+
return name
|
|
103
|
+
}
|
|
104
|
+
if node.Kind == shimast.KindJsxNamespacedName {
|
|
105
|
+
ns := node.AsJsxNamespacedName()
|
|
106
|
+
if ns != nil {
|
|
107
|
+
left := identifierText(ns.Namespace)
|
|
108
|
+
right := identifierText(ns.Name())
|
|
109
|
+
if left != "" && right != "" {
|
|
110
|
+
return left + ":" + right
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return ""
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
func jsxAttrValue(node *shimast.Node) string {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
118
|
+
if node == nil {
|
|
119
|
+
return "true"
|
|
120
|
+
}
|
|
121
|
+
if text := stringLiteralText(node); text != "" || node.Kind == shimast.KindStringLiteral || node.Kind == shimast.KindNoSubstitutionTemplateLiteral {
|
|
122
|
+
return text
|
|
123
|
+
}
|
|
124
|
+
if node.Kind == shimast.KindJsxExpression {
|
|
125
|
+
expr := node.AsJsxExpression()
|
|
126
|
+
if expr == nil || expr.Expression == nil {
|
|
127
|
+
return ""
|
|
128
|
+
}
|
|
129
|
+
if text := stringLiteralText(expr.Expression); text != "" || expr.Expression.Kind == shimast.KindStringLiteral || expr.Expression.Kind == shimast.KindNoSubstitutionTemplateLiteral {
|
|
130
|
+
return text
|
|
131
|
+
}
|
|
132
|
+
if value, ok := isLiteralBoolean(expr.Expression); ok {
|
|
133
|
+
if value {
|
|
134
|
+
return "true"
|
|
135
|
+
}
|
|
136
|
+
return "false"
|
|
137
|
+
}
|
|
138
|
+
if expr.Expression.Kind == shimast.KindNumericLiteral {
|
|
139
|
+
return numericLiteralText(expr.Expression)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return ""
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
func jsxAttrValueKnown(node *shimast.Node) bool {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
146
|
+
if node == nil {
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
switch node.Kind {
|
|
150
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
151
|
+
return true
|
|
152
|
+
case shimast.KindJsxExpression:
|
|
153
|
+
expr := node.AsJsxExpression()
|
|
154
|
+
if expr == nil || expr.Expression == nil {
|
|
155
|
+
return true
|
|
156
|
+
}
|
|
157
|
+
switch expr.Expression.Kind {
|
|
158
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral, shimast.KindTrueKeyword, shimast.KindFalseKeyword, shimast.KindNumericLiteral:
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
func jsxHasAttr(attrs map[string]jsxAttr, names ...string) bool {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
166
|
+
for _, name := range names {
|
|
167
|
+
if _, ok := attrs[name]; ok {
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return false
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
func jsxKnownAttr(attrs map[string]jsxAttr, name string) (jsxAttr, bool) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
attr, ok := attrs[name]
|
|
176
|
+
if !ok || !attr.known {
|
|
177
|
+
return jsxAttr{}, false
|
|
178
|
+
}
|
|
179
|
+
return attr, true
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
func jsxBoolAttr(attrs map[string]jsxAttr, name string) (bool, bool) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
183
|
+
attr, ok := jsxKnownAttr(attrs, name)
|
|
184
|
+
if !ok {
|
|
185
|
+
return false, false
|
|
186
|
+
}
|
|
187
|
+
if attr.boolean {
|
|
188
|
+
return true, true
|
|
189
|
+
}
|
|
190
|
+
switch strings.ToLower(strings.TrimSpace(attr.value)) {
|
|
191
|
+
case "true":
|
|
192
|
+
return true, true
|
|
193
|
+
case "false":
|
|
194
|
+
return false, true
|
|
195
|
+
}
|
|
196
|
+
return false, false
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
func jsxNumericAttr(attrs map[string]jsxAttr, names ...string) (int, bool) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
200
|
+
for _, name := range names {
|
|
201
|
+
attr, ok := jsxKnownAttr(attrs, name)
|
|
202
|
+
if !ok || strings.TrimSpace(attr.value) == "" {
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
value, err := strconv.Atoi(strings.TrimSpace(attr.value))
|
|
206
|
+
if err == nil {
|
|
207
|
+
return value, true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return 0, false
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
func jsxHasAccessibleLabel(info jsxElementInfo) bool {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
214
|
+
if attr, ok := jsxKnownAttr(info.attrs, "aria-label"); ok && strings.TrimSpace(attr.value) != "" {
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
if attr, ok := jsxKnownAttr(info.attrs, "aria-labelledby"); ok && strings.TrimSpace(attr.value) != "" {
|
|
218
|
+
return true
|
|
219
|
+
}
|
|
220
|
+
if attr, ok := jsxKnownAttr(info.attrs, "alt"); ok && strings.TrimSpace(attr.value) != "" {
|
|
221
|
+
return true
|
|
222
|
+
}
|
|
223
|
+
if attr, ok := jsxKnownAttr(info.attrs, "title"); ok && strings.TrimSpace(attr.value) != "" {
|
|
224
|
+
return true
|
|
225
|
+
}
|
|
226
|
+
return jsxChildrenHaveAccessibleContent(info.children)
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
func jsxChildrenHaveAccessibleContent(children *shimast.NodeList) bool {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
230
|
+
if children == nil {
|
|
231
|
+
return false
|
|
232
|
+
}
|
|
233
|
+
for _, child := range children.Nodes {
|
|
234
|
+
if child == nil {
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
switch child.Kind {
|
|
238
|
+
case shimast.KindJsxText:
|
|
239
|
+
text := child.AsJsxText()
|
|
240
|
+
if text != nil && !text.ContainsOnlyTriviaWhiteSpaces && strings.TrimSpace(text.Text) != "" {
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
243
|
+
case shimast.KindJsxExpression:
|
|
244
|
+
expr := child.AsJsxExpression()
|
|
245
|
+
if expr != nil && expr.Expression != nil {
|
|
246
|
+
if text := stringLiteralText(expr.Expression); strings.TrimSpace(text) != "" {
|
|
247
|
+
return true
|
|
248
|
+
}
|
|
249
|
+
if expr.Expression.Kind != shimast.KindNullKeyword && identifierText(expr.Expression) != "undefined" {
|
|
250
|
+
return true
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
case shimast.KindJsxElement, shimast.KindJsxSelfClosingElement:
|
|
254
|
+
nested := jsxElementFromNode(child)
|
|
255
|
+
if jsxHasAccessibleLabel(nested) {
|
|
256
|
+
return true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return false
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
func jsxHasDescendantControl(children *shimast.NodeList) bool {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
264
|
+
if children == nil {
|
|
265
|
+
return false
|
|
266
|
+
}
|
|
267
|
+
for _, child := range children.Nodes {
|
|
268
|
+
if child == nil {
|
|
269
|
+
continue
|
|
270
|
+
}
|
|
271
|
+
switch child.Kind {
|
|
272
|
+
case shimast.KindJsxElement, shimast.KindJsxSelfClosingElement:
|
|
273
|
+
info := jsxElementFromNode(child)
|
|
274
|
+
if jsxIsFormControl(info) {
|
|
275
|
+
return true
|
|
276
|
+
}
|
|
277
|
+
if jsxHasDescendantControl(info.children) {
|
|
278
|
+
return true
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
func jsxHasTrackCaption(children *shimast.NodeList) bool {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
286
|
+
if children == nil {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
for _, child := range children.Nodes {
|
|
290
|
+
info := jsxElementFromNode(child)
|
|
291
|
+
if info.tag != "track" {
|
|
292
|
+
continue
|
|
293
|
+
}
|
|
294
|
+
attr, ok := jsxKnownAttr(info.attrs, "kind")
|
|
295
|
+
if !ok {
|
|
296
|
+
return true
|
|
297
|
+
}
|
|
298
|
+
kind := strings.ToLower(strings.TrimSpace(attr.value))
|
|
299
|
+
if kind == "captions" || kind == "subtitles" {
|
|
300
|
+
return true
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return false
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
func jsxIsFormControl(info jsxElementInfo) bool {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
307
|
+
switch info.tag {
|
|
308
|
+
case "button", "select", "textarea":
|
|
309
|
+
return true
|
|
310
|
+
case "input":
|
|
311
|
+
if attr, ok := jsxKnownAttr(info.attrs, "type"); ok && strings.EqualFold(attr.value, "hidden") {
|
|
312
|
+
return false
|
|
313
|
+
}
|
|
314
|
+
return true
|
|
315
|
+
}
|
|
316
|
+
return false
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
func jsxIsHidden(info jsxElementInfo) bool {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
320
|
+
if value, ok := jsxBoolAttr(info.attrs, "aria-hidden"); ok && value {
|
|
321
|
+
return true
|
|
322
|
+
}
|
|
323
|
+
_, hidden := info.attrs["hidden"]
|
|
324
|
+
return hidden
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
func jsxHasKeyboardHandler(attrs map[string]jsxAttr) bool {
|
|
328
|
-
|
|
328
|
+
return jsxHasAttr(attrs, "onKeyDown", "onKeyUp", "onKeyPress", "onkeydown", "onkeyup", "onkeypress")
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
func jsxHasMouseOrKeyboardInteraction(attrs map[string]jsxAttr) bool {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
332
|
+
return jsxHasAttr(
|
|
333
|
+
attrs,
|
|
334
|
+
"onClick", "onclick",
|
|
335
|
+
"onMouseDown", "onmousedown",
|
|
336
|
+
"onMouseUp", "onmouseup",
|
|
337
|
+
"onKeyDown", "onkeydown",
|
|
338
|
+
"onKeyUp", "onkeyup",
|
|
339
|
+
"onKeyPress", "onkeypress",
|
|
340
|
+
)
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
func jsxRole(attrs map[string]jsxAttr) (string, bool) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
344
|
+
attr, ok := jsxKnownAttr(attrs, "role")
|
|
345
|
+
if !ok {
|
|
346
|
+
return "", false
|
|
347
|
+
}
|
|
348
|
+
for _, token := range strings.Fields(strings.ToLower(attr.value)) {
|
|
349
|
+
if token != "" {
|
|
350
|
+
return token, true
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return "", true
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
func jsxImplicitRole(info jsxElementInfo) string {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
357
|
+
switch info.tag {
|
|
358
|
+
case "a", "area":
|
|
359
|
+
if jsxHasAttr(info.attrs, "href") {
|
|
360
|
+
return "link"
|
|
361
|
+
}
|
|
362
|
+
case "article":
|
|
363
|
+
return "article"
|
|
364
|
+
case "button":
|
|
365
|
+
return "button"
|
|
366
|
+
case "img":
|
|
367
|
+
return "img"
|
|
368
|
+
case "input":
|
|
369
|
+
if attr, ok := jsxKnownAttr(info.attrs, "type"); ok {
|
|
370
|
+
switch strings.ToLower(attr.value) {
|
|
371
|
+
case "button", "submit", "reset":
|
|
372
|
+
return "button"
|
|
373
|
+
case "checkbox":
|
|
374
|
+
return "checkbox"
|
|
375
|
+
case "radio":
|
|
376
|
+
return "radio"
|
|
377
|
+
case "range":
|
|
378
|
+
return "slider"
|
|
379
|
+
case "email", "password", "search", "tel", "text", "url":
|
|
380
|
+
return "textbox"
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return "textbox"
|
|
384
|
+
case "select":
|
|
385
|
+
return "combobox"
|
|
386
|
+
case "textarea":
|
|
387
|
+
return "textbox"
|
|
388
|
+
case "nav":
|
|
389
|
+
return "navigation"
|
|
390
|
+
case "main":
|
|
391
|
+
return "main"
|
|
392
|
+
case "ul", "ol":
|
|
393
|
+
return "list"
|
|
394
|
+
case "li":
|
|
395
|
+
return "listitem"
|
|
396
|
+
case "table":
|
|
397
|
+
return "table"
|
|
398
|
+
case "tr":
|
|
399
|
+
return "row"
|
|
400
|
+
case "td":
|
|
401
|
+
return "cell"
|
|
402
|
+
case "th":
|
|
403
|
+
return "columnheader"
|
|
404
|
+
case "h1", "h2", "h3", "h4", "h5", "h6":
|
|
405
|
+
return "heading"
|
|
406
|
+
}
|
|
407
|
+
return ""
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
func jsxIsInteractive(info jsxElementInfo) bool {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
411
|
+
if role, ok := jsxRole(info.attrs); ok && jsxInteractiveRoles[role] {
|
|
412
|
+
return true
|
|
413
|
+
}
|
|
414
|
+
switch jsxImplicitRole(info) {
|
|
415
|
+
case "button", "checkbox", "combobox", "link", "radio", "slider", "textbox":
|
|
416
|
+
return true
|
|
417
|
+
}
|
|
418
|
+
return false
|
|
419
419
|
}
|
|
420
420
|
|
|
421
421
|
func jsxIsFocusable(info jsxElementInfo) bool {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
422
|
+
if disabled, ok := jsxBoolAttr(info.attrs, "disabled"); ok && disabled {
|
|
423
|
+
return false
|
|
424
|
+
}
|
|
425
|
+
if value, ok := jsxNumericAttr(info.attrs, "tabIndex", "tabindex"); ok && value >= 0 {
|
|
426
|
+
return true
|
|
427
|
+
}
|
|
428
|
+
switch info.tag {
|
|
429
|
+
case "button", "input", "select", "textarea":
|
|
430
|
+
return true
|
|
431
|
+
case "a", "area":
|
|
432
|
+
return jsxHasAttr(info.attrs, "href")
|
|
433
|
+
}
|
|
434
|
+
return false
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
func jsxIsNonInteractiveElement(tag string) bool {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
438
|
+
switch tag {
|
|
439
|
+
case "article", "aside", "div", "footer", "header", "li", "main", "nav", "ol", "p", "section", "ul",
|
|
440
|
+
"h1", "h2", "h3", "h4", "h5", "h6":
|
|
441
|
+
return true
|
|
442
|
+
}
|
|
443
|
+
return false
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
var jsxA11yRoles = map[string]bool{
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
447
|
+
"alert": true, "alertdialog": true, "application": true, "article": true, "banner": true,
|
|
448
|
+
"button": true, "cell": true, "checkbox": true, "columnheader": true, "combobox": true,
|
|
449
|
+
"complementary": true, "contentinfo": true, "definition": true, "dialog": true, "directory": true,
|
|
450
|
+
"document": true, "feed": true, "figure": true, "form": true, "grid": true, "gridcell": true,
|
|
451
|
+
"group": true, "heading": true, "img": true, "link": true, "list": true, "listbox": true,
|
|
452
|
+
"listitem": true, "log": true, "main": true, "marquee": true, "math": true, "menu": true,
|
|
453
|
+
"menubar": true, "menuitem": true, "menuitemcheckbox": true, "menuitemradio": true,
|
|
454
|
+
"meter": true, "navigation": true, "none": true, "note": true, "option": true, "presentation": true,
|
|
455
|
+
"progressbar": true, "radio": true, "radiogroup": true, "region": true, "row": true,
|
|
456
|
+
"rowgroup": true, "rowheader": true, "scrollbar": true, "search": true, "searchbox": true,
|
|
457
|
+
"separator": true, "slider": true, "spinbutton": true, "status": true, "switch": true,
|
|
458
|
+
"tab": true, "table": true, "tablist": true, "tabpanel": true, "term": true, "textbox": true,
|
|
459
|
+
"timer": true, "toolbar": true, "tooltip": true, "tree": true, "treegrid": true, "treeitem": true,
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
var jsxInteractiveRoles = map[string]bool{
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
463
|
+
"button": true, "checkbox": true, "combobox": true, "gridcell": true, "link": true,
|
|
464
|
+
"listbox": true, "menuitem": true, "menuitemcheckbox": true, "menuitemradio": true,
|
|
465
|
+
"option": true, "radio": true, "scrollbar": true, "searchbox": true, "slider": true,
|
|
466
|
+
"spinbutton": true, "switch": true, "tab": true, "textbox": true, "treeitem": true,
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
var jsxNonInteractiveRoles = map[string]bool{
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
470
|
+
"article": true, "complementary": true, "contentinfo": true, "definition": true,
|
|
471
|
+
"document": true, "feed": true, "figure": true, "group": true, "heading": true,
|
|
472
|
+
"img": true, "list": true, "listitem": true, "main": true, "navigation": true,
|
|
473
|
+
"none": true, "note": true, "presentation": true, "region": true, "row": true,
|
|
474
|
+
"rowgroup": true, "separator": true, "table": true, "term": true, "toolbar": true,
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
var jsxAriaProps = map[string]bool{
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
478
|
+
"aria-activedescendant": true, "aria-atomic": true, "aria-autocomplete": true, "aria-braillelabel": true,
|
|
479
|
+
"aria-brailleroledescription": true, "aria-busy": true, "aria-checked": true, "aria-colcount": true,
|
|
480
|
+
"aria-colindex": true, "aria-colindextext": true, "aria-colspan": true, "aria-controls": true,
|
|
481
|
+
"aria-current": true, "aria-describedby": true, "aria-description": true, "aria-details": true,
|
|
482
|
+
"aria-disabled": true, "aria-dropeffect": true, "aria-errormessage": true, "aria-expanded": true,
|
|
483
|
+
"aria-flowto": true, "aria-grabbed": true, "aria-haspopup": true, "aria-hidden": true,
|
|
484
|
+
"aria-invalid": true, "aria-keyshortcuts": true, "aria-label": true, "aria-labelledby": true,
|
|
485
|
+
"aria-level": true, "aria-live": true, "aria-modal": true, "aria-multiline": true,
|
|
486
|
+
"aria-multiselectable": true, "aria-orientation": true, "aria-owns": true, "aria-placeholder": true,
|
|
487
|
+
"aria-posinset": true, "aria-pressed": true, "aria-readonly": true, "aria-relevant": true,
|
|
488
|
+
"aria-required": true, "aria-roledescription": true, "aria-rowcount": true, "aria-rowindex": true,
|
|
489
|
+
"aria-rowindextext": true, "aria-rowspan": true, "aria-selected": true, "aria-setsize": true,
|
|
490
|
+
"aria-sort": true, "aria-valuemax": true, "aria-valuemin": true, "aria-valuenow": true,
|
|
491
|
+
"aria-valuetext": true,
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
var jsxGlobalAriaProps = map[string]bool{
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
495
|
+
"aria-atomic": true, "aria-busy": true, "aria-controls": true, "aria-current": true,
|
|
496
|
+
"aria-describedby": true, "aria-description": true, "aria-details": true, "aria-disabled": true,
|
|
497
|
+
"aria-dropeffect": true, "aria-errormessage": true, "aria-flowto": true, "aria-grabbed": true,
|
|
498
|
+
"aria-haspopup": true, "aria-hidden": true, "aria-invalid": true, "aria-keyshortcuts": true,
|
|
499
|
+
"aria-label": true, "aria-labelledby": true, "aria-live": true, "aria-owns": true,
|
|
500
|
+
"aria-relevant": true, "aria-roledescription": true,
|
|
501
501
|
}
|
|
502
502
|
|
|
503
503
|
var jsxRoleRequiredProps = map[string][]string{
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
504
|
+
"checkbox": {"aria-checked"},
|
|
505
|
+
"combobox": {"aria-controls", "aria-expanded"},
|
|
506
|
+
"heading": {"aria-level"},
|
|
507
|
+
"menuitemcheckbox": {"aria-checked"},
|
|
508
|
+
"menuitemradio": {"aria-checked"},
|
|
509
|
+
"option": {"aria-selected"},
|
|
510
|
+
"radio": {"aria-checked"},
|
|
511
|
+
"scrollbar": {"aria-controls", "aria-valuenow"},
|
|
512
|
+
"slider": {"aria-valuenow"},
|
|
513
|
+
"spinbutton": {"aria-valuenow"},
|
|
514
|
+
"switch": {"aria-checked"},
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
var jsxRoleSupportedProps = map[string]map[string]bool{
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
518
|
+
"button": {"aria-expanded": true, "aria-pressed": true},
|
|
519
|
+
"checkbox": {"aria-checked": true, "aria-readonly": true},
|
|
520
|
+
"combobox": {"aria-activedescendant": true, "aria-autocomplete": true, "aria-controls": true, "aria-expanded": true, "aria-readonly": true, "aria-required": true},
|
|
521
|
+
"heading": {"aria-level": true},
|
|
522
|
+
"img": {"aria-label": true, "aria-labelledby": true},
|
|
523
|
+
"link": {"aria-expanded": true},
|
|
524
|
+
"radio": {"aria-checked": true},
|
|
525
|
+
"slider": {"aria-orientation": true, "aria-valuemax": true, "aria-valuemin": true, "aria-valuenow": true, "aria-valuetext": true},
|
|
526
|
+
"spinbutton": {"aria-valuemax": true, "aria-valuemin": true, "aria-valuenow": true, "aria-valuetext": true},
|
|
527
|
+
"switch": {"aria-checked": true},
|
|
528
|
+
"tab": {"aria-controls": true, "aria-selected": true},
|
|
529
|
+
"textbox": {"aria-activedescendant": true, "aria-autocomplete": true, "aria-multiline": true, "aria-placeholder": true, "aria-readonly": true, "aria-required": true},
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
var jsxRedundantRoles = map[string]string{
|
|
533
|
-
|
|
534
|
-
|
|
533
|
+
"button": "button", "img": "img", "main": "main", "nav": "navigation", "ul": "list", "ol": "list",
|
|
534
|
+
"li": "listitem", "table": "table", "tr": "row", "td": "cell",
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
var jsxRolePreferredTag = map[string]string{
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
538
|
+
"button": "button", "checkbox": "input", "heading": "h1-h6", "img": "img", "link": "a",
|
|
539
|
+
"list": "ul or ol", "listitem": "li", "main": "main", "navigation": "nav", "radio": "input",
|
|
540
|
+
"table": "table", "textbox": "input or textarea",
|
|
541
541
|
}
|
|
542
542
|
|
|
543
543
|
var jsxAutocompleteTokens = map[string]bool{
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
544
|
+
"on": true, "off": true, "name": true, "honorific-prefix": true, "given-name": true,
|
|
545
|
+
"additional-name": true, "family-name": true, "honorific-suffix": true, "nickname": true,
|
|
546
|
+
"email": true, "username": true, "new-password": true, "current-password": true,
|
|
547
|
+
"one-time-code": true, "organization-title": true, "organization": true,
|
|
548
|
+
"street-address": true, "address-line1": true, "address-line2": true, "address-line3": true,
|
|
549
|
+
"address-level1": true, "address-level2": true, "address-level3": true, "address-level4": true,
|
|
550
|
+
"country": true, "country-name": true, "postal-code": true, "cc-name": true,
|
|
551
|
+
"cc-given-name": true, "cc-additional-name": true, "cc-family-name": true, "cc-number": true,
|
|
552
|
+
"cc-exp": true, "cc-exp-month": true, "cc-exp-year": true, "cc-csc": true, "cc-type": true,
|
|
553
|
+
"transaction-currency": true, "transaction-amount": true, "language": true, "bday": true,
|
|
554
|
+
"bday-day": true, "bday-month": true, "bday-year": true, "sex": true, "tel": true,
|
|
555
|
+
"tel-country-code": true, "tel-national": true, "tel-area-code": true, "tel-local": true,
|
|
556
|
+
"tel-extension": true, "impp": true, "url": true, "photo": true,
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
type jsxA11yAltText struct{}
|
|
560
560
|
|
|
561
561
|
func (jsxA11yAltText) Name() string { return "jsx-a11y/alt-text" }
|
|
562
562
|
func (jsxA11yAltText) Visits() []shimast.Kind {
|
|
563
|
-
|
|
563
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
564
564
|
}
|
|
565
565
|
func (jsxA11yAltText) Check(ctx *Context, node *shimast.Node) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
566
|
+
info := jsxElementFromNode(node)
|
|
567
|
+
switch info.tag {
|
|
568
|
+
case "img", "area":
|
|
569
|
+
if !jsxHasAttr(info.attrs, "alt", "aria-label", "aria-labelledby") {
|
|
570
|
+
ctx.Report(info.opening, "Image-like elements must have alt text.")
|
|
571
|
+
}
|
|
572
|
+
case "input":
|
|
573
|
+
attr, ok := jsxKnownAttr(info.attrs, "type")
|
|
574
|
+
if ok && strings.EqualFold(attr.value, "image") && !jsxHasAttr(info.attrs, "alt", "aria-label", "aria-labelledby") {
|
|
575
|
+
ctx.Report(info.opening, "Image input elements must have alt text.")
|
|
576
|
+
}
|
|
577
|
+
case "object":
|
|
578
|
+
if !jsxHasAccessibleLabel(info) {
|
|
579
|
+
ctx.Report(info.opening, "Object elements must have accessible text.")
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
582
|
}
|
|
583
583
|
|
|
584
584
|
type jsxA11yAnchorHasContent struct{}
|
|
@@ -586,47 +586,47 @@ type jsxA11yAnchorHasContent struct{}
|
|
|
586
586
|
func (jsxA11yAnchorHasContent) Name() string { return "jsx-a11y/anchor-has-content" }
|
|
587
587
|
func (jsxA11yAnchorHasContent) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxElement} }
|
|
588
588
|
func (jsxA11yAnchorHasContent) Check(ctx *Context, node *shimast.Node) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
589
|
+
info := jsxElementFromNode(node)
|
|
590
|
+
if info.tag == "a" && !jsxHasAccessibleLabel(info) {
|
|
591
|
+
ctx.Report(info.opening, "Anchors must have accessible content.")
|
|
592
|
+
}
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
type jsxA11yAnchorIsValid struct{}
|
|
596
596
|
|
|
597
597
|
func (jsxA11yAnchorIsValid) Name() string { return "jsx-a11y/anchor-is-valid" }
|
|
598
598
|
func (jsxA11yAnchorIsValid) Visits() []shimast.Kind {
|
|
599
|
-
|
|
599
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
600
600
|
}
|
|
601
601
|
func (jsxA11yAnchorIsValid) Check(ctx *Context, node *shimast.Node) {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
602
|
+
info := jsxElementFromNode(node)
|
|
603
|
+
if info.tag != "a" {
|
|
604
|
+
return
|
|
605
|
+
}
|
|
606
|
+
href, ok := jsxKnownAttr(info.attrs, "href")
|
|
607
|
+
if !ok {
|
|
608
|
+
ctx.Report(info.opening, "Anchor elements must have a valid href.")
|
|
609
|
+
return
|
|
610
|
+
}
|
|
611
|
+
value := strings.TrimSpace(strings.ToLower(href.value))
|
|
612
|
+
if value == "" || value == "#" || strings.HasPrefix(value, "javascript:") {
|
|
613
|
+
ctx.Report(href.node, "Anchor href must be a valid navigation target.")
|
|
614
|
+
}
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
type jsxA11yAriaActivedescendantHasTabindex struct{}
|
|
618
618
|
|
|
619
619
|
func (jsxA11yAriaActivedescendantHasTabindex) Name() string {
|
|
620
|
-
|
|
620
|
+
return "jsx-a11y/aria-activedescendant-has-tabindex"
|
|
621
621
|
}
|
|
622
622
|
func (jsxA11yAriaActivedescendantHasTabindex) Visits() []shimast.Kind {
|
|
623
|
-
|
|
623
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
624
624
|
}
|
|
625
625
|
func (jsxA11yAriaActivedescendantHasTabindex) Check(ctx *Context, node *shimast.Node) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
626
|
+
info := jsxElementFromNode(node)
|
|
627
|
+
if jsxHasAttr(info.attrs, "aria-activedescendant") && !jsxHasAttr(info.attrs, "tabIndex", "tabindex") {
|
|
628
|
+
ctx.Report(info.opening, "Elements with aria-activedescendant must define tabIndex.")
|
|
629
|
+
}
|
|
630
630
|
}
|
|
631
631
|
|
|
632
632
|
type jsxA11yAriaProps struct{}
|
|
@@ -634,14 +634,14 @@ type jsxA11yAriaProps struct{}
|
|
|
634
634
|
func (jsxA11yAriaProps) Name() string { return "jsx-a11y/aria-props" }
|
|
635
635
|
func (jsxA11yAriaProps) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxAttribute} }
|
|
636
636
|
func (jsxA11yAriaProps) Check(ctx *Context, node *shimast.Node) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
637
|
+
attr := node.AsJsxAttribute()
|
|
638
|
+
if attr == nil || attr.Name() == nil {
|
|
639
|
+
return
|
|
640
|
+
}
|
|
641
|
+
name := strings.ToLower(jsxAttrName(attr.Name()))
|
|
642
|
+
if strings.HasPrefix(name, "aria-") && !jsxAriaProps[name] {
|
|
643
|
+
ctx.Report(node, "Unknown ARIA property "+name+".")
|
|
644
|
+
}
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
type jsxA11yAriaProptypes struct{}
|
|
@@ -649,29 +649,29 @@ type jsxA11yAriaProptypes struct{}
|
|
|
649
649
|
func (jsxA11yAriaProptypes) Name() string { return "jsx-a11y/aria-proptypes" }
|
|
650
650
|
func (jsxA11yAriaProptypes) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxAttribute} }
|
|
651
651
|
func (jsxA11yAriaProptypes) Check(ctx *Context, node *shimast.Node) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
652
|
+
attr := node.AsJsxAttribute()
|
|
653
|
+
if attr == nil || attr.Name() == nil {
|
|
654
|
+
return
|
|
655
|
+
}
|
|
656
|
+
name := strings.ToLower(jsxAttrName(attr.Name()))
|
|
657
|
+
value := strings.ToLower(strings.TrimSpace(jsxAttrValue(attr.Initializer)))
|
|
658
|
+
if !jsxAttrValueKnown(attr.Initializer) || value == "" {
|
|
659
|
+
return
|
|
660
|
+
}
|
|
661
|
+
switch name {
|
|
662
|
+
case "aria-hidden", "aria-disabled", "aria-expanded", "aria-modal", "aria-multiline", "aria-multiselectable", "aria-readonly", "aria-required", "aria-selected":
|
|
663
|
+
if value != "true" && value != "false" {
|
|
664
|
+
ctx.Report(node, name+" must be true or false.")
|
|
665
|
+
}
|
|
666
|
+
case "aria-checked", "aria-pressed":
|
|
667
|
+
if value != "true" && value != "false" && value != "mixed" {
|
|
668
|
+
ctx.Report(node, name+" must be true, false, or mixed.")
|
|
669
|
+
}
|
|
670
|
+
case "aria-level", "aria-valuemax", "aria-valuemin", "aria-valuenow", "aria-posinset", "aria-setsize", "aria-colcount", "aria-colindex", "aria-colspan", "aria-rowcount", "aria-rowindex", "aria-rowspan":
|
|
671
|
+
if _, err := strconv.Atoi(value); err != nil {
|
|
672
|
+
ctx.Report(node, name+" must be numeric.")
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
675
|
}
|
|
676
676
|
|
|
677
677
|
type jsxA11yAriaRole struct{}
|
|
@@ -679,189 +679,189 @@ type jsxA11yAriaRole struct{}
|
|
|
679
679
|
func (jsxA11yAriaRole) Name() string { return "jsx-a11y/aria-role" }
|
|
680
680
|
func (jsxA11yAriaRole) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxAttribute} }
|
|
681
681
|
func (jsxA11yAriaRole) Check(ctx *Context, node *shimast.Node) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
682
|
+
attr := node.AsJsxAttribute()
|
|
683
|
+
if attr == nil || attr.Name() == nil || jsxAttrName(attr.Name()) != "role" || !jsxAttrValueKnown(attr.Initializer) {
|
|
684
|
+
return
|
|
685
|
+
}
|
|
686
|
+
tokens := strings.Fields(strings.ToLower(jsxAttrValue(attr.Initializer)))
|
|
687
|
+
if len(tokens) == 0 {
|
|
688
|
+
ctx.Report(node, "Role must not be empty.")
|
|
689
|
+
return
|
|
690
|
+
}
|
|
691
|
+
for _, token := range tokens {
|
|
692
|
+
if jsxA11yRoles[token] {
|
|
693
|
+
return
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
ctx.Report(node, "Role must be a valid ARIA role.")
|
|
697
697
|
}
|
|
698
698
|
|
|
699
699
|
type jsxA11yAriaUnsupportedElements struct{}
|
|
700
700
|
|
|
701
701
|
func (jsxA11yAriaUnsupportedElements) Name() string { return "jsx-a11y/aria-unsupported-elements" }
|
|
702
702
|
func (jsxA11yAriaUnsupportedElements) Visits() []shimast.Kind {
|
|
703
|
-
|
|
703
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
704
704
|
}
|
|
705
705
|
func (jsxA11yAriaUnsupportedElements) Check(ctx *Context, node *shimast.Node) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
706
|
+
info := jsxElementFromNode(node)
|
|
707
|
+
switch info.tag {
|
|
708
|
+
case "meta", "html", "script", "style":
|
|
709
|
+
if jsxHasAttr(info.attrs, "role") {
|
|
710
|
+
ctx.Report(info.opening, "This element does not support ARIA roles.")
|
|
711
|
+
return
|
|
712
|
+
}
|
|
713
|
+
for name := range info.attrs {
|
|
714
|
+
if strings.HasPrefix(strings.ToLower(name), "aria-") {
|
|
715
|
+
ctx.Report(info.opening, "This element does not support ARIA attributes.")
|
|
716
|
+
return
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
720
|
}
|
|
721
721
|
|
|
722
722
|
type jsxA11yAutocompleteValid struct{}
|
|
723
723
|
|
|
724
724
|
func (jsxA11yAutocompleteValid) Name() string { return "jsx-a11y/autocomplete-valid" }
|
|
725
725
|
func (jsxA11yAutocompleteValid) Visits() []shimast.Kind {
|
|
726
|
-
|
|
726
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
727
727
|
}
|
|
728
728
|
func (jsxA11yAutocompleteValid) Check(ctx *Context, node *shimast.Node) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
729
|
+
info := jsxElementFromNode(node)
|
|
730
|
+
attr, ok := jsxKnownAttr(info.attrs, "autoComplete")
|
|
731
|
+
if !ok {
|
|
732
|
+
attr, ok = jsxKnownAttr(info.attrs, "autocomplete")
|
|
733
|
+
}
|
|
734
|
+
if !ok || strings.TrimSpace(attr.value) == "" {
|
|
735
|
+
return
|
|
736
|
+
}
|
|
737
|
+
for _, token := range strings.Fields(strings.ToLower(attr.value)) {
|
|
738
|
+
if strings.HasPrefix(token, "section-") || token == "shipping" || token == "billing" || token == "home" || token == "work" || token == "mobile" || token == "fax" || token == "pager" {
|
|
739
|
+
continue
|
|
740
|
+
}
|
|
741
|
+
if !jsxAutocompleteTokens[token] {
|
|
742
|
+
ctx.Report(attr.node, "autocomplete contains an invalid token.")
|
|
743
|
+
return
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
746
|
}
|
|
747
747
|
|
|
748
748
|
type jsxA11yClickEventsHaveKeyEvents struct{}
|
|
749
749
|
|
|
750
750
|
func (jsxA11yClickEventsHaveKeyEvents) Name() string { return "jsx-a11y/click-events-have-key-events" }
|
|
751
751
|
func (jsxA11yClickEventsHaveKeyEvents) Visits() []shimast.Kind {
|
|
752
|
-
|
|
752
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
753
753
|
}
|
|
754
754
|
func (jsxA11yClickEventsHaveKeyEvents) Check(ctx *Context, node *shimast.Node) {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
755
|
+
info := jsxElementFromNode(node)
|
|
756
|
+
if !jsxHasAttr(info.attrs, "onClick", "onclick") || jsxIsHidden(info) || jsxIsInteractive(info) || jsxHasKeyboardHandler(info.attrs) {
|
|
757
|
+
return
|
|
758
|
+
}
|
|
759
|
+
ctx.Report(info.opening, "Clickable non-interactive elements must also handle keyboard events.")
|
|
760
760
|
}
|
|
761
761
|
|
|
762
762
|
type jsxA11yControlHasAssociatedLabel struct{}
|
|
763
763
|
|
|
764
764
|
func (jsxA11yControlHasAssociatedLabel) Name() string { return "jsx-a11y/control-has-associated-label" }
|
|
765
765
|
func (jsxA11yControlHasAssociatedLabel) Visits() []shimast.Kind {
|
|
766
|
-
|
|
766
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
767
767
|
}
|
|
768
768
|
func (jsxA11yControlHasAssociatedLabel) Check(ctx *Context, node *shimast.Node) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
769
|
+
info := jsxElementFromNode(node)
|
|
770
|
+
if (jsxIsFormControl(info) || jsxIsInteractive(info)) && !jsxHasAccessibleLabel(info) {
|
|
771
|
+
ctx.Report(info.opening, "Interactive controls must have an accessible label.")
|
|
772
|
+
}
|
|
773
773
|
}
|
|
774
774
|
|
|
775
775
|
type jsxA11yHeadingHasContent struct{}
|
|
776
776
|
|
|
777
777
|
func (jsxA11yHeadingHasContent) Name() string { return "jsx-a11y/heading-has-content" }
|
|
778
778
|
func (jsxA11yHeadingHasContent) Visits() []shimast.Kind {
|
|
779
|
-
|
|
779
|
+
return []shimast.Kind{shimast.KindJsxElement}
|
|
780
780
|
}
|
|
781
781
|
func (jsxA11yHeadingHasContent) Check(ctx *Context, node *shimast.Node) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
782
|
+
info := jsxElementFromNode(node)
|
|
783
|
+
switch info.tag {
|
|
784
|
+
case "h1", "h2", "h3", "h4", "h5", "h6":
|
|
785
|
+
if !jsxHasAccessibleLabel(info) {
|
|
786
|
+
ctx.Report(info.opening, "Headings must have accessible content.")
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
789
|
}
|
|
790
790
|
|
|
791
791
|
type jsxA11yHtmlHasLang struct{}
|
|
792
792
|
|
|
793
793
|
func (jsxA11yHtmlHasLang) Name() string { return "jsx-a11y/html-has-lang" }
|
|
794
794
|
func (jsxA11yHtmlHasLang) Visits() []shimast.Kind {
|
|
795
|
-
|
|
795
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
796
796
|
}
|
|
797
797
|
func (jsxA11yHtmlHasLang) Check(ctx *Context, node *shimast.Node) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
798
|
+
info := jsxElementFromNode(node)
|
|
799
|
+
if info.tag == "html" && !jsxHasAttr(info.attrs, "lang") {
|
|
800
|
+
ctx.Report(info.opening, "The html element must have a lang attribute.")
|
|
801
|
+
}
|
|
802
802
|
}
|
|
803
803
|
|
|
804
804
|
type jsxA11yIframeHasTitle struct{}
|
|
805
805
|
|
|
806
806
|
func (jsxA11yIframeHasTitle) Name() string { return "jsx-a11y/iframe-has-title" }
|
|
807
807
|
func (jsxA11yIframeHasTitle) Visits() []shimast.Kind {
|
|
808
|
-
|
|
808
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
809
809
|
}
|
|
810
810
|
func (jsxA11yIframeHasTitle) Check(ctx *Context, node *shimast.Node) {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
811
|
+
info := jsxElementFromNode(node)
|
|
812
|
+
if info.tag == "iframe" {
|
|
813
|
+
attr, ok := jsxKnownAttr(info.attrs, "title")
|
|
814
|
+
if !ok || strings.TrimSpace(attr.value) == "" {
|
|
815
|
+
ctx.Report(info.opening, "Iframes must have a non-empty title.")
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
type jsxA11yImgRedundantAlt struct{}
|
|
821
821
|
|
|
822
822
|
func (jsxA11yImgRedundantAlt) Name() string { return "jsx-a11y/img-redundant-alt" }
|
|
823
823
|
func (jsxA11yImgRedundantAlt) Visits() []shimast.Kind {
|
|
824
|
-
|
|
824
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
825
825
|
}
|
|
826
826
|
func (jsxA11yImgRedundantAlt) Check(ctx *Context, node *shimast.Node) {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
827
|
+
info := jsxElementFromNode(node)
|
|
828
|
+
if info.tag != "img" {
|
|
829
|
+
return
|
|
830
|
+
}
|
|
831
|
+
attr, ok := jsxKnownAttr(info.attrs, "alt")
|
|
832
|
+
if !ok {
|
|
833
|
+
return
|
|
834
|
+
}
|
|
835
|
+
value := strings.ToLower(attr.value)
|
|
836
|
+
if strings.Contains(value, "image") || strings.Contains(value, "photo") || strings.Contains(value, "picture") {
|
|
837
|
+
ctx.Report(attr.node, "Image alt text should not contain redundant words like image, photo, or picture.")
|
|
838
|
+
}
|
|
839
839
|
}
|
|
840
840
|
|
|
841
841
|
type jsxA11yInteractiveSupportsFocus struct{}
|
|
842
842
|
|
|
843
843
|
func (jsxA11yInteractiveSupportsFocus) Name() string { return "jsx-a11y/interactive-supports-focus" }
|
|
844
844
|
func (jsxA11yInteractiveSupportsFocus) Visits() []shimast.Kind {
|
|
845
|
-
|
|
845
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
846
846
|
}
|
|
847
847
|
func (jsxA11yInteractiveSupportsFocus) Check(ctx *Context, node *shimast.Node) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
848
|
+
info := jsxElementFromNode(node)
|
|
849
|
+
if role, ok := jsxRole(info.attrs); ok && jsxInteractiveRoles[role] && !jsxIsFocusable(info) {
|
|
850
|
+
ctx.Report(info.opening, "Elements with interactive roles must be focusable.")
|
|
851
|
+
}
|
|
852
852
|
}
|
|
853
853
|
|
|
854
854
|
type jsxA11yLabelHasAssociatedControl struct{}
|
|
855
855
|
|
|
856
856
|
func (jsxA11yLabelHasAssociatedControl) Name() string { return "jsx-a11y/label-has-associated-control" }
|
|
857
857
|
func (jsxA11yLabelHasAssociatedControl) Visits() []shimast.Kind {
|
|
858
|
-
|
|
858
|
+
return []shimast.Kind{shimast.KindJsxElement}
|
|
859
859
|
}
|
|
860
860
|
func (jsxA11yLabelHasAssociatedControl) Check(ctx *Context, node *shimast.Node) {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
861
|
+
info := jsxElementFromNode(node)
|
|
862
|
+
if info.tag == "label" && !jsxHasAttr(info.attrs, "htmlFor", "for") && !jsxHasDescendantControl(info.children) {
|
|
863
|
+
ctx.Report(info.opening, "Labels must be associated with a control.")
|
|
864
|
+
}
|
|
865
865
|
}
|
|
866
866
|
|
|
867
867
|
type jsxA11yLabelHasFor struct{}
|
|
@@ -869,26 +869,26 @@ type jsxA11yLabelHasFor struct{}
|
|
|
869
869
|
func (jsxA11yLabelHasFor) Name() string { return "jsx-a11y/label-has-for" }
|
|
870
870
|
func (jsxA11yLabelHasFor) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxElement} }
|
|
871
871
|
func (jsxA11yLabelHasFor) Check(ctx *Context, node *shimast.Node) {
|
|
872
|
-
|
|
872
|
+
jsxA11yLabelHasAssociatedControl{}.Check(ctx, node)
|
|
873
873
|
}
|
|
874
874
|
|
|
875
875
|
type jsxA11yLang struct{}
|
|
876
876
|
|
|
877
877
|
func (jsxA11yLang) Name() string { return "jsx-a11y/lang" }
|
|
878
878
|
func (jsxA11yLang) Visits() []shimast.Kind {
|
|
879
|
-
|
|
879
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
880
880
|
}
|
|
881
881
|
func (jsxA11yLang) Check(ctx *Context, node *shimast.Node) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
882
|
+
info := jsxElementFromNode(node)
|
|
883
|
+
attr, ok := jsxKnownAttr(info.attrs, "lang")
|
|
884
|
+
if !ok {
|
|
885
|
+
return
|
|
886
|
+
}
|
|
887
|
+
value := strings.TrimSpace(attr.value)
|
|
888
|
+
parts := strings.Split(value, "-")
|
|
889
|
+
if value == "" || len(parts[0]) < 2 || len(parts[0]) > 3 {
|
|
890
|
+
ctx.Report(attr.node, "lang must be a valid BCP 47 language tag.")
|
|
891
|
+
}
|
|
892
892
|
}
|
|
893
893
|
|
|
894
894
|
type jsxA11yMediaHasCaption struct{}
|
|
@@ -896,27 +896,27 @@ type jsxA11yMediaHasCaption struct{}
|
|
|
896
896
|
func (jsxA11yMediaHasCaption) Name() string { return "jsx-a11y/media-has-caption" }
|
|
897
897
|
func (jsxA11yMediaHasCaption) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxElement} }
|
|
898
898
|
func (jsxA11yMediaHasCaption) Check(ctx *Context, node *shimast.Node) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
899
|
+
info := jsxElementFromNode(node)
|
|
900
|
+
if (info.tag == "audio" || info.tag == "video") && !jsxHasTrackCaption(info.children) {
|
|
901
|
+
ctx.Report(info.opening, "Media elements must provide caption tracks.")
|
|
902
|
+
}
|
|
903
903
|
}
|
|
904
904
|
|
|
905
905
|
type jsxA11yMouseEventsHaveKeyEvents struct{}
|
|
906
906
|
|
|
907
907
|
func (jsxA11yMouseEventsHaveKeyEvents) Name() string { return "jsx-a11y/mouse-events-have-key-events" }
|
|
908
908
|
func (jsxA11yMouseEventsHaveKeyEvents) Visits() []shimast.Kind {
|
|
909
|
-
|
|
909
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
910
910
|
}
|
|
911
911
|
func (jsxA11yMouseEventsHaveKeyEvents) Check(ctx *Context, node *shimast.Node) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
912
|
+
info := jsxElementFromNode(node)
|
|
913
|
+
if jsxHasAttr(info.attrs, "onMouseOver", "onmouseover") && !jsxHasAttr(info.attrs, "onFocus", "onfocus") {
|
|
914
|
+
ctx.Report(info.opening, "onMouseOver must be paired with onFocus.")
|
|
915
|
+
return
|
|
916
|
+
}
|
|
917
|
+
if jsxHasAttr(info.attrs, "onMouseOut", "onmouseout") && !jsxHasAttr(info.attrs, "onBlur", "onblur") {
|
|
918
|
+
ctx.Report(info.opening, "onMouseOut must be paired with onBlur.")
|
|
919
|
+
}
|
|
920
920
|
}
|
|
921
921
|
|
|
922
922
|
type jsxA11yNoAccessKey struct{}
|
|
@@ -924,23 +924,23 @@ type jsxA11yNoAccessKey struct{}
|
|
|
924
924
|
func (jsxA11yNoAccessKey) Name() string { return "jsx-a11y/no-access-key" }
|
|
925
925
|
func (jsxA11yNoAccessKey) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxAttribute} }
|
|
926
926
|
func (jsxA11yNoAccessKey) Check(ctx *Context, node *shimast.Node) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
927
|
+
attr := node.AsJsxAttribute()
|
|
928
|
+
if attr != nil && attr.Name() != nil && jsxAttrName(attr.Name()) == "accessKey" {
|
|
929
|
+
ctx.Report(node, "Do not use accessKey.")
|
|
930
|
+
}
|
|
931
931
|
}
|
|
932
932
|
|
|
933
933
|
type jsxA11yNoAriaHiddenOnFocusable struct{}
|
|
934
934
|
|
|
935
935
|
func (jsxA11yNoAriaHiddenOnFocusable) Name() string { return "jsx-a11y/no-aria-hidden-on-focusable" }
|
|
936
936
|
func (jsxA11yNoAriaHiddenOnFocusable) Visits() []shimast.Kind {
|
|
937
|
-
|
|
937
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
938
938
|
}
|
|
939
939
|
func (jsxA11yNoAriaHiddenOnFocusable) Check(ctx *Context, node *shimast.Node) {
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
940
|
+
info := jsxElementFromNode(node)
|
|
941
|
+
if hidden, ok := jsxBoolAttr(info.attrs, "aria-hidden"); ok && hidden && jsxIsFocusable(info) {
|
|
942
|
+
ctx.Report(info.opening, "Focusable elements must not be aria-hidden.")
|
|
943
|
+
}
|
|
944
944
|
}
|
|
945
945
|
|
|
946
946
|
type jsxA11yNoAutofocus struct{}
|
|
@@ -948,217 +948,217 @@ type jsxA11yNoAutofocus struct{}
|
|
|
948
948
|
func (jsxA11yNoAutofocus) Name() string { return "jsx-a11y/no-autofocus" }
|
|
949
949
|
func (jsxA11yNoAutofocus) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindJsxAttribute} }
|
|
950
950
|
func (jsxA11yNoAutofocus) Check(ctx *Context, node *shimast.Node) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
951
|
+
attr := node.AsJsxAttribute()
|
|
952
|
+
if attr == nil || attr.Name() == nil {
|
|
953
|
+
return
|
|
954
|
+
}
|
|
955
|
+
name := jsxAttrName(attr.Name())
|
|
956
|
+
if name == "autoFocus" || name == "autofocus" {
|
|
957
|
+
ctx.Report(node, "Do not use autoFocus.")
|
|
958
|
+
}
|
|
959
959
|
}
|
|
960
960
|
|
|
961
961
|
type jsxA11yNoDistractingElements struct{}
|
|
962
962
|
|
|
963
963
|
func (jsxA11yNoDistractingElements) Name() string { return "jsx-a11y/no-distracting-elements" }
|
|
964
964
|
func (jsxA11yNoDistractingElements) Visits() []shimast.Kind {
|
|
965
|
-
|
|
965
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
966
966
|
}
|
|
967
967
|
func (jsxA11yNoDistractingElements) Check(ctx *Context, node *shimast.Node) {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
968
|
+
info := jsxElementFromNode(node)
|
|
969
|
+
if info.tag == "blink" || info.tag == "marquee" {
|
|
970
|
+
ctx.Report(info.opening, "Do not use distracting elements.")
|
|
971
|
+
}
|
|
972
972
|
}
|
|
973
973
|
|
|
974
974
|
type jsxA11yNoInteractiveElementToNoninteractiveRole struct{}
|
|
975
975
|
|
|
976
976
|
func (jsxA11yNoInteractiveElementToNoninteractiveRole) Name() string {
|
|
977
|
-
|
|
977
|
+
return "jsx-a11y/no-interactive-element-to-noninteractive-role"
|
|
978
978
|
}
|
|
979
979
|
func (jsxA11yNoInteractiveElementToNoninteractiveRole) Visits() []shimast.Kind {
|
|
980
|
-
|
|
980
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
981
981
|
}
|
|
982
982
|
func (jsxA11yNoInteractiveElementToNoninteractiveRole) Check(ctx *Context, node *shimast.Node) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
983
|
+
info := jsxElementFromNode(node)
|
|
984
|
+
role, ok := jsxRole(info.attrs)
|
|
985
|
+
if ok && jsxIsInteractive(info) && jsxNonInteractiveRoles[role] {
|
|
986
|
+
ctx.Report(info.opening, "Interactive elements must not use non-interactive roles.")
|
|
987
|
+
}
|
|
988
988
|
}
|
|
989
989
|
|
|
990
990
|
type jsxA11yNoNoninteractiveElementInteractions struct{}
|
|
991
991
|
|
|
992
992
|
func (jsxA11yNoNoninteractiveElementInteractions) Name() string {
|
|
993
|
-
|
|
993
|
+
return "jsx-a11y/no-noninteractive-element-interactions"
|
|
994
994
|
}
|
|
995
995
|
func (jsxA11yNoNoninteractiveElementInteractions) Visits() []shimast.Kind {
|
|
996
|
-
|
|
996
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
997
997
|
}
|
|
998
998
|
func (jsxA11yNoNoninteractiveElementInteractions) Check(ctx *Context, node *shimast.Node) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
999
|
+
info := jsxElementFromNode(node)
|
|
1000
|
+
if jsxIsNonInteractiveElement(info.tag) && jsxHasMouseOrKeyboardInteraction(info.attrs) {
|
|
1001
|
+
ctx.Report(info.opening, "Non-interactive elements must not have interaction handlers.")
|
|
1002
|
+
}
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
1005
1005
|
type jsxA11yNoNoninteractiveElementToInteractiveRole struct{}
|
|
1006
1006
|
|
|
1007
1007
|
func (jsxA11yNoNoninteractiveElementToInteractiveRole) Name() string {
|
|
1008
|
-
|
|
1008
|
+
return "jsx-a11y/no-noninteractive-element-to-interactive-role"
|
|
1009
1009
|
}
|
|
1010
1010
|
func (jsxA11yNoNoninteractiveElementToInteractiveRole) Visits() []shimast.Kind {
|
|
1011
|
-
|
|
1011
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1012
1012
|
}
|
|
1013
1013
|
func (jsxA11yNoNoninteractiveElementToInteractiveRole) Check(ctx *Context, node *shimast.Node) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1014
|
+
info := jsxElementFromNode(node)
|
|
1015
|
+
role, ok := jsxRole(info.attrs)
|
|
1016
|
+
if ok && jsxIsNonInteractiveElement(info.tag) && jsxInteractiveRoles[role] {
|
|
1017
|
+
ctx.Report(info.opening, "Non-interactive elements must not use interactive roles.")
|
|
1018
|
+
}
|
|
1019
1019
|
}
|
|
1020
1020
|
|
|
1021
1021
|
type jsxA11yNoNoninteractiveTabindex struct{}
|
|
1022
1022
|
|
|
1023
1023
|
func (jsxA11yNoNoninteractiveTabindex) Name() string { return "jsx-a11y/no-noninteractive-tabindex" }
|
|
1024
1024
|
func (jsxA11yNoNoninteractiveTabindex) Visits() []shimast.Kind {
|
|
1025
|
-
|
|
1025
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1026
1026
|
}
|
|
1027
1027
|
func (jsxA11yNoNoninteractiveTabindex) Check(ctx *Context, node *shimast.Node) {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1028
|
+
info := jsxElementFromNode(node)
|
|
1029
|
+
if _, ok := jsxNumericAttr(info.attrs, "tabIndex", "tabindex"); ok && !jsxIsInteractive(info) {
|
|
1030
|
+
ctx.Report(info.opening, "Non-interactive elements must not be focusable with tabIndex.")
|
|
1031
|
+
}
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
1034
|
type jsxA11yNoRedundantRoles struct{}
|
|
1035
1035
|
|
|
1036
1036
|
func (jsxA11yNoRedundantRoles) Name() string { return "jsx-a11y/no-redundant-roles" }
|
|
1037
1037
|
func (jsxA11yNoRedundantRoles) Visits() []shimast.Kind {
|
|
1038
|
-
|
|
1038
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1039
1039
|
}
|
|
1040
1040
|
func (jsxA11yNoRedundantRoles) Check(ctx *Context, node *shimast.Node) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1041
|
+
info := jsxElementFromNode(node)
|
|
1042
|
+
role, ok := jsxRole(info.attrs)
|
|
1043
|
+
if !ok {
|
|
1044
|
+
return
|
|
1045
|
+
}
|
|
1046
|
+
implicit := jsxImplicitRole(info)
|
|
1047
|
+
if implicit == "" {
|
|
1048
|
+
implicit = jsxRedundantRoles[info.tag]
|
|
1049
|
+
}
|
|
1050
|
+
if implicit == role {
|
|
1051
|
+
ctx.Report(info.opening, "This role is redundant on the element.")
|
|
1052
|
+
}
|
|
1053
1053
|
}
|
|
1054
1054
|
|
|
1055
1055
|
type jsxA11yNoStaticElementInteractions struct{}
|
|
1056
1056
|
|
|
1057
1057
|
func (jsxA11yNoStaticElementInteractions) Name() string {
|
|
1058
|
-
|
|
1058
|
+
return "jsx-a11y/no-static-element-interactions"
|
|
1059
1059
|
}
|
|
1060
1060
|
func (jsxA11yNoStaticElementInteractions) Visits() []shimast.Kind {
|
|
1061
|
-
|
|
1061
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1062
1062
|
}
|
|
1063
1063
|
func (jsxA11yNoStaticElementInteractions) Check(ctx *Context, node *shimast.Node) {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1064
|
+
info := jsxElementFromNode(node)
|
|
1065
|
+
if info.tag == "" || jsxIsInteractive(info) || jsxIsNonInteractiveElement(info.tag) || jsxHasAttr(info.attrs, "role") || !jsxHasMouseOrKeyboardInteraction(info.attrs) {
|
|
1066
|
+
return
|
|
1067
|
+
}
|
|
1068
|
+
ctx.Report(info.opening, "Static elements with interaction handlers must have an ARIA role.")
|
|
1069
1069
|
}
|
|
1070
1070
|
|
|
1071
1071
|
type jsxA11yPreferTagOverRole struct{}
|
|
1072
1072
|
|
|
1073
1073
|
func (jsxA11yPreferTagOverRole) Name() string { return "jsx-a11y/prefer-tag-over-role" }
|
|
1074
1074
|
func (jsxA11yPreferTagOverRole) Visits() []shimast.Kind {
|
|
1075
|
-
|
|
1075
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1076
1076
|
}
|
|
1077
1077
|
func (jsxA11yPreferTagOverRole) Check(ctx *Context, node *shimast.Node) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1078
|
+
info := jsxElementFromNode(node)
|
|
1079
|
+
if info.tag == "" || info.tag != "div" && info.tag != "span" {
|
|
1080
|
+
return
|
|
1081
|
+
}
|
|
1082
|
+
role, ok := jsxRole(info.attrs)
|
|
1083
|
+
if ok {
|
|
1084
|
+
if tag, exists := jsxRolePreferredTag[role]; exists {
|
|
1085
|
+
ctx.Report(info.opening, "Prefer the native "+tag+" element over role="+role+".")
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
1088
|
}
|
|
1089
1089
|
|
|
1090
1090
|
type jsxA11yRoleHasRequiredAriaProps struct{}
|
|
1091
1091
|
|
|
1092
1092
|
func (jsxA11yRoleHasRequiredAriaProps) Name() string { return "jsx-a11y/role-has-required-aria-props" }
|
|
1093
1093
|
func (jsxA11yRoleHasRequiredAriaProps) Visits() []shimast.Kind {
|
|
1094
|
-
|
|
1094
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1095
1095
|
}
|
|
1096
1096
|
func (jsxA11yRoleHasRequiredAriaProps) Check(ctx *Context, node *shimast.Node) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1097
|
+
info := jsxElementFromNode(node)
|
|
1098
|
+
role, ok := jsxRole(info.attrs)
|
|
1099
|
+
if !ok {
|
|
1100
|
+
return
|
|
1101
|
+
}
|
|
1102
|
+
for _, required := range jsxRoleRequiredProps[role] {
|
|
1103
|
+
if !jsxHasAttr(info.attrs, required) {
|
|
1104
|
+
ctx.Report(info.opening, "Role "+role+" requires "+required+".")
|
|
1105
|
+
return
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
1108
|
}
|
|
1109
1109
|
|
|
1110
1110
|
type jsxA11yRoleSupportsAriaProps struct{}
|
|
1111
1111
|
|
|
1112
1112
|
func (jsxA11yRoleSupportsAriaProps) Name() string { return "jsx-a11y/role-supports-aria-props" }
|
|
1113
1113
|
func (jsxA11yRoleSupportsAriaProps) Visits() []shimast.Kind {
|
|
1114
|
-
|
|
1114
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1115
1115
|
}
|
|
1116
1116
|
func (jsxA11yRoleSupportsAriaProps) Check(ctx *Context, node *shimast.Node) {
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1117
|
+
info := jsxElementFromNode(node)
|
|
1118
|
+
role, ok := jsxRole(info.attrs)
|
|
1119
|
+
if !ok {
|
|
1120
|
+
return
|
|
1121
|
+
}
|
|
1122
|
+
if role == "" {
|
|
1123
|
+
return
|
|
1124
|
+
}
|
|
1125
|
+
supported := jsxRoleSupportedProps[role]
|
|
1126
|
+
for name, attr := range info.attrs {
|
|
1127
|
+
lower := strings.ToLower(name)
|
|
1128
|
+
if !strings.HasPrefix(lower, "aria-") || !jsxAriaProps[lower] || jsxGlobalAriaProps[lower] {
|
|
1129
|
+
continue
|
|
1130
|
+
}
|
|
1131
|
+
if supported == nil || !supported[lower] {
|
|
1132
|
+
ctx.Report(attr.node, "ARIA property "+lower+" is not supported by role "+role+".")
|
|
1133
|
+
return
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
1136
|
}
|
|
1137
1137
|
|
|
1138
1138
|
type jsxA11yScope struct{}
|
|
1139
1139
|
|
|
1140
1140
|
func (jsxA11yScope) Name() string { return "jsx-a11y/scope" }
|
|
1141
1141
|
func (jsxA11yScope) Visits() []shimast.Kind {
|
|
1142
|
-
|
|
1142
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1143
1143
|
}
|
|
1144
1144
|
func (jsxA11yScope) Check(ctx *Context, node *shimast.Node) {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1145
|
+
info := jsxElementFromNode(node)
|
|
1146
|
+
if jsxHasAttr(info.attrs, "scope") && info.tag != "th" {
|
|
1147
|
+
ctx.Report(info.opening, "The scope attribute is only valid on th elements.")
|
|
1148
|
+
}
|
|
1149
1149
|
}
|
|
1150
1150
|
|
|
1151
1151
|
type jsxA11yTabindexNoPositive struct{}
|
|
1152
1152
|
|
|
1153
1153
|
func (jsxA11yTabindexNoPositive) Name() string { return "jsx-a11y/tabindex-no-positive" }
|
|
1154
1154
|
func (jsxA11yTabindexNoPositive) Visits() []shimast.Kind {
|
|
1155
|
-
|
|
1155
|
+
return []shimast.Kind{shimast.KindJsxElement, shimast.KindJsxSelfClosingElement}
|
|
1156
1156
|
}
|
|
1157
1157
|
func (jsxA11yTabindexNoPositive) Check(ctx *Context, node *shimast.Node) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1158
|
+
info := jsxElementFromNode(node)
|
|
1159
|
+
if value, ok := jsxNumericAttr(info.attrs, "tabIndex", "tabindex"); ok && value > 0 {
|
|
1160
|
+
ctx.Report(info.opening, "tabIndex must not be positive.")
|
|
1161
|
+
}
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
1164
|
// jsxA11yAnchorAmbiguousText reports `<a>` elements whose visible text
|
|
@@ -1171,21 +1171,21 @@ type jsxA11yAnchorAmbiguousText struct{}
|
|
|
1171
1171
|
|
|
1172
1172
|
func (jsxA11yAnchorAmbiguousText) Name() string { return "jsx-a11y/anchor-ambiguous-text" }
|
|
1173
1173
|
func (jsxA11yAnchorAmbiguousText) Visits() []shimast.Kind {
|
|
1174
|
-
|
|
1174
|
+
return []shimast.Kind{shimast.KindJsxElement}
|
|
1175
1175
|
}
|
|
1176
1176
|
func (jsxA11yAnchorAmbiguousText) Check(ctx *Context, node *shimast.Node) {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1177
|
+
info := jsxElementFromNode(node)
|
|
1178
|
+
if info.tag != "a" {
|
|
1179
|
+
return
|
|
1180
|
+
}
|
|
1181
|
+
text := strings.ToLower(strings.TrimSpace(jsxAnchorText(info.children)))
|
|
1182
|
+
if text == "" {
|
|
1183
|
+
return
|
|
1184
|
+
}
|
|
1185
|
+
switch text {
|
|
1186
|
+
case "click here", "here", "link", "a link", "click", "more", "read more":
|
|
1187
|
+
ctx.Report(info.opening, "Anchor text \""+text+"\" is too ambiguous out of context; describe the destination.")
|
|
1188
|
+
}
|
|
1189
1189
|
}
|
|
1190
1190
|
|
|
1191
1191
|
// jsxAnchorText concatenates the visible text content of a JSX
|
|
@@ -1194,71 +1194,71 @@ func (jsxA11yAnchorAmbiguousText) Check(ctx *Context, node *shimast.Node) {
|
|
|
1194
1194
|
// text; other expression shapes are skipped because their runtime value
|
|
1195
1195
|
// is not known at lint time.
|
|
1196
1196
|
func jsxAnchorText(children *shimast.NodeList) string {
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1197
|
+
if children == nil {
|
|
1198
|
+
return ""
|
|
1199
|
+
}
|
|
1200
|
+
var b strings.Builder
|
|
1201
|
+
for _, child := range children.Nodes {
|
|
1202
|
+
if child == nil {
|
|
1203
|
+
continue
|
|
1204
|
+
}
|
|
1205
|
+
switch child.Kind {
|
|
1206
|
+
case shimast.KindJsxText:
|
|
1207
|
+
t := child.AsJsxText()
|
|
1208
|
+
if t != nil && !t.ContainsOnlyTriviaWhiteSpaces {
|
|
1209
|
+
b.WriteString(t.Text)
|
|
1210
|
+
}
|
|
1211
|
+
case shimast.KindJsxExpression:
|
|
1212
|
+
expr := child.AsJsxExpression()
|
|
1213
|
+
if expr != nil && expr.Expression != nil {
|
|
1214
|
+
if s := stringLiteralText(expr.Expression); s != "" {
|
|
1215
|
+
b.WriteString(s)
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
case shimast.KindJsxElement:
|
|
1219
|
+
inner := jsxElementFromNode(child)
|
|
1220
|
+
b.WriteString(jsxAnchorText(inner.children))
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return b.String()
|
|
1224
1224
|
}
|
|
1225
1225
|
|
|
1226
1226
|
func init() {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1227
|
+
Register(jsxA11yAltText{})
|
|
1228
|
+
Register(jsxA11yAnchorAmbiguousText{})
|
|
1229
|
+
Register(jsxA11yAnchorHasContent{})
|
|
1230
|
+
Register(jsxA11yAnchorIsValid{})
|
|
1231
|
+
Register(jsxA11yAriaActivedescendantHasTabindex{})
|
|
1232
|
+
Register(jsxA11yAriaProps{})
|
|
1233
|
+
Register(jsxA11yAriaProptypes{})
|
|
1234
|
+
Register(jsxA11yAriaRole{})
|
|
1235
|
+
Register(jsxA11yAriaUnsupportedElements{})
|
|
1236
|
+
Register(jsxA11yAutocompleteValid{})
|
|
1237
|
+
Register(jsxA11yClickEventsHaveKeyEvents{})
|
|
1238
|
+
Register(jsxA11yControlHasAssociatedLabel{})
|
|
1239
|
+
Register(jsxA11yHeadingHasContent{})
|
|
1240
|
+
Register(jsxA11yHtmlHasLang{})
|
|
1241
|
+
Register(jsxA11yIframeHasTitle{})
|
|
1242
|
+
Register(jsxA11yImgRedundantAlt{})
|
|
1243
|
+
Register(jsxA11yInteractiveSupportsFocus{})
|
|
1244
|
+
Register(jsxA11yLabelHasAssociatedControl{})
|
|
1245
|
+
Register(jsxA11yLabelHasFor{})
|
|
1246
|
+
Register(jsxA11yLang{})
|
|
1247
|
+
Register(jsxA11yMediaHasCaption{})
|
|
1248
|
+
Register(jsxA11yMouseEventsHaveKeyEvents{})
|
|
1249
|
+
Register(jsxA11yNoAccessKey{})
|
|
1250
|
+
Register(jsxA11yNoAriaHiddenOnFocusable{})
|
|
1251
|
+
Register(jsxA11yNoAutofocus{})
|
|
1252
|
+
Register(jsxA11yNoDistractingElements{})
|
|
1253
|
+
Register(jsxA11yNoInteractiveElementToNoninteractiveRole{})
|
|
1254
|
+
Register(jsxA11yNoNoninteractiveElementInteractions{})
|
|
1255
|
+
Register(jsxA11yNoNoninteractiveElementToInteractiveRole{})
|
|
1256
|
+
Register(jsxA11yNoNoninteractiveTabindex{})
|
|
1257
|
+
Register(jsxA11yNoRedundantRoles{})
|
|
1258
|
+
Register(jsxA11yNoStaticElementInteractions{})
|
|
1259
|
+
Register(jsxA11yPreferTagOverRole{})
|
|
1260
|
+
Register(jsxA11yRoleHasRequiredAriaProps{})
|
|
1261
|
+
Register(jsxA11yRoleSupportsAriaProps{})
|
|
1262
|
+
Register(jsxA11yScope{})
|
|
1263
|
+
Register(jsxA11yTabindexNoPositive{})
|
|
1264
1264
|
}
|