@ttsc/lint 0.14.0-dev.20260529.1 → 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 +85 -87
- 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
|
@@ -15,7 +15,7 @@ import (
|
|
|
15
15
|
// node kinds whose per-node printers are registered with the
|
|
16
16
|
// dispatcher (object/array literals, call/new expressions, named
|
|
17
17
|
// import / export clauses, top-level import declarations). For every
|
|
18
|
-
// other kind, the rule abstains
|
|
18
|
+
// other kind, the rule abstains, it never emits an edit that would
|
|
19
19
|
// modify bytes the dispatcher does not fully control. Coverage
|
|
20
20
|
// expands by adding kinds to the dispatcher; the rule itself does not
|
|
21
21
|
// need to grow.
|
|
@@ -23,7 +23,7 @@ import (
|
|
|
23
23
|
// Per-node decision flow:
|
|
24
24
|
//
|
|
25
25
|
// 1. Skip leading trivia to find the node's actual first byte.
|
|
26
|
-
// 2. Count the leading column on that line
|
|
26
|
+
// 2. Count the leading column on that line, that becomes the
|
|
27
27
|
// printer's StartingIndent so continuation lines align under the
|
|
28
28
|
// opening token and fit measurement charges the prefix against
|
|
29
29
|
// the budget.
|
|
@@ -41,7 +41,7 @@ import (
|
|
|
41
41
|
// idempotent: a second pass renders identical bytes, the comparison
|
|
42
42
|
// short-circuits, and the cascade converges. The `covered` abstain in
|
|
43
43
|
// step 4 is the safety floor: `ttsc format` either reflows correctly or
|
|
44
|
-
// leaves the node byte-identical
|
|
44
|
+
// leaves the node byte-identical, it never emits a half-reflowed,
|
|
45
45
|
// inconsistently indented shape.
|
|
46
46
|
//
|
|
47
47
|
// The rule is a format-class rule (IsFormat == true) so `ttsc format`
|
|
@@ -54,7 +54,7 @@ type formatPrintWidth struct{}
|
|
|
54
54
|
// formatPrintWidthOptions mirrors `TtscLintRuleOptions.PrintWidth`.
|
|
55
55
|
//
|
|
56
56
|
// TrailingComma reaches this rule because the printer's reflow decides
|
|
57
|
-
// whether to emit a trailing comma on every multi-line list
|
|
57
|
+
// whether to emit a trailing comma on every multi-line list, and that
|
|
58
58
|
// decision must match the user's `format.trailingComma` setting or the
|
|
59
59
|
// reflow oscillates against `format/trailing-comma` on every cascade
|
|
60
60
|
// pass. The config layer mirrors `format.trailingComma` into both
|
|
@@ -79,6 +79,7 @@ func (formatPrintWidth) Visits() []shimast.Kind {
|
|
|
79
79
|
shimast.KindNamedImports,
|
|
80
80
|
shimast.KindNamedExports,
|
|
81
81
|
shimast.KindImportDeclaration,
|
|
82
|
+
shimast.KindConditionalExpression,
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -92,19 +93,21 @@ func (formatPrintWidth) Visits() []shimast.Kind {
|
|
|
92
93
|
// re-render still over-breaks some of these; the next slice should
|
|
93
94
|
// measure fitsFirstLine against `pw - col - trailingNonComment`
|
|
94
95
|
// before committing to the broken layout.
|
|
95
|
-
// -
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
// measured against the full declaration line, or visiting
|
|
101
|
-
// ExportDeclaration directly so the `from "..."` tail joins the
|
|
102
|
-
// reflow surface.
|
|
96
|
+
// - Default-combined imports (`import D, { X } from "x"`) are kept
|
|
97
|
+
// verbatim by printImportDeclaration (its `clause.Name() != nil`
|
|
98
|
+
// guard), so a default import whose named clause overflows is not
|
|
99
|
+
// broken; Prettier breaks it. Lifting the guard, threading the default
|
|
100
|
+
// name into the printed prefix, is a future default-import reflow slice.
|
|
103
101
|
//
|
|
104
|
-
//
|
|
105
|
-
// `
|
|
106
|
-
//
|
|
107
|
-
//
|
|
102
|
+
// (A single-specifier `import/export { X } from "long-path"` that overflows
|
|
103
|
+
// only because of the `from "..."` tail is now kept inline by the
|
|
104
|
+
// isSingleSpecifierNamedClause abstain, matching Prettier, which never
|
|
105
|
+
// breaks a one-name clause. Multi-specifier clauses still break correctly:
|
|
106
|
+
// the trailing-width charge measures the `from "..."` tail against the line
|
|
107
|
+
// budget, so a brace that fits but whose declaration overflows is broken.)
|
|
108
|
+
//
|
|
109
|
+
// These cases are tracked benchmark divergences. They are listed here so a
|
|
110
|
+
// future slice can pick them up without rediscovering the divergence.
|
|
108
111
|
func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
109
112
|
if ctx == nil || ctx.File == nil || node == nil {
|
|
110
113
|
return
|
|
@@ -135,6 +138,54 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
135
138
|
return
|
|
136
139
|
}
|
|
137
140
|
|
|
141
|
+
printOpts.StartingColumn = leadingColumn(src, start, printOpts.TabWidth)
|
|
142
|
+
|
|
143
|
+
// trailingWidth is the column span of the tokens that stay on the
|
|
144
|
+
// node's last line after `end`, a `;`, a `);`, a `) {`. The reflow
|
|
145
|
+
// replaces only [start, end) and cannot move them, so both the fast
|
|
146
|
+
// path and the layout budget must reserve those columns; otherwise
|
|
147
|
+
// the rule emits a line that overflows by exactly the suffix.
|
|
148
|
+
trailingWidth := trailingLineWidth(src, end, printOpts.TabWidth)
|
|
149
|
+
|
|
150
|
+
// Fast path: if the node's existing single-line bytes already fit
|
|
151
|
+
// the printWidth budget, prefix column, node width and trailing
|
|
152
|
+
// suffix all charged, the reflowed output cannot differ from the
|
|
153
|
+
// source (the printer would render the same flat shape). Skip the
|
|
154
|
+
// Doc build + render entirely. This is the common case on
|
|
155
|
+
// well-formatted code, every short call, every short literal,
|
|
156
|
+
// and saves the allocations from PrintNode + Print.
|
|
157
|
+
//
|
|
158
|
+
// This runs before the abstain guards below on purpose: those guards
|
|
159
|
+
// only ever return (they never emit an edit), so for a node that
|
|
160
|
+
// already fits the outcome is identical whichever fires first, and
|
|
161
|
+
// the fast path is far cheaper than the guards' ancestor walks and
|
|
162
|
+
// the per-byte comment scan in hasNonChildComments. Charging that
|
|
163
|
+
// cost only on nodes that actually overflow keeps the hot path on
|
|
164
|
+
// well-formatted code allocation- and scan-free.
|
|
165
|
+
// A call with two or more function/arrow arguments explodes regardless of
|
|
166
|
+
// width (Prettier's multiple-callback rule), and an array of same-kind
|
|
167
|
+
// multi-child arrays/objects explodes under Prettier's shouldBreak heuristic,
|
|
168
|
+
// so a flat one-line node of either shape still needs a reflow. Skip the fast
|
|
169
|
+
// path for them; the printer's ForceBreak then produces the exploded shape.
|
|
170
|
+
// Everything else that fits flat is byte-identical after reflow, so the fast
|
|
171
|
+
// path stands.
|
|
172
|
+
if !callForcesFunctionBreak(node) &&
|
|
173
|
+
!arrayForcesBreak(node) &&
|
|
174
|
+
!sliceContainsNewline(src, start, end) &&
|
|
175
|
+
printOpts.StartingColumn+displayWidth(src[start:end])+trailingWidth <= printOpts.PrintWidth {
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// A named import/export clause with a single specifier is never broken by
|
|
180
|
+
// Prettier 3: `import { X } from "long"` and `export type { X } from "long"`
|
|
181
|
+
// stay inline even when the `from "..."` tail pushes the whole declaration
|
|
182
|
+
// past printWidth (Prettier breaks a brace clause only at two or more
|
|
183
|
+
// specifiers). The rule visits the brace clause (or the import declaration)
|
|
184
|
+
// in isolation, so without this it would break a one-name clause and diverge.
|
|
185
|
+
if isSingleSpecifierNamedClause(node) {
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
138
189
|
// Skip nested-print fires: if the visiting node has an ancestor
|
|
139
190
|
// that also belongs to the rule's set, the outer reflow already
|
|
140
191
|
// includes us. Acting at every level would emit overlapping edits
|
|
@@ -144,8 +195,8 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
144
195
|
}
|
|
145
196
|
|
|
146
197
|
// Abstain on any node nested inside a template-literal substitution.
|
|
147
|
-
// Prettier renders `${…}` expressions at printWidth:Infinity
|
|
148
|
-
// never breaks an interpolation the source wrote on one line
|
|
198
|
+
// Prettier renders `${…}` expressions at printWidth:Infinity, it
|
|
199
|
+
// never breaks an interpolation the source wrote on one line, so
|
|
149
200
|
// reflowing a call or literal inside `${…}` would split the template
|
|
150
201
|
// across lines and diverge from Prettier. See the printWidth:Infinity
|
|
151
202
|
// branch in Prettier's printTemplateExpression.
|
|
@@ -153,50 +204,42 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
153
204
|
return
|
|
154
205
|
}
|
|
155
206
|
|
|
207
|
+
// Abstain on any node nested inside a JSX expression container (`{…}`),
|
|
208
|
+
// the JSX analogue of the template-substitution guard above. A reflow
|
|
209
|
+
// node (call / conditional) that sits inside BOTH a JSX attribute
|
|
210
|
+
// initializer (`x={cond ? a : b}`) and a JSX child
|
|
211
|
+
// (`{items.map(…)}`) gets broken here, then the next pass measures each
|
|
212
|
+
// fragment flat, finds it fits, and reverts — `ttsc format` oscillates to
|
|
213
|
+
// the 10-pass cap and exits 2. `KindJsxExpression` wraps both the
|
|
214
|
+
// attribute `{…}` and the child `{…}`, so this one guard covers both.
|
|
215
|
+
if hasJsxExpressionAncestor(node) {
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
156
219
|
// Safety: abstain when the node carries comments outside its
|
|
157
220
|
// children. The per-node printers join child docs with a fresh
|
|
158
221
|
// `, ` separator and have no path for trivia between siblings, so
|
|
159
222
|
// reflowing such a node would silently delete the comment.
|
|
160
|
-
// Conservative
|
|
223
|
+
// Conservative, false positives mean a missed reflow opportunity,
|
|
161
224
|
// not a regression. Coverage of inline comments inside lists is
|
|
162
225
|
// the next slice of work.
|
|
163
226
|
if hasNonChildComments(node, src, start, end) {
|
|
164
227
|
return
|
|
165
228
|
}
|
|
166
229
|
|
|
167
|
-
printOpts.StartingColumn = leadingColumn(src, start, printOpts.TabWidth)
|
|
168
230
|
// A node reflowed on a ternary-arm continuation line (`? expr` or
|
|
169
231
|
// `: expr`) hangs its broken continuation under the arm's expression,
|
|
170
|
-
// two columns past the `?`/`:` marker
|
|
232
|
+
// two columns past the `?`/`:` marker, not under the marker itself.
|
|
171
233
|
printOpts.BaseIndent = lineLeadingIndent(src, start, printOpts.TabWidth) +
|
|
172
234
|
ternaryArmIndentBonus(src, start)
|
|
173
235
|
|
|
174
|
-
// trailingWidth is the column span of the tokens that stay on the
|
|
175
|
-
// node's last line after `end` — a `;`, a `);`, a `) {`. The reflow
|
|
176
|
-
// replaces only [start, end) and cannot move them, so both the fast
|
|
177
|
-
// path and the layout budget must reserve those columns; otherwise
|
|
178
|
-
// the rule emits a line that overflows by exactly the suffix.
|
|
179
|
-
trailingWidth := trailingLineWidth(src, end, printOpts.TabWidth)
|
|
180
|
-
|
|
181
|
-
// Fast path: if the node's existing single-line bytes already fit
|
|
182
|
-
// the printWidth budget — prefix column, node width and trailing
|
|
183
|
-
// suffix all charged — the reflowed output cannot differ from the
|
|
184
|
-
// source (the printer would render the same flat shape). Skip the
|
|
185
|
-
// Doc build + render entirely. This is the common case on
|
|
186
|
-
// well-formatted code — every short call, every short literal —
|
|
187
|
-
// and saves the allocations from PrintNode + Print.
|
|
188
|
-
if !sliceContainsNewline(src, start, end) &&
|
|
189
|
-
printOpts.StartingColumn+(end-start)+trailingWidth <= printOpts.PrintWidth {
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
|
|
193
236
|
printCtx := NewPrintContext(ctx.File, printOpts)
|
|
194
237
|
doc, covered := PrintNode(printCtx, node)
|
|
195
238
|
if doc.IsNil() {
|
|
196
239
|
return
|
|
197
240
|
}
|
|
198
241
|
// Safety abstain: the printed subtree contains a multi-line verbatim
|
|
199
|
-
// node
|
|
242
|
+
// node, one the dispatcher has no printer for. Such a node keeps the
|
|
200
243
|
// source columns its lines were written at, while the reflow
|
|
201
244
|
// re-indents everything around it. Emitting the edit would produce
|
|
202
245
|
// inconsistently indented, corrupt output (a callback header at one
|
|
@@ -207,8 +250,8 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
207
250
|
return
|
|
208
251
|
}
|
|
209
252
|
// Render at the full printWidth budget. A reflow that breaks across
|
|
210
|
-
// lines then makes every layout decision
|
|
211
|
-
// where a list explodes
|
|
253
|
+
// lines then makes every layout decision, which call argument hugs,
|
|
254
|
+
// where a list explodes, against the true column budget. The
|
|
212
255
|
// un-movable trailing suffix (`;`, `) {`, ` satisfies T`) lands on a
|
|
213
256
|
// short last line; charging it against the whole budget would
|
|
214
257
|
// wrongly penalize the interior lines and over-break the node.
|
|
@@ -217,7 +260,7 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
217
260
|
// reflow that collapses to a single line. When the flat form plus
|
|
218
261
|
// the suffix would overflow, re-render under a budget shrunk by the
|
|
219
262
|
// suffix so the node breaks instead of spilling the suffix past
|
|
220
|
-
// printWidth
|
|
263
|
+
// printWidth, the regression that keeps a call flat at exactly
|
|
221
264
|
// printWidth while the trailing `;` runs over.
|
|
222
265
|
if trailingWidth > 0 &&
|
|
223
266
|
!strings.Contains(rendered, "\n") &&
|
|
@@ -233,8 +276,8 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
233
276
|
}
|
|
234
277
|
// Safety floor: never emit an edit that makes the widest line wider
|
|
235
278
|
// than it already was. The reflow may be unable to break an
|
|
236
|
-
// un-breakable token run
|
|
237
|
-
// member
|
|
279
|
+
// un-breakable token run, a long string literal, a verbatim object
|
|
280
|
+
// member, but it must never *worsen* the worst line. That is exactly
|
|
238
281
|
// the regression this guards: `ttsc format` collapsing an
|
|
239
282
|
// already-broken, fitting call into one over-wide line. A reflow that
|
|
240
283
|
// only fixes indentation and leaves a pre-existing over-wide line
|
|
@@ -255,14 +298,14 @@ func (formatPrintWidth) Check(ctx *Context, node *shimast.Node) {
|
|
|
255
298
|
// trailingLineWidth returns the visual column width of src[end:] up to
|
|
256
299
|
// the next newline, with trailing whitespace trimmed. The formatPrintWidth
|
|
257
300
|
// reflow replaces only the node's own byte range, so whatever
|
|
258
|
-
// shares the node's last source line
|
|
259
|
-
// tail, a `, nextArg)` continuation
|
|
301
|
+
// shares the node's last source line, a statement `;`, a `) {` header
|
|
302
|
+
// tail, a `, nextArg)` continuation, stays put. Charging that width
|
|
260
303
|
// against the budget keeps the rule from emitting a line that overflows
|
|
261
304
|
// by exactly the suffix it could never move.
|
|
262
305
|
//
|
|
263
306
|
// Trailing `//` line comments are excluded from the budget. A line
|
|
264
307
|
// comment runs to the end of the source line by definition, so breaking
|
|
265
|
-
// the reflowed node to make the comment fit cannot help
|
|
308
|
+
// the reflowed node to make the comment fit cannot help, Prettier 3
|
|
266
309
|
// keeps the node inline and lets the comment trail (see typeorm's
|
|
267
310
|
// `comment.replaceAll(...) // Null bytes' shape that pushed
|
|
268
311
|
// `formatPrintWidth: 'off'` onto the ttsc-lint benchmark branch).
|
|
@@ -278,11 +321,11 @@ func trailingLineWidth(src string, end int, tabWidth int) int {
|
|
|
278
321
|
}
|
|
279
322
|
lineEnd := trailingSuffixEnd(src, end)
|
|
280
323
|
col := 0
|
|
281
|
-
for
|
|
282
|
-
if
|
|
324
|
+
for _, r := range src[end:lineEnd] {
|
|
325
|
+
if r == '\t' {
|
|
283
326
|
col += tabWidth - (col % tabWidth)
|
|
284
327
|
} else {
|
|
285
|
-
col
|
|
328
|
+
col += runeWidth(r)
|
|
286
329
|
}
|
|
287
330
|
}
|
|
288
331
|
return col
|
|
@@ -302,7 +345,7 @@ func trailingSuffixEnd(src string, end int) int {
|
|
|
302
345
|
if src[lineEnd] == '/' && lineEnd+1 < len(src) {
|
|
303
346
|
next := src[lineEnd+1]
|
|
304
347
|
if next == '/' {
|
|
305
|
-
// `//` line comment
|
|
348
|
+
// `//` line comment, Prettier treats the whole tail as a
|
|
306
349
|
// trailing comment that runs to EOL. Drop it from the suffix
|
|
307
350
|
// budget so the rule does not break the node to chase a
|
|
308
351
|
// comment that cannot be moved or wrapped.
|
|
@@ -322,8 +365,8 @@ func trailingSuffixEnd(src string, end int) int {
|
|
|
322
365
|
}
|
|
323
366
|
|
|
324
367
|
// maxLineWidth returns the widest effective column span among the lines
|
|
325
|
-
// of `text`. The first line is charged `startingColumn
|
|
326
|
-
// already on that source line that the reflow does not re-emit
|
|
368
|
+
// of `text`. The first line is charged `startingColumn`, the prefix
|
|
369
|
+
// already on that source line that the reflow does not re-emit, and
|
|
327
370
|
// the last line is charged `trailingWidth` for the un-movable suffix
|
|
328
371
|
// that follows the node. Tabs count as `tabWidth` columns.
|
|
329
372
|
//
|
|
@@ -343,7 +386,7 @@ func maxLineWidth(text string, startingColumn, trailingWidth, tabWidth int) int
|
|
|
343
386
|
if r == '\t' {
|
|
344
387
|
width += tabWidth - (width % tabWidth)
|
|
345
388
|
} else if r != '\r' {
|
|
346
|
-
width
|
|
389
|
+
width += runeWidth(r)
|
|
347
390
|
}
|
|
348
391
|
}
|
|
349
392
|
if i == 0 {
|
|
@@ -372,11 +415,11 @@ func leadingColumn(src string, pos int, tabWidth int) int {
|
|
|
372
415
|
}
|
|
373
416
|
lineStart := lineStartOffset(src, pos)
|
|
374
417
|
col := 0
|
|
375
|
-
for
|
|
376
|
-
if
|
|
418
|
+
for _, r := range src[lineStart:pos] {
|
|
419
|
+
if r == '\t' {
|
|
377
420
|
col += tabWidth - (col % tabWidth)
|
|
378
421
|
} else {
|
|
379
|
-
col
|
|
422
|
+
col += runeWidth(r)
|
|
380
423
|
}
|
|
381
424
|
}
|
|
382
425
|
return col
|
|
@@ -389,7 +432,7 @@ func leadingColumn(src string, pos int, tabWidth int) int {
|
|
|
389
432
|
//
|
|
390
433
|
// Continuation lines (Hardline / broken-mode Line) emitted by the
|
|
391
434
|
// reflow doc should align relative to this value, not relative to the
|
|
392
|
-
// node's leading column
|
|
435
|
+
// node's leading column, see PrintOptions.BaseIndent for the contract.
|
|
393
436
|
//
|
|
394
437
|
// When the visited node lives on a *continuation line* (e.g. an RHS
|
|
395
438
|
// expression hanging below a binary operator on the previous line),
|
|
@@ -462,7 +505,7 @@ func lineStartOffset(src string, pos int) int {
|
|
|
462
505
|
// the arm's expression rather than under the `?`/`:` token. In practice
|
|
463
506
|
// only a ternary arm opens a reflow target's line with `? ` / `: `; the
|
|
464
507
|
// two-byte prefix is a heuristic, and a rare false positive only shifts
|
|
465
|
-
// a broken continuation by two columns
|
|
508
|
+
// a broken continuation by two columns, it never corrupts bytes.
|
|
466
509
|
func ternaryArmIndentBonus(src string, pos int) int {
|
|
467
510
|
i := lineStartOffset(src, pos)
|
|
468
511
|
for i < len(src) && (src[i] == ' ' || src[i] == '\t') {
|
|
@@ -508,6 +551,26 @@ func hasTemplateSubstitutionAncestor(node *shimast.Node) bool {
|
|
|
508
551
|
return false
|
|
509
552
|
}
|
|
510
553
|
|
|
554
|
+
// hasJsxExpressionAncestor reports whether `node` sits inside a JSX
|
|
555
|
+
// expression container (`{…}`). formatPrintWidth abstains on such nodes for
|
|
556
|
+
// the same reason as hasTemplateSubstitutionAncestor: a reflow node that lives
|
|
557
|
+
// inside both a JSX attribute initializer and a JSX child breaks under
|
|
558
|
+
// print-width and then reverts on the next pass when each fragment measures
|
|
559
|
+
// flat, so `ttsc format` never converges and exits 2. `KindJsxExpression`
|
|
560
|
+
// wraps both the attribute `{…}` and the child `{…}`, so a single ancestor
|
|
561
|
+
// walk covers both placements.
|
|
562
|
+
func hasJsxExpressionAncestor(node *shimast.Node) bool {
|
|
563
|
+
if node == nil {
|
|
564
|
+
return false
|
|
565
|
+
}
|
|
566
|
+
for parent := node.Parent; parent != nil; parent = parent.Parent {
|
|
567
|
+
if parent.Kind == shimast.KindJsxExpression {
|
|
568
|
+
return true
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return false
|
|
572
|
+
}
|
|
573
|
+
|
|
511
574
|
// isReflowKind reports whether `k` is one of the node kinds the
|
|
512
575
|
// formatPrintWidth rule visits. Kept in sync with Visits() so
|
|
513
576
|
// hasReflowAncestor does not need to call Visits() at runtime.
|
|
@@ -519,7 +582,8 @@ func isReflowKind(k shimast.Kind) bool {
|
|
|
519
582
|
shimast.KindNewExpression,
|
|
520
583
|
shimast.KindNamedImports,
|
|
521
584
|
shimast.KindNamedExports,
|
|
522
|
-
shimast.KindImportDeclaration
|
|
585
|
+
shimast.KindImportDeclaration,
|
|
586
|
+
shimast.KindConditionalExpression:
|
|
523
587
|
return true
|
|
524
588
|
}
|
|
525
589
|
return false
|
|
@@ -528,7 +592,7 @@ func isReflowKind(k shimast.Kind) bool {
|
|
|
528
592
|
// hasNonChildComments scans the byte range [start, end) and returns true
|
|
529
593
|
// if any `//` or `/*` lives outside the union of `node`'s direct child
|
|
530
594
|
// byte ranges. The rule uses this to abstain from reflows that would
|
|
531
|
-
// drop inter-child comments
|
|
595
|
+
// drop inter-child comments, the v1 list printers join children with
|
|
532
596
|
// a fresh separator that has no slot for trivia between them.
|
|
533
597
|
//
|
|
534
598
|
// The scan is byte-level for simplicity: a TS scanner would also work
|
|
@@ -537,7 +601,7 @@ func isReflowKind(k shimast.Kind) bool {
|
|
|
537
601
|
// are children, so `"//"` inside them never reaches the comment
|
|
538
602
|
// check). The residual conservative case is comment-shaped bytes
|
|
539
603
|
// inside an inter-child gap that the TS grammar would never tokenize
|
|
540
|
-
// as a comment
|
|
604
|
+
// as a comment, effectively nil for valid TypeScript source.
|
|
541
605
|
//
|
|
542
606
|
// `format/sort-imports` chose the opposite path on a similar shape:
|
|
543
607
|
// it actively preserves inter-specifier comments by walking the
|
|
@@ -558,18 +622,36 @@ func hasNonChildComments(node *shimast.Node, src string, start, end int) bool {
|
|
|
558
622
|
// child.Pos() points at the start of leading trivia, which
|
|
559
623
|
// can include comments belonging to the sibling boundary.
|
|
560
624
|
// Trim past trivia so the inChild check only covers the
|
|
561
|
-
// child's actual token bytes
|
|
625
|
+
// child's actual token bytes, comments between siblings
|
|
562
626
|
// then surface to the scanner below.
|
|
563
627
|
children = append(children, span{shimscanner.SkipTrivia(src, child.Pos()), child.End()})
|
|
564
628
|
return false
|
|
565
629
|
})
|
|
630
|
+
// inChild reports whether offset `i` lies inside any child's [pos, end)
|
|
631
|
+
// token range. The children are appended in source order (ForEachChild
|
|
632
|
+
// walks the AST in source order) with non-overlapping ranges, so a binary
|
|
633
|
+
// search for the last child whose `pos <= i` answers the query in O(log n)
|
|
634
|
+
// instead of the former O(n) per-byte linear scan. The boolean returned is
|
|
635
|
+
// identical to the linear scan for every offset; this is a pure speedup of
|
|
636
|
+
// the predicate, it changes no formatting output.
|
|
566
637
|
inChild := func(i int) bool {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
638
|
+
lo, hi := 0, len(children)
|
|
639
|
+
for lo < hi {
|
|
640
|
+
mid := (lo + hi) / 2
|
|
641
|
+
if children[mid].pos <= i {
|
|
642
|
+
lo = mid + 1
|
|
643
|
+
} else {
|
|
644
|
+
hi = mid
|
|
570
645
|
}
|
|
571
646
|
}
|
|
572
|
-
|
|
647
|
+
// lo is the count of children with pos <= i; the candidate is the one
|
|
648
|
+
// just before it. A non-overlapping, source-ordered child list means at
|
|
649
|
+
// most this single candidate can contain `i`.
|
|
650
|
+
if lo == 0 {
|
|
651
|
+
return false
|
|
652
|
+
}
|
|
653
|
+
c := children[lo-1]
|
|
654
|
+
return i >= c.pos && i < c.end
|
|
573
655
|
}
|
|
574
656
|
for i := start; i < end-1 && i < len(src)-1; i++ {
|
|
575
657
|
if inChild(i) {
|
|
@@ -582,6 +664,40 @@ func hasNonChildComments(node *shimast.Node, src string, start, end int) bool {
|
|
|
582
664
|
return false
|
|
583
665
|
}
|
|
584
666
|
|
|
667
|
+
// isSingleSpecifierNamedClause reports whether `node` is a named import/export
|
|
668
|
+
// brace clause (or an import declaration whose binding is one) carrying exactly
|
|
669
|
+
// one specifier. Prettier never breaks such a clause; the print-width rule
|
|
670
|
+
// abstains so a one-name `{ X }` survives a `from "long-path"` overflow inline.
|
|
671
|
+
func isSingleSpecifierNamedClause(node *shimast.Node) bool {
|
|
672
|
+
switch node.Kind {
|
|
673
|
+
case shimast.KindNamedImports:
|
|
674
|
+
ni := node.AsNamedImports()
|
|
675
|
+
return ni != nil && ni.Elements != nil && len(ni.Elements.Nodes) == 1
|
|
676
|
+
case shimast.KindNamedExports:
|
|
677
|
+
ne := node.AsNamedExports()
|
|
678
|
+
return ne != nil && ne.Elements != nil && len(ne.Elements.Nodes) == 1
|
|
679
|
+
case shimast.KindImportDeclaration:
|
|
680
|
+
imp := node.AsImportDeclaration()
|
|
681
|
+
if imp == nil || imp.ImportClause == nil {
|
|
682
|
+
return false
|
|
683
|
+
}
|
|
684
|
+
clause := imp.ImportClause.AsImportClause()
|
|
685
|
+
if clause == nil || clause.NamedBindings == nil ||
|
|
686
|
+
clause.NamedBindings.Kind != shimast.KindNamedImports {
|
|
687
|
+
return false
|
|
688
|
+
}
|
|
689
|
+
// A default binding alongside the named clause (`import D, { X } from …`)
|
|
690
|
+
// makes Prettier break the brace even for a single specifier, so the
|
|
691
|
+
// abstain applies only to a bare `import { X } from …`.
|
|
692
|
+
if clause.Name() != nil {
|
|
693
|
+
return false
|
|
694
|
+
}
|
|
695
|
+
ni := clause.NamedBindings.AsNamedImports()
|
|
696
|
+
return ni != nil && ni.Elements != nil && len(ni.Elements.Nodes) == 1
|
|
697
|
+
}
|
|
698
|
+
return false
|
|
699
|
+
}
|
|
700
|
+
|
|
585
701
|
func init() {
|
|
586
702
|
Register(formatPrintWidth{})
|
|
587
703
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// formatQuoteProps normalizes quoting of object-literal property keys,
|
|
9
|
+
// mirroring Prettier's `quoteProps`:
|
|
10
|
+
//
|
|
11
|
+
// - "as-needed" (default): drop the quotes from a string key that is a
|
|
12
|
+
// valid identifier and not a numeric-looking key. `{ "foo": 1 }` becomes
|
|
13
|
+
// `{ foo: 1 }`; `{ "bar-baz": 1 }` and `{ "123": 1 }` keep their quotes.
|
|
14
|
+
// - "consistent": if ANY key in the object needs quotes, leave the object
|
|
15
|
+
// alone; otherwise unquote every removable key.
|
|
16
|
+
// - "preserve": never change quoting.
|
|
17
|
+
//
|
|
18
|
+
// Only quoted keys the rule can SAFELY unquote are ever touched: a string
|
|
19
|
+
// whose content is a plain ASCII identifier (letters, `_`, `$`, digits after
|
|
20
|
+
// the first) with no escapes. A key with escapes, unicode, a leading digit
|
|
21
|
+
// (numeric-looking), or any non-identifier byte is left quoted in every
|
|
22
|
+
// mode, so the rule can never produce an invalid key. Idempotent.
|
|
23
|
+
type formatQuoteProps struct{}
|
|
24
|
+
|
|
25
|
+
type formatQuotePropsOptions struct {
|
|
26
|
+
Mode string `json:"mode"`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func (formatQuoteProps) Name() string { return "format/quote-props" }
|
|
30
|
+
func (formatQuoteProps) IsFormat() bool { return true }
|
|
31
|
+
|
|
32
|
+
func (formatQuoteProps) Visits() []shimast.Kind {
|
|
33
|
+
return []shimast.Kind{shimast.KindObjectLiteralExpression}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func (formatQuoteProps) Check(ctx *Context, node *shimast.Node) {
|
|
37
|
+
if ctx == nil || ctx.File == nil || node == nil {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
obj := node.AsObjectLiteralExpression()
|
|
41
|
+
if obj == nil || obj.Properties == nil {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
var opts formatQuotePropsOptions
|
|
45
|
+
_ = ctx.DecodeOptions(&opts)
|
|
46
|
+
mode := opts.Mode
|
|
47
|
+
switch mode {
|
|
48
|
+
case "consistent", "preserve", "as-needed":
|
|
49
|
+
default:
|
|
50
|
+
mode = "as-needed"
|
|
51
|
+
}
|
|
52
|
+
if mode == "preserve" {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
src := ctx.File.Text()
|
|
57
|
+
|
|
58
|
+
// removable collects each quoted key that is safely unquotable, paired
|
|
59
|
+
// with its bare identifier. anyMustStayQuoted records whether some quoted
|
|
60
|
+
// key cannot be unquoted (drives "consistent").
|
|
61
|
+
type removableKey struct {
|
|
62
|
+
start, end int
|
|
63
|
+
ident string
|
|
64
|
+
}
|
|
65
|
+
var removable []removableKey
|
|
66
|
+
anyMustStayQuoted := false
|
|
67
|
+
|
|
68
|
+
for _, prop := range obj.Properties.Nodes {
|
|
69
|
+
if prop == nil {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
name := propertyKeyName(prop)
|
|
73
|
+
if name == nil || name.Kind != shimast.KindStringLiteral {
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
ks := shimscanner.SkipTrivia(src, name.Pos())
|
|
77
|
+
ke := name.End()
|
|
78
|
+
if ks < 0 || ke <= ks || ke > len(src) {
|
|
79
|
+
return // give up on the whole object rather than risk a partial edit
|
|
80
|
+
}
|
|
81
|
+
ident := unquotableIdentifier(src[ks:ke])
|
|
82
|
+
if ident == "" {
|
|
83
|
+
anyMustStayQuoted = true
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
removable = append(removable, removableKey{start: ks, end: ke, ident: ident})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if len(removable) == 0 {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
// In "consistent" mode, if any key must stay quoted, the whole object is
|
|
93
|
+
// kept quoted, so unquote nothing.
|
|
94
|
+
if mode == "consistent" && anyMustStayQuoted {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var edits []TextEdit
|
|
99
|
+
for _, k := range removable {
|
|
100
|
+
edits = append(edits, TextEdit{Pos: k.start, End: k.end, Text: k.ident})
|
|
101
|
+
}
|
|
102
|
+
ctx.ReportRangeFix(
|
|
103
|
+
edits[0].Pos,
|
|
104
|
+
edits[0].End,
|
|
105
|
+
"Object property key does not need quotes.",
|
|
106
|
+
edits...,
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// propertyKeyName returns the static name node of an object-literal property
|
|
111
|
+
// (assignment, method, or accessor), or nil when it has no static name.
|
|
112
|
+
func propertyKeyName(prop *shimast.Node) *shimast.Node {
|
|
113
|
+
switch prop.Kind {
|
|
114
|
+
case shimast.KindPropertyAssignment:
|
|
115
|
+
if p := prop.AsPropertyAssignment(); p != nil {
|
|
116
|
+
return p.Name()
|
|
117
|
+
}
|
|
118
|
+
case shimast.KindMethodDeclaration,
|
|
119
|
+
shimast.KindGetAccessor,
|
|
120
|
+
shimast.KindSetAccessor:
|
|
121
|
+
return prop.Name()
|
|
122
|
+
}
|
|
123
|
+
return nil
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// unquotableIdentifier returns the bare identifier a quoted string key can
|
|
127
|
+
// safely become, or "" when the key must stay quoted. `raw` includes the
|
|
128
|
+
// surrounding quotes. A key is unquotable only when its content is a plain
|
|
129
|
+
// ASCII identifier with no escapes, conservative on purpose: a
|
|
130
|
+
// numeric-looking key (`"123"`), a unicode key, an escaped key, or anything
|
|
131
|
+
// with a non-identifier byte stays quoted, matching Prettier's never
|
|
132
|
+
// unquoting `"123"` and guaranteeing the result is a valid bare key.
|
|
133
|
+
func unquotableIdentifier(raw string) string {
|
|
134
|
+
if len(raw) < 2 {
|
|
135
|
+
return ""
|
|
136
|
+
}
|
|
137
|
+
q := raw[0]
|
|
138
|
+
if (q != '"' && q != '\'') || raw[len(raw)-1] != q {
|
|
139
|
+
return ""
|
|
140
|
+
}
|
|
141
|
+
inner := raw[1 : len(raw)-1]
|
|
142
|
+
if len(inner) == 0 {
|
|
143
|
+
return ""
|
|
144
|
+
}
|
|
145
|
+
if inner == "__proto__" {
|
|
146
|
+
// A bare `__proto__:` key in an object literal is the spec-special
|
|
147
|
+
// prototype setter (sets [[Prototype]]), whereas a quoted `"__proto__"`
|
|
148
|
+
// key is an ordinary own data property. Unquoting would change runtime
|
|
149
|
+
// semantics, so keep it quoted (Prettier does the same).
|
|
150
|
+
return ""
|
|
151
|
+
}
|
|
152
|
+
for i := 0; i < len(inner); i++ {
|
|
153
|
+
c := inner[i]
|
|
154
|
+
isLetter := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$'
|
|
155
|
+
isDigit := c >= '0' && c <= '9'
|
|
156
|
+
if i == 0 {
|
|
157
|
+
if !isLetter {
|
|
158
|
+
return "" // leading digit (numeric-looking) or other byte: keep quoted
|
|
159
|
+
}
|
|
160
|
+
} else if !isLetter && !isDigit {
|
|
161
|
+
return ""
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return inner
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
func init() {
|
|
168
|
+
Register(formatQuoteProps{})
|
|
169
|
+
}
|