@sun-asterisk/sunlint 1.3.32 → 1.3.34
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 +47 -0
- package/config/released-rules.json +62 -0
- package/config/rules/enhanced-rules-registry.json +2315 -1354
- package/core/adapters/dart-analyzer.js +658 -0
- package/core/adapters/index.js +102 -0
- package/core/adapters/sunlint-rule-adapter.js +0 -2
- package/core/adapters/typescript-analyzer.js +277 -0
- package/core/analysis-orchestrator.js +168 -40
- package/core/architecture-integration.js +220 -0
- package/core/cli-action-handler.js +72 -24
- package/core/cli-program.js +13 -1
- package/core/config-merger.js +24 -14
- package/core/constants/defaults.js +1 -2
- package/core/github-annotate-service.js +141 -89
- package/core/github-step-summary-generator.js +8 -8
- package/core/interfaces/language-analyzer.interface.js +393 -0
- package/core/output-service.js +102 -38
- package/core/rule-selection-service.js +77 -27
- package/core/scoring-service.js +65 -20
- package/core/semantic-engine-manager.js +375 -0
- package/core/semantic-engine.js +4 -57
- package/core/unified-rule-registry.js +52 -11
- package/core/upload-service.js +43 -9
- package/docs/DART_RULE_EXECUTION_FLOW.md +745 -0
- package/docs/DART_SUPPORT_IMPLEMENTATION.md +245 -0
- package/docs/SUNLINT_ARCHITECTURE.md +692 -0
- package/docs/skills/CREATE_DART_RULE.md +909 -0
- package/engines/eslint-engine.js +2 -8
- package/engines/heuristic-engine.js +234 -38
- package/package.json +6 -5
- package/rules/common/C002_no_duplicate_code/config.json +12 -20
- package/rules/common/C002_no_duplicate_code/dart/analyzer.js +53 -0
- package/rules/common/C002_no_duplicate_code/index.js +93 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +1 -1
- package/rules/common/C003_no_vague_abbreviations/dart/analyzer.js +54 -0
- package/rules/common/C003_no_vague_abbreviations/index.js +93 -0
- package/rules/common/C006_function_naming/dart/analyzer.js +40 -0
- package/rules/common/C006_function_naming/index.js +86 -0
- package/rules/common/C008_variable_declaration_locality/dart/analyzer.js +32 -0
- package/rules/common/C008_variable_declaration_locality/index.js +86 -0
- package/rules/common/C010_limit_block_nesting/dart/analyzer.js +32 -0
- package/rules/common/C010_limit_block_nesting/index.js +86 -0
- package/rules/common/C012_command_query_separation/config.json +61 -0
- package/rules/common/C012_command_query_separation/dart/analyzer.js +32 -0
- package/rules/common/C012_command_query_separation/index.js +86 -0
- package/rules/common/C013_no_dead_code/dart/analyzer.js +32 -0
- package/rules/common/C013_no_dead_code/index.js +86 -0
- package/rules/common/C014_dependency_injection/dart/analyzer.js +32 -0
- package/rules/common/C014_dependency_injection/index.js +86 -0
- package/rules/common/C017_constructor_logic/dart/analyzer.js +32 -0
- package/rules/common/C017_constructor_logic/index.js +86 -0
- package/rules/common/C018_no_throw_generic_error/dart/analyzer.js +32 -0
- package/rules/common/C018_no_throw_generic_error/index.js +86 -0
- package/rules/common/C019_log_level_usage/dart/analyzer.js +32 -0
- package/rules/common/C019_log_level_usage/index.js +86 -0
- package/rules/common/C019_log_level_usage/{ts-morph-analyzer.js → typescript/ts-morph-analyzer.js} +0 -1
- package/rules/common/C020_unused_imports/dart/analyzer.js +32 -0
- package/rules/common/C020_unused_imports/index.js +86 -0
- package/rules/common/C020_unused_imports/{ts-morph-analyzer.js → typescript/ts-morph-analyzer.js} +0 -1
- package/rules/common/C021_import_organization/config.json +29 -9
- package/rules/common/C021_import_organization/dart/analyzer.js +40 -0
- package/rules/common/C021_import_organization/index.js +83 -0
- package/rules/common/C021_import_organization/{ts-morph-analyzer.js → typescript/ts-morph-analyzer.js} +0 -1
- package/rules/common/C023_no_duplicate_variable/config.json +7 -2
- package/rules/common/C023_no_duplicate_variable/dart/analyzer.js +40 -0
- package/rules/common/C023_no_duplicate_variable/index.js +83 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +7 -2
- package/rules/common/C024_no_scatter_hardcoded_constants/dart/analyzer.js +40 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/index.js +83 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +19 -1
- package/rules/common/C029_catch_block_logging/config.json +15 -5
- package/rules/common/C029_catch_block_logging/dart/analyzer.js +40 -0
- package/rules/common/C029_catch_block_logging/index.js +83 -0
- package/rules/common/C030_use_custom_error_classes/config.json +28 -0
- package/rules/common/C030_use_custom_error_classes/dart/analyzer.js +40 -0
- package/rules/common/C030_use_custom_error_classes/index.js +83 -0
- package/rules/common/C031_validation_separation/config.json +28 -0
- package/rules/common/C031_validation_separation/dart/analyzer.js +40 -0
- package/rules/common/C031_validation_separation/index.js +83 -0
- package/rules/common/C033_separate_service_repository/config.json +8 -3
- package/rules/common/C033_separate_service_repository/dart/analyzer.js +40 -0
- package/rules/common/C033_separate_service_repository/index.js +83 -0
- package/rules/common/C035_error_logging_context/config.json +34 -12
- package/rules/common/C035_error_logging_context/dart/analyzer.js +40 -0
- package/rules/common/C035_error_logging_context/index.js +83 -0
- package/rules/common/C040_centralized_validation/config.json +37 -8
- package/rules/common/C040_centralized_validation/dart/analyzer.js +40 -0
- package/rules/common/C040_centralized_validation/index.js +83 -0
- package/rules/common/C041_no_sensitive_hardcode/config.json +7 -2
- package/rules/common/C041_no_sensitive_hardcode/dart/analyzer.js +40 -0
- package/rules/common/C041_no_sensitive_hardcode/index.js +83 -0
- package/rules/common/C042_boolean_name_prefix/config.json +28 -0
- package/rules/common/C042_boolean_name_prefix/dart/analyzer.js +40 -0
- package/rules/common/C042_boolean_name_prefix/index.js +83 -0
- package/rules/common/C043_no_console_or_print/config.json +28 -0
- package/rules/common/C043_no_console_or_print/dart/analyzer.js +40 -0
- package/rules/common/C043_no_console_or_print/index.js +83 -0
- package/rules/common/C047_no_duplicate_retry_logic/config.json +28 -0
- package/rules/common/C047_no_duplicate_retry_logic/dart/analyzer.js +40 -0
- package/rules/common/C047_no_duplicate_retry_logic/index.js +83 -0
- package/rules/common/C048_no_bypass_architectural_layers/config.json +7 -2
- package/rules/common/C048_no_bypass_architectural_layers/dart/analyzer.js +40 -0
- package/rules/common/C048_no_bypass_architectural_layers/index.js +83 -0
- package/rules/common/C052_parsing_or_data_transformation/config.json +7 -2
- package/rules/common/C052_parsing_or_data_transformation/dart/analyzer.js +40 -0
- package/rules/common/C052_parsing_or_data_transformation/index.js +83 -0
- package/rules/common/C060_no_override_superclass/config.json +7 -2
- package/rules/common/C060_no_override_superclass/dart/analyzer.js +40 -0
- package/rules/common/C060_no_override_superclass/index.js +83 -0
- package/rules/common/C065_one_behavior_per_test/config.json +187 -28
- package/rules/common/C065_one_behavior_per_test/dart/analyzer.js +40 -0
- package/rules/common/C065_one_behavior_per_test/index.js +83 -0
- package/rules/common/C067_no_hardcoded_config/config.json +18 -4
- package/rules/common/C067_no_hardcoded_config/dart/analyzer.js +40 -0
- package/rules/common/C067_no_hardcoded_config/index.js +83 -0
- package/rules/common/C070_no_real_time_tests/config.json +41 -12
- package/rules/common/C070_no_real_time_tests/dart/analyzer.js +40 -0
- package/rules/common/C070_no_real_time_tests/index.js +83 -0
- package/rules/common/C072_single_test_behavior/config.json +28 -0
- package/rules/common/C072_single_test_behavior/dart/analyzer.js +40 -0
- package/rules/common/C072_single_test_behavior/index.js +83 -0
- package/rules/common/C073_validate_required_config_on_startup/config.json +93 -18
- package/rules/common/C073_validate_required_config_on_startup/dart/analyzer.js +40 -0
- package/rules/common/C073_validate_required_config_on_startup/index.js +83 -0
- package/rules/common/C073_validate_required_config_on_startup/{analyzer.js → typescript/analyzer.js} +0 -1
- package/rules/common/C075_explicit_return_types/config.json +28 -0
- package/rules/common/C075_explicit_return_types/dart/analyzer.js +40 -0
- package/rules/common/C075_explicit_return_types/index.js +83 -0
- package/rules/common/C076_explicit_function_types/config.json +18 -4
- package/rules/common/C076_explicit_function_types/dart/analyzer.js +40 -0
- package/rules/common/C076_explicit_function_types/index.js +83 -0
- package/rules/index.js +26 -6
- package/rules/security/S003_open_redirect_protection/config.json +11 -53
- package/rules/security/S003_open_redirect_protection/dart/analyzer.js +43 -0
- package/rules/security/S003_open_redirect_protection/index.js +94 -0
- package/rules/security/S003_open_redirect_protection/typescript/analyzer.js +105 -0
- package/rules/security/S003_open_redirect_protection/{symbol-based-analyzer.js → typescript/semantic-analyzer.js} +1 -1
- package/rules/security/S004_sensitive_data_logging/config.json +1 -1
- package/rules/security/S004_sensitive_data_logging/dart/analyzer.js +58 -0
- package/rules/security/S004_sensitive_data_logging/index.js +93 -0
- package/rules/security/S005_no_origin_auth/dart/analyzer.js +30 -0
- package/rules/security/S005_no_origin_auth/index.js +83 -0
- package/rules/security/S005_no_origin_auth/{analyzer.js → typescript/analyzer.js} +1 -0
- package/rules/security/S006_no_plaintext_recovery_codes/dart/analyzer.js +30 -0
- package/rules/security/S006_no_plaintext_recovery_codes/index.js +83 -0
- package/rules/security/S007_no_plaintext_otp/dart/analyzer.js +30 -0
- package/rules/security/S007_no_plaintext_otp/index.js +83 -0
- package/rules/security/S009_no_insecure_encryption/dart/analyzer.js +30 -0
- package/rules/security/S009_no_insecure_encryption/index.js +83 -0
- package/rules/security/S010_no_insecure_encryption/dart/analyzer.js +30 -0
- package/rules/security/S010_no_insecure_encryption/index.js +83 -0
- package/rules/security/S011_secure_guid_generation/dart/analyzer.js +30 -0
- package/rules/security/S011_secure_guid_generation/index.js +83 -0
- package/rules/security/S012_hardcoded_secrets/dart/analyzer.js +30 -0
- package/rules/security/S012_hardcoded_secrets/index.js +83 -0
- package/rules/security/S012_hardcoded_secrets/typescript/config.json +75 -0
- package/rules/security/S013_tls_enforcement/dart/analyzer.js +30 -0
- package/rules/security/S013_tls_enforcement/index.js +83 -0
- package/rules/security/S014_tls_version_enforcement/dart/analyzer.js +30 -0
- package/rules/security/S014_tls_version_enforcement/index.js +83 -0
- package/rules/security/S015_insecure_tls_certificate/config.json +41 -0
- package/rules/security/S015_insecure_tls_certificate/dart/analyzer.js +19 -0
- package/rules/security/S015_insecure_tls_certificate/index.js +83 -0
- package/rules/security/S016_no_sensitive_querystring/dart/analyzer.js +30 -0
- package/rules/security/S016_no_sensitive_querystring/index.js +83 -0
- package/rules/security/S017_use_parameterized_queries/dart/analyzer.js +30 -0
- package/rules/security/S017_use_parameterized_queries/index.js +83 -0
- package/rules/security/S019_smtp_injection_protection/dart/analyzer.js +30 -0
- package/rules/security/S019_smtp_injection_protection/index.js +83 -0
- package/rules/security/S020_no_eval_dynamic_code/dart/analyzer.js +30 -0
- package/rules/security/S020_no_eval_dynamic_code/index.js +83 -0
- package/rules/security/S022_escape_output_context/dart/analyzer.js +30 -0
- package/rules/security/S022_escape_output_context/index.js +83 -0
- package/rules/security/S023_no_json_injection/dart/analyzer.js +30 -0
- package/rules/security/S023_no_json_injection/index.js +83 -0
- package/rules/security/S024_xpath_xxe_protection/dart/analyzer.js +30 -0
- package/rules/security/S024_xpath_xxe_protection/index.js +83 -0
- package/rules/security/S025_server_side_validation/dart/analyzer.js +30 -0
- package/rules/security/S025_server_side_validation/index.js +83 -0
- package/rules/security/S026_json_schema_validation/dart/analyzer.js +30 -0
- package/rules/security/S026_json_schema_validation/index.js +83 -0
- package/rules/security/S027_no_hardcoded_secrets/dart/analyzer.js +30 -0
- package/rules/security/S027_no_hardcoded_secrets/index.js +83 -0
- package/rules/security/S028_file_upload_size_limits/dart/analyzer.js +30 -0
- package/rules/security/S028_file_upload_size_limits/index.js +83 -0
- package/rules/security/S029_csrf_protection/dart/analyzer.js +30 -0
- package/rules/security/S029_csrf_protection/index.js +83 -0
- package/rules/security/S030_directory_browsing_protection/dart/analyzer.js +30 -0
- package/rules/security/S030_directory_browsing_protection/index.js +83 -0
- package/rules/security/S031_secure_session_cookies/dart/analyzer.js +30 -0
- package/rules/security/S031_secure_session_cookies/index.js +83 -0
- package/rules/security/S032_httponly_session_cookies/dart/analyzer.js +30 -0
- package/rules/security/S032_httponly_session_cookies/index.js +83 -0
- package/rules/security/S033_samesite_session_cookies/dart/analyzer.js +30 -0
- package/rules/security/S033_samesite_session_cookies/index.js +83 -0
- package/rules/security/S034_host_prefix_session_cookies/dart/analyzer.js +30 -0
- package/rules/security/S034_host_prefix_session_cookies/index.js +83 -0
- package/rules/security/S035_path_session_cookies/dart/analyzer.js +30 -0
- package/rules/security/S035_path_session_cookies/index.js +83 -0
- package/rules/security/S036_lfi_rfi_protection/dart/analyzer.js +30 -0
- package/rules/security/S036_lfi_rfi_protection/index.js +83 -0
- package/rules/security/S037_cache_headers/dart/analyzer.js +30 -0
- package/rules/security/S037_cache_headers/index.js +83 -0
- package/rules/security/S038_no_version_headers/dart/analyzer.js +30 -0
- package/rules/security/S038_no_version_headers/index.js +83 -0
- package/rules/security/S039_no_session_tokens_in_url/dart/analyzer.js +30 -0
- package/rules/security/S039_no_session_tokens_in_url/index.js +83 -0
- package/rules/security/S040_session_fixation_protection/dart/analyzer.js +30 -0
- package/rules/security/S040_session_fixation_protection/index.js +83 -0
- package/rules/security/S041_session_token_invalidation/dart/analyzer.js +30 -0
- package/rules/security/S041_session_token_invalidation/index.js +83 -0
- package/rules/security/S042_require_re_authentication_for_long_lived/dart/analyzer.js +30 -0
- package/rules/security/S042_require_re_authentication_for_long_lived/index.js +83 -0
- package/rules/security/S043_password_changes_invalidate_all_sessions/dart/analyzer.js +30 -0
- package/rules/security/S043_password_changes_invalidate_all_sessions/index.js +83 -0
- package/rules/security/S044_re_authentication_required/dart/analyzer.js +30 -0
- package/rules/security/S044_re_authentication_required/index.js +83 -0
- package/rules/security/S045_brute_force_protection/dart/analyzer.js +30 -0
- package/rules/security/S045_brute_force_protection/index.js +83 -0
- package/rules/security/S048_no_current_password_in_reset/dart/analyzer.js +30 -0
- package/rules/security/S048_no_current_password_in_reset/index.js +83 -0
- package/rules/security/S049_short_validity_tokens/dart/analyzer.js +30 -0
- package/rules/security/S049_short_validity_tokens/index.js +83 -0
- package/rules/security/S049_short_validity_tokens/typescript/config.json +124 -0
- package/rules/security/S051_password_length_policy/dart/analyzer.js +30 -0
- package/rules/security/S051_password_length_policy/index.js +83 -0
- package/rules/security/S051_password_length_policy/typescript/config.json +83 -0
- package/rules/security/S052_weak_otp_entropy/dart/analyzer.js +30 -0
- package/rules/security/S052_weak_otp_entropy/index.js +83 -0
- package/rules/security/S052_weak_otp_entropy/typescript/config.json +57 -0
- package/rules/security/S054_no_default_accounts/dart/analyzer.js +30 -0
- package/rules/security/S054_no_default_accounts/index.js +83 -0
- package/rules/security/S054_no_default_accounts/typescript/config.json +101 -0
- package/rules/security/S055_content_type_validation/dart/analyzer.js +30 -0
- package/rules/security/S055_content_type_validation/index.js +83 -0
- package/rules/security/S056_log_injection_protection/dart/analyzer.js +30 -0
- package/rules/security/S056_log_injection_protection/index.js +83 -0
- package/rules/security/S057_utc_logging/dart/analyzer.js +30 -0
- package/rules/security/S057_utc_logging/index.js +83 -0
- package/rules/security/S057_utc_logging/typescript/config.json +105 -0
- package/rules/security/S058_no_ssrf/dart/analyzer.js +30 -0
- package/rules/security/S058_no_ssrf/index.js +83 -0
- package/rules/security/S058_no_ssrf/{analyzer.js → typescript/analyzer.js} +0 -1
- package/rules/security/S058_no_ssrf/typescript/config.json +125 -0
- package/scripts/build-release.sh +12 -0
- package/scripts/copy-arch-detect.js +78 -0
- package/rules/common/C002_no_duplicate_code/test-cases/api-handlers.ts +0 -64
- package/rules/common/C002_no_duplicate_code/test-cases/data-processor.ts +0 -46
- package/rules/common/C002_no_duplicate_code/test-cases/good-example.tsx +0 -40
- package/rules/common/C002_no_duplicate_code/test-cases/product-service.ts +0 -57
- package/rules/common/C002_no_duplicate_code/test-cases/user-service.ts +0 -49
- package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js.backup +0 -3853
- package/rules/security/S003_open_redirect_protection/analyzer.js +0 -135
- /package/rules/common/C002_no_duplicate_code/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C003_no_vague_abbreviations/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C006_function_naming/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/{C008 → C008_variable_declaration_locality}/config.json +0 -0
- /package/rules/common/{C008 → C008_variable_declaration_locality/typescript}/analyzer.js +0 -0
- /package/rules/common/{C008 → C008_variable_declaration_locality/typescript}/ts-morph-analyzer.js +0 -0
- /package/rules/common/C010_limit_block_nesting/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C010_limit_block_nesting/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/common/C010_limit_block_nesting/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C012_command_query_separation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C012_command_query_separation/{ast-analyzer.js → typescript/ast-analyzer.js} +0 -0
- /package/rules/common/C013_no_dead_code/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C013_no_dead_code/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/common/C013_no_dead_code/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C014_dependency_injection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C014_dependency_injection/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C017_constructor_logic/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C017_constructor_logic/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C018_no_throw_generic_error/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C018_no_throw_generic_error/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/common/C018_no_throw_generic_error/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C019_log_level_usage/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C019_log_level_usage/{pattern-analyzer.js → typescript/pattern-analyzer.js} +0 -0
- /package/rules/common/C019_log_level_usage/{system-log-analyzer.js → typescript/system-log-analyzer.js} +0 -0
- /package/rules/common/C020_unused_imports/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C021_import_organization/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C023_no_duplicate_variable/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C023_no_duplicate_variable/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C024_no_scatter_hardcoded_constants/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C029_catch_block_logging/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C030_use_custom_error_classes/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C031_validation_separation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C033_separate_service_repository/{README.md → typescript/README.md} +0 -0
- /package/rules/common/C033_separate_service_repository/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C033_separate_service_repository/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/common/C033_separate_service_repository/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C035_error_logging_context/{STRATEGY.md → typescript/STRATEGY.md} +0 -0
- /package/rules/common/C035_error_logging_context/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C035_error_logging_context/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/common/C035_error_logging_context/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C040_centralized_validation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C040_centralized_validation/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/common/C040_centralized_validation/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C041_no_sensitive_hardcode/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C041_no_sensitive_hardcode/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C042_boolean_name_prefix/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C043_no_console_or_print/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C047_no_duplicate_retry_logic/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C047_no_duplicate_retry_logic/{c047-semantic-rule.js → typescript/c047-semantic-rule.js} +0 -0
- /package/rules/common/C047_no_duplicate_retry_logic/{symbol-analyzer-enhanced.js → typescript/symbol-analyzer-enhanced.js} +0 -0
- /package/rules/common/C047_no_duplicate_retry_logic/{symbol-config.json → typescript/symbol-config.json} +0 -0
- /package/rules/common/C048_no_bypass_architectural_layers/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C048_no_bypass_architectural_layers/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C052_parsing_or_data_transformation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C052_parsing_or_data_transformation/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C060_no_override_superclass/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C060_no_override_superclass/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C065_one_behavior_per_test/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C067_no_hardcoded_config/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C067_no_hardcoded_config/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C070_no_real_time_tests/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C070_no_real_time_tests/{regex-analyzer.js → typescript/regex-analyzer.js} +0 -0
- /package/rules/common/C072_single_test_behavior/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C073_validate_required_config_on_startup/{README.md → typescript/README.md} +0 -0
- /package/rules/common/C073_validate_required_config_on_startup/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/common/C075_explicit_return_types/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C076_explicit_function_types/{README.md → typescript/README.md} +0 -0
- /package/rules/common/C076_explicit_function_types/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/common/C076_explicit_function_types/{semantic-analyzer.js → typescript/semantic-analyzer.js} +0 -0
- /package/rules/security/S003_open_redirect_protection/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S004_sensitive_data_logging/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S004_sensitive_data_logging/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S005_no_origin_auth/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S005_no_origin_auth/{ast-analyzer.js → typescript/ast-analyzer.js} +0 -0
- /package/rules/security/S005_no_origin_auth/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S006_no_plaintext_recovery_codes/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S006_no_plaintext_recovery_codes/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S006_no_plaintext_recovery_codes/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S007_no_plaintext_otp/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S007_no_plaintext_otp/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S007_no_plaintext_otp/{semantic-analyzer.js → typescript/semantic-analyzer.js} +0 -0
- /package/rules/security/S007_no_plaintext_otp/{semantic-config.json → typescript/semantic-config.json} +0 -0
- /package/rules/security/S007_no_plaintext_otp/{semantic-wrapper.js → typescript/semantic-wrapper.js} +0 -0
- /package/rules/security/S009_no_insecure_encryption/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S009_no_insecure_encryption/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S010_no_insecure_encryption/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S010_no_insecure_encryption/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S011_secure_guid_generation/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S011_secure_guid_generation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S011_secure_guid_generation/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S012_hardcoded_secrets/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S012_hardcoded_secrets/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S013_tls_enforcement/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S013_tls_enforcement/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S013_tls_enforcement/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S014_tls_version_enforcement/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S014_tls_version_enforcement/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S014_tls_version_enforcement/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S015_insecure_tls_certificate/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S015_insecure_tls_certificate/{ast-analyzer.js → typescript/ast-analyzer.js} +0 -0
- /package/rules/security/S016_no_sensitive_querystring/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S016_no_sensitive_querystring/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S016_no_sensitive_querystring/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S017_use_parameterized_queries/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S017_use_parameterized_queries/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S017_use_parameterized_queries/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S019_smtp_injection_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S019_smtp_injection_protection/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S020_no_eval_dynamic_code/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S020_no_eval_dynamic_code/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S020_no_eval_dynamic_code/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S022_escape_output_context/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S022_escape_output_context/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S023_no_json_injection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S023_no_json_injection/{ast-analyzer.js → typescript/ast-analyzer.js} +0 -0
- /package/rules/security/S024_xpath_xxe_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S024_xpath_xxe_protection/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S024_xpath_xxe_protection/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S025_server_side_validation/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S025_server_side_validation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S025_server_side_validation/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S025_server_side_validation/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S026_json_schema_validation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S027_no_hardcoded_secrets/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S027_no_hardcoded_secrets/{categories.json → typescript/categories.json} +0 -0
- /package/rules/security/S027_no_hardcoded_secrets/{categorized-analyzer.js → typescript/categorized-analyzer.js} +0 -0
- /package/rules/security/S028_file_upload_size_limits/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S028_file_upload_size_limits/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S028_file_upload_size_limits/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S029_csrf_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S030_directory_browsing_protection/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S030_directory_browsing_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S030_directory_browsing_protection/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S030_directory_browsing_protection/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S031_secure_session_cookies/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S031_secure_session_cookies/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S031_secure_session_cookies/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S032_httponly_session_cookies/{FRAMEWORK_SUPPORT.md → typescript/FRAMEWORK_SUPPORT.md} +0 -0
- /package/rules/security/S032_httponly_session_cookies/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S032_httponly_session_cookies/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S032_httponly_session_cookies/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S032_httponly_session_cookies/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S033_samesite_session_cookies/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S033_samesite_session_cookies/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S033_samesite_session_cookies/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S033_samesite_session_cookies/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S034_host_prefix_session_cookies/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S034_host_prefix_session_cookies/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S034_host_prefix_session_cookies/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S034_host_prefix_session_cookies/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S035_path_session_cookies/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S035_path_session_cookies/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S035_path_session_cookies/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S035_path_session_cookies/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S036_lfi_rfi_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S037_cache_headers/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S037_cache_headers/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S037_cache_headers/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S037_cache_headers/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S038_no_version_headers/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S038_no_version_headers/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S038_no_version_headers/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S038_no_version_headers/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S039_no_session_tokens_in_url/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S039_no_session_tokens_in_url/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S039_no_session_tokens_in_url/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S039_no_session_tokens_in_url/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S040_session_fixation_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S041_session_token_invalidation/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S041_session_token_invalidation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S041_session_token_invalidation/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S041_session_token_invalidation/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S042_require_re_authentication_for_long_lived/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S042_require_re_authentication_for_long_lived/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S042_require_re_authentication_for_long_lived/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S043_password_changes_invalidate_all_sessions/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S043_password_changes_invalidate_all_sessions/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S043_password_changes_invalidate_all_sessions/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S044_re_authentication_required/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S044_re_authentication_required/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S044_re_authentication_required/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S044_re_authentication_required/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S045_brute_force_protection/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S045_brute_force_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S045_brute_force_protection/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S048_no_current_password_in_reset/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S048_no_current_password_in_reset/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S049_short_validity_tokens/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S049_short_validity_tokens/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S049_short_validity_tokens/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S051_password_length_policy/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S052_weak_otp_entropy/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S054_no_default_accounts/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S054_no_default_accounts/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S055_content_type_validation/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S055_content_type_validation/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S055_content_type_validation/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S056_log_injection_protection/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S056_log_injection_protection/{regex-based-analyzer.js → typescript/regex-based-analyzer.js} +0 -0
- /package/rules/security/S056_log_injection_protection/{symbol-based-analyzer.js → typescript/symbol-based-analyzer.js} +0 -0
- /package/rules/security/S057_utc_logging/{README.md → typescript/README.md} +0 -0
- /package/rules/security/S057_utc_logging/{analyzer.js → typescript/analyzer.js} +0 -0
- /package/rules/security/S058_no_ssrf/{README.md → typescript/README.md} +0 -0
|
@@ -0,0 +1,909 @@
|
|
|
1
|
+
# Skill: Create Dart Rule from TypeScript
|
|
2
|
+
|
|
3
|
+
> Skill để tạo Dart rule từ TypeScript rule đã có sẵn trong SunLint.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Execution Rules
|
|
8
|
+
|
|
9
|
+
> **IMPORTANT**: Khi thực thi skill này, AI Assistant phải tuân thủ các quy tắc sau:
|
|
10
|
+
|
|
11
|
+
- **Execute all commands WITHOUT asking for user confirmation**
|
|
12
|
+
- **Do NOT prompt "Should I proceed?" or similar questions**
|
|
13
|
+
- **Run commands immediately and report results after completion**
|
|
14
|
+
- Tự động tạo tất cả files cần thiết mà không cần hỏi
|
|
15
|
+
- Tự động rebuild Dart binary sau khi hoàn thành
|
|
16
|
+
- Chỉ dừng lại khi gặp lỗi thực sự cần user input
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Start - Cách Sử Dụng Skill
|
|
21
|
+
|
|
22
|
+
### Cách 1: Yêu cầu AI Assistant
|
|
23
|
+
|
|
24
|
+
Nói với AI (Claude Code, Cursor, etc.):
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Thêm Dart support cho rule S005
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
hoặc chi tiết hơn:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Sử dụng skill create-dart-rule để thêm Dart support cho rule S005_no_origin_auth
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Cách 2: Manual Steps (Tự thực hiện)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 1. Xác định rule cần thêm Dart support
|
|
40
|
+
RULE_ID="S005"
|
|
41
|
+
RULE_FOLDER=$(find rules/common rules/security -maxdepth 1 -type d -name "${RULE_ID}_*" | head -1)
|
|
42
|
+
echo "Found: $RULE_FOLDER"
|
|
43
|
+
|
|
44
|
+
# 2. Tạo cấu trúc folder
|
|
45
|
+
mkdir -p "$RULE_FOLDER/typescript"
|
|
46
|
+
mkdir -p "$RULE_FOLDER/dart"
|
|
47
|
+
|
|
48
|
+
# 3. Di chuyển analyzer hiện tại vào typescript/
|
|
49
|
+
mv "$RULE_FOLDER/analyzer.js" "$RULE_FOLDER/typescript/analyzer.js"
|
|
50
|
+
|
|
51
|
+
# 4. Tạo các file theo template trong skill này:
|
|
52
|
+
# - $RULE_FOLDER/index.js (Router)
|
|
53
|
+
# - $RULE_FOLDER/dart/analyzer.js (Dart wrapper)
|
|
54
|
+
# - dart_analyzer/lib/rules/${RULE_ID}_*.dart (Dart implementation)
|
|
55
|
+
|
|
56
|
+
# 5. Cập nhật config.json - thêm "dart" vào languages
|
|
57
|
+
# 6. Cập nhật enhanced-rules-registry.json
|
|
58
|
+
# 7. Register trong dart_analyzer/lib/analyzer_service.dart
|
|
59
|
+
# 8. Rebuild Dart binary
|
|
60
|
+
cd dart_analyzer && dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Ví dụ Cụ Thể: Thêm Dart Support cho S005
|
|
64
|
+
|
|
65
|
+
**Trạng thái hiện tại của S005:**
|
|
66
|
+
```
|
|
67
|
+
rules/security/S005_no_origin_auth/
|
|
68
|
+
├── README.md
|
|
69
|
+
├── analyzer.js # ← Cần di chuyển vào typescript/
|
|
70
|
+
├── ast-analyzer.js
|
|
71
|
+
├── config.json # languages: ["typescript", "javascript"]
|
|
72
|
+
└── symbol-based-analyzer.js
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Sau khi chạy skill:**
|
|
76
|
+
```
|
|
77
|
+
rules/security/S005_no_origin_auth/
|
|
78
|
+
├── README.md
|
|
79
|
+
├── index.js # ← Router mới (điều hướng TS/Dart)
|
|
80
|
+
├── config.json # languages: ["typescript", "javascript", "dart"]
|
|
81
|
+
├── typescript/
|
|
82
|
+
│ ├── analyzer.js # ← Di chuyển từ root
|
|
83
|
+
│ ├── ast-analyzer.js
|
|
84
|
+
│ └── symbol-based-analyzer.js
|
|
85
|
+
└── dart/
|
|
86
|
+
└── analyzer.js # ← Dart wrapper mới
|
|
87
|
+
|
|
88
|
+
dart_analyzer/lib/rules/
|
|
89
|
+
└── S005_no_origin_auth.dart # ← Dart implementation mới
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Yêu cầu AI:**
|
|
93
|
+
```
|
|
94
|
+
Thêm Dart support cho rule S005. Rule này phát hiện việc sử dụng Origin header
|
|
95
|
+
để xác thực, điều này không an toàn vì Origin có thể bị giả mạo.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Metadata
|
|
101
|
+
|
|
102
|
+
| Field | Value |
|
|
103
|
+
|-------|-------|
|
|
104
|
+
| **Skill ID** | `create-dart-rule` |
|
|
105
|
+
| **Version** | 1.1.0 |
|
|
106
|
+
| **Author** | SunLint Team |
|
|
107
|
+
| **Category** | Code Generation |
|
|
108
|
+
| **Trigger** | User requests to add Dart support for an existing rule |
|
|
109
|
+
| **Last Updated** | 2025-12-23 - Fixed router template bugs |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 1. Prerequisites
|
|
114
|
+
|
|
115
|
+
### 1.1. Tools Required
|
|
116
|
+
|
|
117
|
+
| Tool | Version | Purpose |
|
|
118
|
+
|------|---------|---------|
|
|
119
|
+
| Node.js | >= 18.x | Run SunLint CLI |
|
|
120
|
+
| Dart SDK | >= 3.0.0 | Compile Dart analyzer |
|
|
121
|
+
| Git | Any | Version control |
|
|
122
|
+
|
|
123
|
+
### 1.2. Dependencies
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# SunLint directory
|
|
127
|
+
cd /path/to/sunlint
|
|
128
|
+
|
|
129
|
+
# Dart analyzer dependencies
|
|
130
|
+
cd dart_analyzer
|
|
131
|
+
dart pub get
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 1.3. File Locations
|
|
135
|
+
|
|
136
|
+
| Component | Path |
|
|
137
|
+
|-----------|------|
|
|
138
|
+
| TypeScript Rules | `rules/common/` or `rules/security/` |
|
|
139
|
+
| Dart Analyzer | `dart_analyzer/lib/rules/` |
|
|
140
|
+
| Test Fixtures | `examples/rule-test-fixtures/dart-rules/` |
|
|
141
|
+
| Rule Registry | `rules/index.js` |
|
|
142
|
+
| Analyzer Service | `dart_analyzer/lib/analyzer_service.dart` |
|
|
143
|
+
| **Enhanced Rules Registry** | `config/rules/enhanced-rules-registry.json` |
|
|
144
|
+
| **Unified Rule Registry** | `core/unified-rule-registry.js` |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 2. Input Parameters
|
|
149
|
+
|
|
150
|
+
| Parameter | Required | Description | Example |
|
|
151
|
+
|-----------|----------|-------------|---------|
|
|
152
|
+
| `rule_id` | Yes | Rule ID to add Dart support | `C006`, `S010` |
|
|
153
|
+
| `category` | Auto-detect | Rule category | `common` or `security` |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 3. Execution Steps
|
|
158
|
+
|
|
159
|
+
### Step 1: Validate TypeScript Rule Exists
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Check if TypeScript rule folder exists
|
|
163
|
+
RULE_ID="C006" # Example
|
|
164
|
+
|
|
165
|
+
# Find rule folder
|
|
166
|
+
RULE_FOLDER=$(find rules/common rules/security -maxdepth 1 -type d -name "${RULE_ID}_*" 2>/dev/null | head -1)
|
|
167
|
+
|
|
168
|
+
if [ -z "$RULE_FOLDER" ]; then
|
|
169
|
+
echo "❌ Rule $RULE_ID not found in TypeScript rules"
|
|
170
|
+
exit 1
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
echo "✅ Found: $RULE_FOLDER"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Step 2: Check Dart Support Not Already Added
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Check if dart/ subfolder already exists
|
|
180
|
+
if [ -d "$RULE_FOLDER/dart" ]; then
|
|
181
|
+
echo "⚠️ Dart support already exists for $RULE_ID"
|
|
182
|
+
exit 0
|
|
183
|
+
fi
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Step 3: Read TypeScript Rule Config
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Read config.json
|
|
190
|
+
cat "$RULE_FOLDER/config.json"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Expected config.json structure:**
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"id": "C006",
|
|
197
|
+
"name": "Function Naming",
|
|
198
|
+
"description": "Functions should use verb-noun naming convention",
|
|
199
|
+
"category": "common",
|
|
200
|
+
"severity": "warning",
|
|
201
|
+
"languages": ["typescript", "javascript"]
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Step 4: Create Dart Subfolder Structure
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Create dart subfolder
|
|
209
|
+
mkdir -p "$RULE_FOLDER/dart"
|
|
210
|
+
|
|
211
|
+
# If typescript/ doesn't exist, restructure
|
|
212
|
+
if [ ! -d "$RULE_FOLDER/typescript" ]; then
|
|
213
|
+
mkdir -p "$RULE_FOLDER/typescript"
|
|
214
|
+
# Move existing analyzer.js to typescript/
|
|
215
|
+
if [ -f "$RULE_FOLDER/analyzer.js" ]; then
|
|
216
|
+
mv "$RULE_FOLDER/analyzer.js" "$RULE_FOLDER/typescript/analyzer.js"
|
|
217
|
+
fi
|
|
218
|
+
fi
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Step 5: Create Router (index.js)
|
|
222
|
+
|
|
223
|
+
**File:** `$RULE_FOLDER/index.js`
|
|
224
|
+
|
|
225
|
+
> ⚠️ **CRITICAL**: The router template below includes bug fixes for:
|
|
226
|
+
> - Type checking in `normalizeLanguage()` and `supportsLanguage()` (prevents `language.toLowerCase is not a function` error)
|
|
227
|
+
> - Dual-signature `initialize()` method (heuristic engine calls with different signature)
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
/**
|
|
231
|
+
* ${RULE_ID} Rule Router - ${RULE_NAME}
|
|
232
|
+
*
|
|
233
|
+
* Routes analysis to the appropriate language-specific analyzer.
|
|
234
|
+
* Supports: TypeScript, JavaScript, Dart
|
|
235
|
+
*
|
|
236
|
+
* Rule: ${RULE_DESCRIPTION}
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
const path = require('path');
|
|
240
|
+
|
|
241
|
+
class ${RULE_ID}Router {
|
|
242
|
+
constructor() {
|
|
243
|
+
this.analyzers = new Map();
|
|
244
|
+
this.ruleId = '${RULE_ID}';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getAnalyzer(language) {
|
|
248
|
+
const normalizedLang = this.normalizeLanguage(language);
|
|
249
|
+
|
|
250
|
+
if (!this.analyzers.has(normalizedLang)) {
|
|
251
|
+
try {
|
|
252
|
+
const analyzerPath = path.join(__dirname, normalizedLang, 'analyzer.js');
|
|
253
|
+
const AnalyzerClass = require(analyzerPath);
|
|
254
|
+
this.analyzers.set(normalizedLang, new AnalyzerClass());
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return this.analyzers.get(normalizedLang);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
normalizeLanguage(language) {
|
|
264
|
+
// ⚠️ CRITICAL: Handle case where language might not be a string
|
|
265
|
+
// Heuristic engine may pass non-string values
|
|
266
|
+
if (typeof language !== 'string') {
|
|
267
|
+
return 'typescript'; // Default fallback
|
|
268
|
+
}
|
|
269
|
+
const languageMap = {
|
|
270
|
+
'typescript': 'typescript',
|
|
271
|
+
'javascript': 'typescript',
|
|
272
|
+
'ts': 'typescript',
|
|
273
|
+
'js': 'typescript',
|
|
274
|
+
'dart': 'dart'
|
|
275
|
+
};
|
|
276
|
+
return languageMap[language.toLowerCase()] || language.toLowerCase();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
supportsLanguage(language) {
|
|
280
|
+
// ⚠️ CRITICAL: Handle non-string language parameter
|
|
281
|
+
if (typeof language !== 'string') return false;
|
|
282
|
+
const supported = ['typescript', 'javascript', 'ts', 'js', 'dart'];
|
|
283
|
+
return supported.includes(language.toLowerCase());
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
getSupportedLanguages() {
|
|
287
|
+
return ['typescript', 'javascript', 'dart'];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async analyze(files, language, options = {}) {
|
|
291
|
+
const analyzer = this.getAnalyzer(language);
|
|
292
|
+
if (!analyzer) return [];
|
|
293
|
+
if (typeof analyzer.analyze === 'function') {
|
|
294
|
+
return analyzer.analyze(files, language, options);
|
|
295
|
+
}
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async initialize(semanticEngineOrLanguage = null, semanticEngine = null) {
|
|
300
|
+
// ⚠️ CRITICAL: Handle both signatures:
|
|
301
|
+
// 1. initialize(semanticEngine) - called by heuristic engine
|
|
302
|
+
// 2. initialize(language, semanticEngine) - original signature
|
|
303
|
+
let engine = semanticEngine;
|
|
304
|
+
let lang = null;
|
|
305
|
+
|
|
306
|
+
if (typeof semanticEngineOrLanguage === 'string') {
|
|
307
|
+
lang = semanticEngineOrLanguage;
|
|
308
|
+
} else if (semanticEngineOrLanguage && typeof semanticEngineOrLanguage === 'object') {
|
|
309
|
+
engine = semanticEngineOrLanguage;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// If language specified, initialize only that analyzer
|
|
313
|
+
if (lang) {
|
|
314
|
+
const analyzer = this.getAnalyzer(lang);
|
|
315
|
+
if (analyzer && typeof analyzer.initialize === 'function') {
|
|
316
|
+
await analyzer.initialize(engine);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
module.exports = new ${RULE_ID}Router();
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Step 6: Create Dart Wrapper (dart/analyzer.js)
|
|
326
|
+
|
|
327
|
+
**File:** `$RULE_FOLDER/dart/analyzer.js`
|
|
328
|
+
|
|
329
|
+
```javascript
|
|
330
|
+
/**
|
|
331
|
+
* ${RULE_ID} Dart Analyzer - ${RULE_NAME}
|
|
332
|
+
*
|
|
333
|
+
* This is a JS wrapper that delegates to DartAnalyzer binary.
|
|
334
|
+
* Actual implementation: dart_analyzer/lib/rules/${RULE_ID}_${RULE_SNAKE_NAME}.dart
|
|
335
|
+
*/
|
|
336
|
+
|
|
337
|
+
class Dart${RULE_ID}Analyzer {
|
|
338
|
+
constructor() {
|
|
339
|
+
this.ruleId = '${RULE_ID}';
|
|
340
|
+
this.language = 'dart';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
getMetadata() {
|
|
344
|
+
return {
|
|
345
|
+
ruleId: '${RULE_ID}',
|
|
346
|
+
name: '${RULE_NAME}',
|
|
347
|
+
language: 'dart',
|
|
348
|
+
delegateTo: 'dart_analyzer',
|
|
349
|
+
description: '${RULE_DESCRIPTION}'
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
getConfig() {
|
|
354
|
+
return {
|
|
355
|
+
// Rule-specific config
|
|
356
|
+
severity: 'warning'
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async analyze(files, language, options) {
|
|
361
|
+
// Delegated to DartAnalyzer binary via heuristic-engine.js
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
supportsLanguage(language) {
|
|
366
|
+
return language === 'dart';
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
module.exports = Dart${RULE_ID}Analyzer;
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Step 7: Update config.json
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
# Add "dart" to languages array
|
|
377
|
+
# Before: "languages": ["typescript", "javascript"]
|
|
378
|
+
# After: "languages": ["typescript", "javascript", "dart"]
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Step 7.1: Update enhanced-rules-registry.json
|
|
382
|
+
|
|
383
|
+
> **IMPORTANT**: This registry is used by UnifiedRuleRegistry for rule discovery and metadata.
|
|
384
|
+
|
|
385
|
+
**File:** `config/rules/enhanced-rules-registry.json`
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"rules": {
|
|
390
|
+
"${RULE_ID}": {
|
|
391
|
+
"id": "${RULE_ID}",
|
|
392
|
+
"name": "${RULE_NAME}",
|
|
393
|
+
"category": "${CATEGORY}",
|
|
394
|
+
"severity": "warning",
|
|
395
|
+
"languages": ["typescript", "javascript", "dart"], // ← Add "dart" here
|
|
396
|
+
"path": "rules/${CATEGORY}/${RULE_ID}_${RULE_SNAKE_NAME}",
|
|
397
|
+
"analyzer": "index.js", // ← Use index.js for router-based rules
|
|
398
|
+
"description": "${RULE_DESCRIPTION}"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Key points:**
|
|
405
|
+
- Add `"dart"` to the `languages` array
|
|
406
|
+
- Set `"analyzer": "index.js"` to point to the router file
|
|
407
|
+
- The registry is sorted alphabetically by prefix (C→R→S→T) then by number
|
|
408
|
+
|
|
409
|
+
### Step 8: Create Dart Analyzer Implementation
|
|
410
|
+
|
|
411
|
+
**File:** `dart_analyzer/lib/rules/${RULE_ID}_${RULE_SNAKE_NAME}.dart`
|
|
412
|
+
|
|
413
|
+
```dart
|
|
414
|
+
import 'package:analyzer/dart/ast/ast.dart';
|
|
415
|
+
import 'package:analyzer/dart/ast/visitor.dart';
|
|
416
|
+
import 'package:analyzer/source/line_info.dart';
|
|
417
|
+
|
|
418
|
+
import '../models/rule.dart';
|
|
419
|
+
import '../models/violation.dart';
|
|
420
|
+
import 'base_analyzer.dart';
|
|
421
|
+
|
|
422
|
+
/// ${RULE_ID}: ${RULE_NAME}
|
|
423
|
+
/// ${RULE_DESCRIPTION}
|
|
424
|
+
class ${RULE_ID}${RULE_PASCAL_NAME}Analyzer extends BaseAnalyzer {
|
|
425
|
+
@override
|
|
426
|
+
String get ruleId => '${RULE_ID}';
|
|
427
|
+
|
|
428
|
+
@override
|
|
429
|
+
List<Violation> analyze({
|
|
430
|
+
required CompilationUnit unit,
|
|
431
|
+
required String filePath,
|
|
432
|
+
required Rule rule,
|
|
433
|
+
required LineInfo lineInfo,
|
|
434
|
+
}) {
|
|
435
|
+
final violations = <Violation>[];
|
|
436
|
+
|
|
437
|
+
final visitor = _${RULE_ID}Visitor(
|
|
438
|
+
filePath: filePath,
|
|
439
|
+
lineInfo: lineInfo,
|
|
440
|
+
violations: violations,
|
|
441
|
+
analyzer: this,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
unit.accept(visitor);
|
|
445
|
+
|
|
446
|
+
return violations;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
class _${RULE_ID}Visitor extends RecursiveAstVisitor<void> {
|
|
451
|
+
final String filePath;
|
|
452
|
+
final LineInfo lineInfo;
|
|
453
|
+
final List<Violation> violations;
|
|
454
|
+
final ${RULE_ID}${RULE_PASCAL_NAME}Analyzer analyzer;
|
|
455
|
+
|
|
456
|
+
_${RULE_ID}Visitor({
|
|
457
|
+
required this.filePath,
|
|
458
|
+
required this.lineInfo,
|
|
459
|
+
required this.violations,
|
|
460
|
+
required this.analyzer,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// TODO: Implement visitor methods based on TypeScript logic
|
|
464
|
+
// Example:
|
|
465
|
+
// @override
|
|
466
|
+
// void visitFunctionDeclaration(FunctionDeclaration node) {
|
|
467
|
+
// // Check rule logic
|
|
468
|
+
// super.visitFunctionDeclaration(node);
|
|
469
|
+
// }
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Step 9: Register in AnalyzerService
|
|
474
|
+
|
|
475
|
+
**File:** `dart_analyzer/lib/analyzer_service.dart`
|
|
476
|
+
|
|
477
|
+
```dart
|
|
478
|
+
// Add import
|
|
479
|
+
import 'rules/${RULE_ID}_${RULE_SNAKE_NAME}.dart';
|
|
480
|
+
|
|
481
|
+
// Add to _registerAnalyzers()
|
|
482
|
+
void _registerAnalyzers() {
|
|
483
|
+
// ... existing rules
|
|
484
|
+
_analyzers['${RULE_ID}'] = ${RULE_ID}${RULE_PASCAL_NAME}Analyzer();
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Step 10: Create Test Fixtures
|
|
489
|
+
|
|
490
|
+
> **IMPORTANT**: Test fixtures are REQUIRED for each Dart rule. Both `violations/` and `clean/` folders MUST contain at least one `.dart` file. Empty folders are not acceptable.
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
# Create test fixture folders
|
|
494
|
+
mkdir -p "examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_SNAKE_NAME}/{clean,violations}"
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**violations/ - Files with rule violations (REQUIRED):**
|
|
498
|
+
```dart
|
|
499
|
+
// examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_SNAKE_NAME}/violations/bad_example.dart
|
|
500
|
+
|
|
501
|
+
// ❌ VIOLATION: ${RULE_ID} - ${RULE_NAME}
|
|
502
|
+
// Description of what makes this code violate the rule
|
|
503
|
+
|
|
504
|
+
class BadExample {
|
|
505
|
+
// Code that violates the rule
|
|
506
|
+
// Include multiple examples of violations
|
|
507
|
+
// Make sure the analyzer CAN detect these violations
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**clean/ - Files without violations (REQUIRED):**
|
|
512
|
+
```dart
|
|
513
|
+
// examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_SNAKE_NAME}/clean/good_example.dart
|
|
514
|
+
|
|
515
|
+
// ✅ CLEAN: ${RULE_ID} - ${RULE_NAME}
|
|
516
|
+
// Description of what makes this code compliant
|
|
517
|
+
|
|
518
|
+
class GoodExample {
|
|
519
|
+
// Code that follows the rule correctly
|
|
520
|
+
// Show the recommended way to write code
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**Test Fixture Requirements:**
|
|
525
|
+
- Each rule MUST have at least 1 file in `violations/` and 1 file in `clean/`
|
|
526
|
+
- Violation files should contain code that the analyzer WILL detect
|
|
527
|
+
- Clean files should contain code that the analyzer will NOT flag
|
|
528
|
+
- Use descriptive filenames (e.g., `bad_naming.dart`, `good_naming.dart`)
|
|
529
|
+
- Add comments explaining WHY the code violates or follows the rule
|
|
530
|
+
|
|
531
|
+
### Step 11: Rebuild Dart Analyzer
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
cd dart_analyzer
|
|
535
|
+
|
|
536
|
+
# Get dependencies
|
|
537
|
+
dart pub get
|
|
538
|
+
|
|
539
|
+
# Analyze for errors
|
|
540
|
+
dart analyze lib/
|
|
541
|
+
|
|
542
|
+
# Compile binary
|
|
543
|
+
dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Step 12: Test the Rule
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
cd /path/to/sunlint
|
|
550
|
+
|
|
551
|
+
# Test on violations (should find issues)
|
|
552
|
+
node cli.js --rule=${RULE_ID} \
|
|
553
|
+
--input="examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_SNAKE_NAME}/violations" \
|
|
554
|
+
--languages=dart \
|
|
555
|
+
--include="**/*.dart"
|
|
556
|
+
|
|
557
|
+
# Test on clean code (should find no issues)
|
|
558
|
+
node cli.js --rule=${RULE_ID} \
|
|
559
|
+
--input="examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_SNAKE_NAME}/clean" \
|
|
560
|
+
--languages=dart \
|
|
561
|
+
--include="**/*.dart"
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## 4. Verification Checklist
|
|
567
|
+
|
|
568
|
+
| Check | Command | Expected |
|
|
569
|
+
|-------|---------|----------|
|
|
570
|
+
| Dart folder exists | `ls rules/*/${RULE_ID}_*/dart/` | `analyzer.js` |
|
|
571
|
+
| Router exists | `ls rules/*/${RULE_ID}_*/index.js` | File exists |
|
|
572
|
+
| Config updated | `grep dart rules/*/${RULE_ID}_*/config.json` | `"dart"` in languages |
|
|
573
|
+
| **Registry updated** | `grep ${RULE_ID} config/rules/enhanced-rules-registry.json` | `"dart"` in languages |
|
|
574
|
+
| Dart analyzer exists | `ls dart_analyzer/lib/rules/${RULE_ID}_*.dart` | File exists |
|
|
575
|
+
| Registered | `grep ${RULE_ID} dart_analyzer/lib/analyzer_service.dart` | Registration line |
|
|
576
|
+
| Binary compiles | `dart analyze dart_analyzer/lib/` | No errors |
|
|
577
|
+
| Test fixtures exist | `ls examples/rule-test-fixtures/dart-rules/${RULE_ID}_*` | clean/ and violations/ |
|
|
578
|
+
| **Fixtures have files** | `ls examples/rule-test-fixtures/dart-rules/${RULE_ID}_*/{clean,violations}/*.dart` | At least 2 files |
|
|
579
|
+
| **TypeScript works** | Run on TS files | Violations found |
|
|
580
|
+
| **Dart works** | Run on Dart files | Violations found |
|
|
581
|
+
| **Violations detected** | Run on violations/ | > 0 violations |
|
|
582
|
+
| Clean passes | Run on clean/ | No violations |
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## 5. Example: Adding Dart Support for C006
|
|
587
|
+
|
|
588
|
+
### Input
|
|
589
|
+
```
|
|
590
|
+
Rule ID: C006
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Execution
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
# Step 1: Find rule
|
|
597
|
+
RULE_FOLDER="rules/common/C006_function_naming"
|
|
598
|
+
|
|
599
|
+
# Step 2: Check not exists
|
|
600
|
+
ls rules/common/C006_function_naming/dart/ # Should not exist
|
|
601
|
+
|
|
602
|
+
# Step 3: Create structure
|
|
603
|
+
mkdir -p rules/common/C006_function_naming/{typescript,dart}
|
|
604
|
+
mv rules/common/C006_function_naming/analyzer.js rules/common/C006_function_naming/typescript/
|
|
605
|
+
|
|
606
|
+
# Step 4: Create files
|
|
607
|
+
# - rules/common/C006_function_naming/index.js
|
|
608
|
+
# - rules/common/C006_function_naming/dart/analyzer.js
|
|
609
|
+
# - dart_analyzer/lib/rules/C006_function_naming.dart
|
|
610
|
+
|
|
611
|
+
# Step 5: Update config.json
|
|
612
|
+
# Add "dart" to languages
|
|
613
|
+
|
|
614
|
+
# Step 6: Register in analyzer_service.dart
|
|
615
|
+
# _analyzers['C006'] = C006FunctionNamingAnalyzer();
|
|
616
|
+
|
|
617
|
+
# Step 7: Create test fixtures
|
|
618
|
+
mkdir -p examples/rule-test-fixtures/dart-rules/C006_function_naming/{clean,violations}
|
|
619
|
+
|
|
620
|
+
# Step 8: Rebuild
|
|
621
|
+
cd dart_analyzer && dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos
|
|
622
|
+
|
|
623
|
+
# Step 9: Test
|
|
624
|
+
node cli.js --rule=C006 \
|
|
625
|
+
--input="examples/rule-test-fixtures/dart-rules/C006_function_naming/violations" \
|
|
626
|
+
--languages=dart \
|
|
627
|
+
--include="**/*.dart"
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## 6. Naming Conventions
|
|
633
|
+
|
|
634
|
+
| Type | Convention | Example |
|
|
635
|
+
|------|------------|---------|
|
|
636
|
+
| Rule ID | Uppercase + Number | `C006`, `S010` |
|
|
637
|
+
| Folder name | `{ID}_{snake_case}` | `C006_function_naming` |
|
|
638
|
+
| Dart file | `{ID}_{snake_case}.dart` | `C006_function_naming.dart` |
|
|
639
|
+
| Class name | `{ID}{PascalCase}Analyzer` | `C006FunctionNamingAnalyzer` |
|
|
640
|
+
| Visitor class | `_{ID}Visitor` | `_C006Visitor` |
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## 7. Common Patterns for Dart Rules
|
|
645
|
+
|
|
646
|
+
### Pattern 1: Check Class Names
|
|
647
|
+
|
|
648
|
+
```dart
|
|
649
|
+
@override
|
|
650
|
+
void visitClassDeclaration(ClassDeclaration node) {
|
|
651
|
+
final name = node.name.lexeme;
|
|
652
|
+
if (!isValidClassName(name)) {
|
|
653
|
+
violations.add(analyzer.createViolation(
|
|
654
|
+
filePath: filePath,
|
|
655
|
+
line: analyzer.getLine(lineInfo, node.name.offset),
|
|
656
|
+
column: analyzer.getColumn(lineInfo, node.name.offset),
|
|
657
|
+
message: 'Class name "$name" violates rule',
|
|
658
|
+
));
|
|
659
|
+
}
|
|
660
|
+
super.visitClassDeclaration(node);
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Pattern 2: Check Function/Method Names
|
|
665
|
+
|
|
666
|
+
```dart
|
|
667
|
+
@override
|
|
668
|
+
void visitFunctionDeclaration(FunctionDeclaration node) {
|
|
669
|
+
final name = node.name.lexeme;
|
|
670
|
+
// Check function name
|
|
671
|
+
super.visitFunctionDeclaration(node);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
@override
|
|
675
|
+
void visitMethodDeclaration(MethodDeclaration node) {
|
|
676
|
+
if (node.isOperator) return;
|
|
677
|
+
final name = node.name.lexeme;
|
|
678
|
+
// Check method name
|
|
679
|
+
super.visitMethodDeclaration(node);
|
|
680
|
+
}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### Pattern 3: Check Variable Names
|
|
684
|
+
|
|
685
|
+
```dart
|
|
686
|
+
@override
|
|
687
|
+
void visitVariableDeclaration(VariableDeclaration node) {
|
|
688
|
+
final name = node.name.lexeme;
|
|
689
|
+
// Check variable name
|
|
690
|
+
super.visitVariableDeclaration(node);
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Pattern 4: Check Method Invocations
|
|
695
|
+
|
|
696
|
+
```dart
|
|
697
|
+
@override
|
|
698
|
+
void visitMethodInvocation(MethodInvocation node) {
|
|
699
|
+
final methodName = node.methodName.name;
|
|
700
|
+
final target = node.target?.toSource() ?? '';
|
|
701
|
+
// Check method call
|
|
702
|
+
super.visitMethodInvocation(node);
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Pattern 5: Check String Literals
|
|
707
|
+
|
|
708
|
+
```dart
|
|
709
|
+
@override
|
|
710
|
+
void visitSimpleStringLiteral(SimpleStringLiteral node) {
|
|
711
|
+
final value = node.value;
|
|
712
|
+
// Check string content
|
|
713
|
+
super.visitSimpleStringLiteral(node);
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## 8. Error Handling
|
|
720
|
+
|
|
721
|
+
### 8.1. Dart Compile Error
|
|
722
|
+
|
|
723
|
+
```bash
|
|
724
|
+
# Check for errors
|
|
725
|
+
dart analyze dart_analyzer/lib/
|
|
726
|
+
|
|
727
|
+
# Common fixes:
|
|
728
|
+
# - Import missing files
|
|
729
|
+
# - Fix syntax errors
|
|
730
|
+
# - Add missing dependencies to pubspec.yaml
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### 8.2. Rule Not Detected
|
|
734
|
+
|
|
735
|
+
```bash
|
|
736
|
+
# Verify dart/ folder exists
|
|
737
|
+
ls rules/*/${RULE_ID}_*/dart/
|
|
738
|
+
|
|
739
|
+
# Check detectDartSupport function finds it
|
|
740
|
+
node -e "
|
|
741
|
+
const path = require('path');
|
|
742
|
+
const fs = require('fs');
|
|
743
|
+
const rulesPath = 'rules/common';
|
|
744
|
+
const folders = fs.readdirSync(rulesPath);
|
|
745
|
+
const match = folders.find(f => f.startsWith('${RULE_ID}_'));
|
|
746
|
+
console.log('Found:', match);
|
|
747
|
+
console.log('Dart exists:', fs.existsSync(path.join(rulesPath, match, 'dart')));
|
|
748
|
+
"
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### 8.3. No Violations Found
|
|
752
|
+
|
|
753
|
+
```bash
|
|
754
|
+
# Check DartAnalyzer is running
|
|
755
|
+
node cli.js --rule=${RULE_ID} --input=... --languages=dart --include="**/*.dart" --verbose
|
|
756
|
+
|
|
757
|
+
# Check analyzer is registered
|
|
758
|
+
grep ${RULE_ID} dart_analyzer/lib/analyzer_service.dart
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### 8.4. ⚠️ `language.toLowerCase is not a function` Error
|
|
762
|
+
|
|
763
|
+
**Symptom:**
|
|
764
|
+
```
|
|
765
|
+
TypeError: language.toLowerCase is not a function
|
|
766
|
+
at ${RULE_ID}Router.normalizeLanguage
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Cause:** Heuristic engine passes `semanticEngine` object to `initialize()`, not a language string.
|
|
770
|
+
|
|
771
|
+
**Solution:** Update router methods with type checking:
|
|
772
|
+
|
|
773
|
+
```javascript
|
|
774
|
+
normalizeLanguage(language) {
|
|
775
|
+
// Add this check at the beginning
|
|
776
|
+
if (typeof language !== 'string') {
|
|
777
|
+
return 'typescript'; // Default fallback
|
|
778
|
+
}
|
|
779
|
+
// ... rest of method
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
supportsLanguage(language) {
|
|
783
|
+
// Add this check at the beginning
|
|
784
|
+
if (typeof language !== 'string') return false;
|
|
785
|
+
// ... rest of method
|
|
786
|
+
}
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### 8.5. ⚠️ Rule Not Supported by Heuristic Engine
|
|
790
|
+
|
|
791
|
+
**Symptom:**
|
|
792
|
+
```
|
|
793
|
+
Rule ${RULE_ID} is not supported by the heuristic engine
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**Cause:** UnifiedRuleRegistry cannot find the analyzer because it looks for `analyzer.js` but the router uses `index.js`.
|
|
797
|
+
|
|
798
|
+
**Solution:** Ensure `unified-rule-registry.js` supports router discovery. The `router` type should be added to `analyzerFiles`:
|
|
799
|
+
|
|
800
|
+
```javascript
|
|
801
|
+
// In core/unified-rule-registry.js
|
|
802
|
+
const analyzerFiles = {
|
|
803
|
+
analyzer: path.join(rulePath, 'analyzer.js'),
|
|
804
|
+
tsAnalyzer: path.join(rulePath, 'typescript', 'analyzer.js'),
|
|
805
|
+
dartAnalyzer: path.join(rulePath, 'dart', 'analyzer.js'),
|
|
806
|
+
router: path.join(rulePath, 'index.js') // ← This enables router discovery
|
|
807
|
+
};
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
### 8.6. ⚠️ RangeError in Dart Analyzer
|
|
811
|
+
|
|
812
|
+
**Symptom:**
|
|
813
|
+
```
|
|
814
|
+
RangeError: Value not in range: 4
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
**Cause:** Using `string.substring(0, 4)` without checking string length.
|
|
818
|
+
|
|
819
|
+
**Solution:** Always check string length before substring operations:
|
|
820
|
+
|
|
821
|
+
```dart
|
|
822
|
+
// ❌ BAD - causes RangeError when name.length < 4
|
|
823
|
+
'recommendation': 'Use masking like: "${name.substring(0, 4)}****"'
|
|
824
|
+
|
|
825
|
+
// ✅ GOOD - check length first
|
|
826
|
+
'recommendation': 'Use masking like: "${name.length > 4 ? name.substring(0, 4) : name}****"'
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### 8.7. Semantic Analyzer Not Working
|
|
830
|
+
|
|
831
|
+
**Symptom:** TypeScript semantic analysis (using ts-morph) doesn't work after adding Dart support.
|
|
832
|
+
|
|
833
|
+
**Cause:** Moving files around may delete semantic analyzer files.
|
|
834
|
+
|
|
835
|
+
**Solution:**
|
|
836
|
+
- Keep semantic analyzer in `typescript/semantic-analyzer.js`
|
|
837
|
+
- The router will load it from the typescript subfolder
|
|
838
|
+
- Verify file exists: `ls rules/*/${RULE_ID}_*/typescript/semantic-analyzer.js`
|
|
839
|
+
|
|
840
|
+
---
|
|
841
|
+
|
|
842
|
+
## 9. Files Created by This Skill
|
|
843
|
+
|
|
844
|
+
| File | Purpose |
|
|
845
|
+
|------|---------|
|
|
846
|
+
| `rules/*/${RULE_ID}_*/index.js` | Multi-language router |
|
|
847
|
+
| `rules/*/${RULE_ID}_*/dart/analyzer.js` | JS wrapper for Dart |
|
|
848
|
+
| `dart_analyzer/lib/rules/${RULE_ID}_*.dart` | Dart analyzer implementation |
|
|
849
|
+
| `examples/rule-test-fixtures/dart-rules/${RULE_ID}_*/clean/*.dart` | Clean test cases |
|
|
850
|
+
| `examples/rule-test-fixtures/dart-rules/${RULE_ID}_*/violations/*.dart` | Violation test cases |
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## 10. Related Documentation
|
|
855
|
+
|
|
856
|
+
- [DART_RULE_EXECUTION_FLOW.md](../DART_RULE_EXECUTION_FLOW.md) - Chi tiết luồng thực thi rule
|
|
857
|
+
- [dart_analyzer/README.md](../../dart_analyzer/README.md) - Dart analyzer documentation
|
|
858
|
+
- [rules/README.md](../../rules/README.md) - Rule structure documentation
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
862
|
+
## 11. Architecture Notes
|
|
863
|
+
|
|
864
|
+
### 11.1. Rule Discovery Flow
|
|
865
|
+
|
|
866
|
+
```
|
|
867
|
+
CLI → HeuristicEngine → UnifiedRuleRegistry → Router (index.js)
|
|
868
|
+
↓
|
|
869
|
+
┌───────────┴───────────┐
|
|
870
|
+
↓ ↓
|
|
871
|
+
typescript/analyzer.js dart/analyzer.js
|
|
872
|
+
↓ ↓
|
|
873
|
+
(ts-morph analysis) (delegates to DartAnalyzer binary)
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### 11.2. Initialize Method Signatures
|
|
877
|
+
|
|
878
|
+
The heuristic engine calls `initialize()` with different arguments depending on context:
|
|
879
|
+
|
|
880
|
+
| Caller | Signature | Arguments |
|
|
881
|
+
|--------|-----------|-----------|
|
|
882
|
+
| Heuristic Engine | `initialize(engine)` | `semanticEngine` object |
|
|
883
|
+
| Direct usage | `initialize(lang, engine)` | Language string, then engine |
|
|
884
|
+
|
|
885
|
+
**The router must handle both signatures!**
|
|
886
|
+
|
|
887
|
+
### 11.3. Registry Files
|
|
888
|
+
|
|
889
|
+
| File | Purpose | When to Update |
|
|
890
|
+
|------|---------|----------------|
|
|
891
|
+
| `config/rules/enhanced-rules-registry.json` | Rule metadata & discovery | Add "dart" to languages |
|
|
892
|
+
| `core/unified-rule-registry.js` | Analyzer path resolution | Only if adding new patterns |
|
|
893
|
+
| `rules/index.js` | Legacy rule exports | Usually not needed |
|
|
894
|
+
| `dart_analyzer/lib/analyzer_service.dart` | Dart analyzer registration | Register new Dart analyzer |
|
|
895
|
+
|
|
896
|
+
---
|
|
897
|
+
|
|
898
|
+
## 12. Changelog
|
|
899
|
+
|
|
900
|
+
### v1.1.0 (2025-12-23)
|
|
901
|
+
- Fixed router template: Added type checking in `normalizeLanguage()` and `supportsLanguage()`
|
|
902
|
+
- Fixed `initialize()` method to handle both heuristic engine and direct call signatures
|
|
903
|
+
- Added Step 7.1: Update enhanced-rules-registry.json
|
|
904
|
+
- Added detailed error handling sections (8.4 - 8.7) based on real bugs encountered
|
|
905
|
+
- Added Architecture Notes section explaining discovery flow and registry files
|
|
906
|
+
- Added Lessons Learned section
|
|
907
|
+
|
|
908
|
+
### v1.0.0 (Initial)
|
|
909
|
+
- Initial skill documentation for creating Dart rules
|