@sun-asterisk/sunlint 1.3.43 → 1.3.44
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/dart_analyzer/README.md +226 -0
- package/dart_analyzer/analysis_options.yaml +66 -0
- package/dart_analyzer/bin/sunlint-dart-macos +0 -0
- package/dart_analyzer/bin/sunlint_dart_analyzer.dart +124 -0
- package/dart_analyzer/lib/analyzer_service.dart +625 -0
- package/dart_analyzer/lib/json_rpc_server.dart +275 -0
- package/dart_analyzer/lib/models/rule.dart +67 -0
- package/dart_analyzer/lib/models/symbol_table.dart +607 -0
- package/dart_analyzer/lib/models/violation.dart +69 -0
- package/dart_analyzer/lib/rules/base_analyzer.dart +52 -0
- package/dart_analyzer/lib/rules/common/C002_no_duplicate_code.dart +344 -0
- package/dart_analyzer/lib/rules/common/C003_no_vague_abbreviations.dart +318 -0
- package/dart_analyzer/lib/rules/common/C006_function_naming.dart +219 -0
- package/dart_analyzer/lib/rules/common/C008_variable_declaration_locality.dart +205 -0
- package/dart_analyzer/lib/rules/common/C010_limit_block_nesting.dart +162 -0
- package/dart_analyzer/lib/rules/common/C012_command_query_separation.dart +214 -0
- package/dart_analyzer/lib/rules/common/C013_no_dead_code.dart +225 -0
- package/dart_analyzer/lib/rules/common/C014_dependency_injection.dart +249 -0
- package/dart_analyzer/lib/rules/common/C017_constructor_logic.dart +158 -0
- package/dart_analyzer/lib/rules/common/C018_no_throw_generic_error.dart +141 -0
- package/dart_analyzer/lib/rules/common/C019_log_level_usage.dart +165 -0
- package/dart_analyzer/lib/rules/common/C020_unused_imports.dart +128 -0
- package/dart_analyzer/lib/rules/common/C021_import_organization.dart +86 -0
- package/dart_analyzer/lib/rules/common/C023_no_duplicate_variable.dart +112 -0
- package/dart_analyzer/lib/rules/common/C024_no_scatter_hardcoded_constants.dart +79 -0
- package/dart_analyzer/lib/rules/common/C029_catch_block_logging.dart +81 -0
- package/dart_analyzer/lib/rules/common/C030_use_custom_error_classes.dart +77 -0
- package/dart_analyzer/lib/rules/common/C031_validation_separation.dart +90 -0
- package/dart_analyzer/lib/rules/common/C033_separate_service_repository.dart +80 -0
- package/dart_analyzer/lib/rules/common/C035_error_logging_context.dart +148 -0
- package/dart_analyzer/lib/rules/common/C040_centralized_validation.dart +84 -0
- package/dart_analyzer/lib/rules/common/C041_no_sensitive_hardcode.dart +103 -0
- package/dart_analyzer/lib/rules/common/C042_boolean_name_prefix.dart +105 -0
- package/dart_analyzer/lib/rules/common/C043_no_console_or_print.dart +101 -0
- package/dart_analyzer/lib/rules/common/C047_no_duplicate_retry_logic.dart +94 -0
- package/dart_analyzer/lib/rules/common/C048_no_bypass_architectural_layers.dart +132 -0
- package/dart_analyzer/lib/rules/common/C052_parsing_or_data_transformation.dart +95 -0
- package/dart_analyzer/lib/rules/common/C060_no_override_superclass.dart +81 -0
- package/dart_analyzer/lib/rules/common/C065_one_behavior_per_test.dart +83 -0
- package/dart_analyzer/lib/rules/common/C067_no_hardcoded_config.dart +89 -0
- package/dart_analyzer/lib/rules/common/C070_no_real_time_tests.dart +99 -0
- package/dart_analyzer/lib/rules/common/C072_single_test_behavior.dart +78 -0
- package/dart_analyzer/lib/rules/common/C073_validate_required_config_on_startup.dart +82 -0
- package/dart_analyzer/lib/rules/common/C075_explicit_return_types.dart +85 -0
- package/dart_analyzer/lib/rules/common/C076_explicit_function_types.dart +104 -0
- package/dart_analyzer/lib/rules/dart/D001_recommended_lint_rules.dart +309 -0
- package/dart_analyzer/lib/rules/dart/D002_dispose_resources.dart +338 -0
- package/dart_analyzer/lib/rules/dart/D003_prefer_widgets_over_methods.dart +273 -0
- package/dart_analyzer/lib/rules/dart/D004_avoid_shrinkwrap_listview.dart +154 -0
- package/dart_analyzer/lib/rules/dart/D005_limit_widget_nesting.dart +265 -0
- package/dart_analyzer/lib/rules/dart/D006_prefer_extracting_large_callbacks.dart +135 -0
- package/dart_analyzer/lib/rules/dart/D007_prefer_init_first_dispose_last.dart +150 -0
- package/dart_analyzer/lib/rules/dart/D008_avoid_long_functions.dart +394 -0
- package/dart_analyzer/lib/rules/dart/D009_limit_function_parameters.dart +179 -0
- package/dart_analyzer/lib/rules/dart/D010_limit_cyclomatic_complexity.dart +257 -0
- package/dart_analyzer/lib/rules/dart/D011_prefer_named_parameters.dart +152 -0
- package/dart_analyzer/lib/rules/dart/D012_prefer_named_boolean_parameters.dart +156 -0
- package/dart_analyzer/lib/rules/dart/D013_single_public_class.dart +246 -0
- package/dart_analyzer/lib/rules/dart/D014_unsafe_collection_access.dart +202 -0
- package/dart_analyzer/lib/rules/dart/D015_copywith_all_parameters.dart +125 -0
- package/dart_analyzer/lib/rules/dart/D016_project_should_have_tests.dart +134 -0
- package/dart_analyzer/lib/rules/dart/D017_pubspec_dependencies_review.dart +187 -0
- package/dart_analyzer/lib/rules/dart/D018_remove_commented_code.dart +196 -0
- package/dart_analyzer/lib/rules/dart/D019_avoid_single_child_multi_child_widget.dart +161 -0
- package/dart_analyzer/lib/rules/dart/D020_limit_if_else_branches.dart +125 -0
- package/dart_analyzer/lib/rules/dart/D021_avoid_negated_boolean_checks.dart +227 -0
- package/dart_analyzer/lib/rules/dart/D022_use_setstate_correctly.dart +269 -0
- package/dart_analyzer/lib/rules/dart/D023_avoid_unnecessary_method_overrides.dart +191 -0
- package/dart_analyzer/lib/rules/dart/D024_avoid_unnecessary_stateful_widget.dart +194 -0
- package/dart_analyzer/lib/rules/dart/D025_avoid_nested_conditional_expressions.dart +90 -0
- package/dart_analyzer/lib/rules/security/S001_backend_auth_communications.dart +155 -0
- package/dart_analyzer/lib/rules/security/S002_os_command_injection.dart +159 -0
- package/dart_analyzer/lib/rules/security/S003_open_redirect_protection.dart +208 -0
- package/dart_analyzer/lib/rules/security/S004_sensitive_data_logging.dart +391 -0
- package/dart_analyzer/lib/rules/security/S005_trusted_service_authorization.dart +182 -0
- package/dart_analyzer/lib/rules/security/S006_no_default_credentials.dart +208 -0
- package/dart_analyzer/lib/rules/security/S007_output_encoding.dart +224 -0
- package/dart_analyzer/lib/rules/security/S008_svg_content_sanitization.dart +211 -0
- package/dart_analyzer/lib/rules/security/S009_no_insecure_encryption.dart +160 -0
- package/dart_analyzer/lib/rules/security/S010_use_csprng.dart +184 -0
- package/dart_analyzer/lib/rules/security/S011_ech_tls_config.dart +175 -0
- package/dart_analyzer/lib/rules/security/S012_hardcoded_secrets.dart +255 -0
- package/dart_analyzer/lib/rules/security/S013_tls_enforcement.dart +148 -0
- package/dart_analyzer/lib/rules/security/S014_tls_version_enforcement.dart +117 -0
- package/dart_analyzer/lib/rules/security/S015_insecure_tls_certificate.dart +315 -0
- package/dart_analyzer/lib/rules/security/S016_no_sensitive_querystring.dart +244 -0
- package/dart_analyzer/lib/rules/security/S017_use_parameterized_queries.dart +191 -0
- package/dart_analyzer/lib/rules/security/S018_no_sensitive_browser_storage.dart +175 -0
- package/dart_analyzer/lib/rules/security/S019_smtp_injection_protection.dart +166 -0
- package/dart_analyzer/lib/rules/security/S020_no_eval_dynamic_code.dart +149 -0
- package/dart_analyzer/lib/rules/security/S021_referrer_policy.dart +146 -0
- package/dart_analyzer/lib/rules/security/S022_escape_output_context.dart +111 -0
- package/dart_analyzer/lib/rules/security/S023_no_json_injection.dart +550 -0
- package/dart_analyzer/lib/rules/security/S024_xpath_xxe_protection.dart +299 -0
- package/dart_analyzer/lib/rules/security/S025_server_side_validation.dart +140 -0
- package/dart_analyzer/lib/rules/security/S026_tls_all_connections.dart +196 -0
- package/dart_analyzer/lib/rules/security/S027_mtls_certificate_validation.dart +195 -0
- package/dart_analyzer/lib/rules/security/S028_file_upload_size_limits.dart +186 -0
- package/dart_analyzer/lib/rules/security/S029_csrf_protection.dart +171 -0
- package/dart_analyzer/lib/rules/security/S030_directory_browsing_protection.dart +144 -0
- package/dart_analyzer/lib/rules/security/S031_secure_session_cookies.dart +118 -0
- package/dart_analyzer/lib/rules/security/S032_httponly_session_cookies.dart +114 -0
- package/dart_analyzer/lib/rules/security/S033_samesite_session_cookies.dart +120 -0
- package/dart_analyzer/lib/rules/security/S034_host_prefix_session_cookies.dart +160 -0
- package/dart_analyzer/lib/rules/security/S035_separate_app_hostnames.dart +117 -0
- package/dart_analyzer/lib/rules/security/S036_lfi_rfi_protection.dart +188 -0
- package/dart_analyzer/lib/rules/security/S037_cache_headers.dart +113 -0
- package/dart_analyzer/lib/rules/security/S038_no_version_headers.dart +114 -0
- package/dart_analyzer/lib/rules/security/S039_tls_certificate_validation.dart +131 -0
- package/dart_analyzer/lib/rules/security/S040_session_fixation_protection.dart +155 -0
- package/dart_analyzer/lib/rules/security/S041_session_token_invalidation.dart +201 -0
- package/dart_analyzer/lib/rules/security/S042_require_re_authentication_for_long_lived.dart +158 -0
- package/dart_analyzer/lib/rules/security/S043_password_changes_invalidate_all_sessions.dart +88 -0
- package/dart_analyzer/lib/rules/security/S044_re_authentication_required.dart +119 -0
- package/dart_analyzer/lib/rules/security/S045_brute_force_protection.dart +253 -0
- package/dart_analyzer/lib/rules/security/S046_jwt_algorithm_allowlist.dart +113 -0
- package/dart_analyzer/lib/rules/security/S047_oauth_pkce_protection.dart +124 -0
- package/dart_analyzer/lib/rules/security/S048_oauth_redirect_uri_validation.dart +134 -0
- package/dart_analyzer/lib/rules/security/S049_short_validity_tokens.dart +145 -0
- package/dart_analyzer/lib/rules/security/S050_reference_tokens_entropy.dart +234 -0
- package/dart_analyzer/lib/rules/security/S051_password_length_policy.dart +171 -0
- package/dart_analyzer/lib/rules/security/S052_weak_otp_entropy.dart +107 -0
- package/dart_analyzer/lib/rules/security/S053_generic_error_messages.dart +159 -0
- package/dart_analyzer/lib/rules/security/S054_no_default_accounts.dart +141 -0
- package/dart_analyzer/lib/rules/security/S055_content_type_validation.dart +324 -0
- package/dart_analyzer/lib/rules/security/S056_log_injection_protection.dart +119 -0
- package/dart_analyzer/lib/rules/security/S057_utc_logging.dart +114 -0
- package/dart_analyzer/lib/rules/security/S058_no_ssrf.dart +175 -0
- package/dart_analyzer/lib/rules/security/S059_disable_debug_mode.dart +172 -0
- package/dart_analyzer/lib/rules/security/S060_password_minimum_length.dart +170 -0
- package/dart_analyzer/lib/symbol_table_extractor.dart +510 -0
- package/dart_analyzer/lib/utils/common_utils.dart +26 -0
- package/dart_analyzer/pubspec.lock +557 -0
- package/dart_analyzer/pubspec.yaml +39 -0
- package/dart_analyzer/test/fixtures/complex_code.dart +95 -0
- package/docs/GENERATED_FILE_HANDLING_SUMMARY.md +2 -2
- package/package.json +3 -2
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import 'dart:async';
|
|
2
|
+
import 'dart:convert';
|
|
3
|
+
import 'dart:io';
|
|
4
|
+
|
|
5
|
+
import 'analyzer_service.dart';
|
|
6
|
+
|
|
7
|
+
/// JSON-RPC 2.0 Server for SunLint Dart Analyzer
|
|
8
|
+
/// Communicates via STDIO with the Node.js parent process
|
|
9
|
+
class JsonRpcServer {
|
|
10
|
+
final AnalyzerService _analyzerService;
|
|
11
|
+
bool _running = false;
|
|
12
|
+
StreamSubscription? _subscription;
|
|
13
|
+
|
|
14
|
+
JsonRpcServer() : _analyzerService = AnalyzerService();
|
|
15
|
+
|
|
16
|
+
/// Start the JSON-RPC server
|
|
17
|
+
/// Listens on stdin for requests, writes responses to stdout
|
|
18
|
+
Future<void> start() async {
|
|
19
|
+
if (_running) {
|
|
20
|
+
_logError('Server already running');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_running = true;
|
|
25
|
+
_log('SunLint Dart Analyzer started');
|
|
26
|
+
_log('Listening for JSON-RPC requests on stdin...');
|
|
27
|
+
|
|
28
|
+
// Listen on stdin for newline-delimited JSON
|
|
29
|
+
_subscription = stdin
|
|
30
|
+
.transform(utf8.decoder)
|
|
31
|
+
.transform(const LineSplitter())
|
|
32
|
+
.listen(
|
|
33
|
+
_handleLine,
|
|
34
|
+
onError: _handleError,
|
|
35
|
+
onDone: _handleDone,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Stop the server
|
|
40
|
+
Future<void> stop() async {
|
|
41
|
+
_running = false;
|
|
42
|
+
await _subscription?.cancel();
|
|
43
|
+
_subscription = null;
|
|
44
|
+
_log('Server stopped');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Handle a single line of input (one JSON-RPC request)
|
|
48
|
+
Future<void> _handleLine(String line) async {
|
|
49
|
+
if (line.trim().isEmpty) return;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
final request = jsonDecode(line) as Map<String, dynamic>;
|
|
53
|
+
final response = await _processRequest(request);
|
|
54
|
+
_sendResponse(response);
|
|
55
|
+
} catch (e, stackTrace) {
|
|
56
|
+
_logError('Failed to process request: $e');
|
|
57
|
+
_logError('Stack trace: $stackTrace');
|
|
58
|
+
_sendResponse(_createErrorResponse(
|
|
59
|
+
null,
|
|
60
|
+
-32700,
|
|
61
|
+
'Parse error: $e',
|
|
62
|
+
));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Process a JSON-RPC request and return a response
|
|
67
|
+
Future<Map<String, dynamic>> _processRequest(
|
|
68
|
+
Map<String, dynamic> request) async {
|
|
69
|
+
// Validate JSON-RPC 2.0 format
|
|
70
|
+
final jsonrpc = request['jsonrpc'];
|
|
71
|
+
if (jsonrpc != '2.0') {
|
|
72
|
+
return _createErrorResponse(
|
|
73
|
+
request['id'],
|
|
74
|
+
-32600,
|
|
75
|
+
'Invalid Request: jsonrpc must be "2.0"',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
final method = request['method'] as String?;
|
|
80
|
+
if (method == null) {
|
|
81
|
+
return _createErrorResponse(
|
|
82
|
+
request['id'],
|
|
83
|
+
-32600,
|
|
84
|
+
'Invalid Request: method is required',
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
final id = request['id'];
|
|
89
|
+
final params = request['params'] as Map<String, dynamic>? ?? {};
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
final result = await _invokeMethod(method, params);
|
|
93
|
+
return _createSuccessResponse(id, result);
|
|
94
|
+
} on MethodNotFoundException catch (e) {
|
|
95
|
+
return _createErrorResponse(id, -32601, e.message);
|
|
96
|
+
} on InvalidParamsException catch (e) {
|
|
97
|
+
return _createErrorResponse(id, -32602, e.message);
|
|
98
|
+
} catch (e, stackTrace) {
|
|
99
|
+
_logError('Error in method $method: $e');
|
|
100
|
+
_logError('Stack trace: $stackTrace');
|
|
101
|
+
return _createErrorResponse(id, -32603, 'Internal error: $e');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Invoke the requested method
|
|
106
|
+
Future<dynamic> _invokeMethod(
|
|
107
|
+
String method, Map<String, dynamic> params) async {
|
|
108
|
+
switch (method) {
|
|
109
|
+
case 'initialize':
|
|
110
|
+
return await _handleInitialize(params);
|
|
111
|
+
case 'analyze':
|
|
112
|
+
return await _handleAnalyze(params);
|
|
113
|
+
case 'getSymbolTable':
|
|
114
|
+
return await _handleGetSymbolTable(params);
|
|
115
|
+
case 'shutdown':
|
|
116
|
+
return await _handleShutdown(params);
|
|
117
|
+
case 'ping':
|
|
118
|
+
return _handlePing(params);
|
|
119
|
+
default:
|
|
120
|
+
throw MethodNotFoundException('Method not found: $method');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Handle initialize request
|
|
125
|
+
Future<Map<String, dynamic>> _handleInitialize(
|
|
126
|
+
Map<String, dynamic> params) async {
|
|
127
|
+
final projectPath = params['projectPath'] as String?;
|
|
128
|
+
if (projectPath == null) {
|
|
129
|
+
throw InvalidParamsException('projectPath is required');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
final targetFiles = (params['targetFiles'] as List<dynamic>?)
|
|
133
|
+
?.map((e) => e as String)
|
|
134
|
+
.toList();
|
|
135
|
+
|
|
136
|
+
final sdkPath = params['sdkPath'] as String?;
|
|
137
|
+
|
|
138
|
+
await _analyzerService.initialize(
|
|
139
|
+
projectPath: projectPath,
|
|
140
|
+
targetFiles: targetFiles,
|
|
141
|
+
sdkPath: sdkPath,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
'success': true,
|
|
146
|
+
'version': '1.0.0',
|
|
147
|
+
'capabilities': {
|
|
148
|
+
'analyze': true,
|
|
149
|
+
'getSymbolTable': true,
|
|
150
|
+
'rules': _analyzerService.supportedRules,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Handle analyze request
|
|
156
|
+
Future<Map<String, dynamic>> _handleAnalyze(
|
|
157
|
+
Map<String, dynamic> params) async {
|
|
158
|
+
final filePath = params['filePath'] as String?;
|
|
159
|
+
if (filePath == null) {
|
|
160
|
+
throw InvalidParamsException('filePath is required');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
final rulesData = params['rules'] as List<dynamic>?;
|
|
164
|
+
|
|
165
|
+
final violations = await _analyzerService.analyzeFile(
|
|
166
|
+
filePath: filePath,
|
|
167
|
+
rulesData: rulesData,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
'violations': violations.map((v) => v.toJson()).toList(),
|
|
172
|
+
'fileAnalyzed': filePath,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Handle getSymbolTable request
|
|
177
|
+
Future<Map<String, dynamic>> _handleGetSymbolTable(
|
|
178
|
+
Map<String, dynamic> params) async {
|
|
179
|
+
final filePath = params['filePath'] as String?;
|
|
180
|
+
if (filePath == null) {
|
|
181
|
+
throw InvalidParamsException('filePath is required');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
final symbolTable = await _analyzerService.getSymbolTable(filePath);
|
|
185
|
+
|
|
186
|
+
if (symbolTable == null) {
|
|
187
|
+
return {'error': 'Failed to parse file', 'filePath': filePath};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return symbolTable.toJson();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Handle shutdown request
|
|
194
|
+
Future<Map<String, dynamic>> _handleShutdown(
|
|
195
|
+
Map<String, dynamic> params) async {
|
|
196
|
+
_log('Shutdown requested');
|
|
197
|
+
|
|
198
|
+
// Schedule shutdown after response is sent
|
|
199
|
+
Future.delayed(const Duration(milliseconds: 100), () {
|
|
200
|
+
stop();
|
|
201
|
+
exit(0);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return {'success': true};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Handle ping request (for health checks)
|
|
208
|
+
Map<String, dynamic> _handlePing(Map<String, dynamic> params) {
|
|
209
|
+
return {
|
|
210
|
+
'pong': true,
|
|
211
|
+
'timestamp': DateTime.now().toIso8601String(),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/// Create a success response
|
|
216
|
+
Map<String, dynamic> _createSuccessResponse(dynamic id, dynamic result) {
|
|
217
|
+
return {
|
|
218
|
+
'jsonrpc': '2.0',
|
|
219
|
+
'id': id,
|
|
220
|
+
'result': result,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/// Create an error response
|
|
225
|
+
Map<String, dynamic> _createErrorResponse(
|
|
226
|
+
dynamic id, int code, String message) {
|
|
227
|
+
return {
|
|
228
|
+
'jsonrpc': '2.0',
|
|
229
|
+
'id': id,
|
|
230
|
+
'error': {
|
|
231
|
+
'code': code,
|
|
232
|
+
'message': message,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// Send a response to stdout
|
|
238
|
+
void _sendResponse(Map<String, dynamic> response) {
|
|
239
|
+
final json = jsonEncode(response);
|
|
240
|
+
stdout.writeln(json);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Handle stream errors
|
|
244
|
+
void _handleError(dynamic error) {
|
|
245
|
+
_logError('Stream error: $error');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// Handle stream done
|
|
249
|
+
void _handleDone() {
|
|
250
|
+
_log('Input stream closed');
|
|
251
|
+
_running = false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/// Log to stderr (debug output)
|
|
255
|
+
void _log(String message) {
|
|
256
|
+
stderr.writeln('[SunLint Dart] $message');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// Log error to stderr
|
|
260
|
+
void _logError(String message) {
|
|
261
|
+
stderr.writeln('[SunLint Dart ERROR] $message');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/// Exception for method not found errors
|
|
266
|
+
class MethodNotFoundException implements Exception {
|
|
267
|
+
final String message;
|
|
268
|
+
MethodNotFoundException(this.message);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/// Exception for invalid params errors
|
|
272
|
+
class InvalidParamsException implements Exception {
|
|
273
|
+
final String message;
|
|
274
|
+
InvalidParamsException(this.message);
|
|
275
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/// Represents a lint rule configuration
|
|
2
|
+
class Rule {
|
|
3
|
+
/// Rule ID (e.g., 'C001', 'N001', 'S003')
|
|
4
|
+
final String id;
|
|
5
|
+
|
|
6
|
+
/// Rule name
|
|
7
|
+
final String name;
|
|
8
|
+
|
|
9
|
+
/// Rule description
|
|
10
|
+
final String description;
|
|
11
|
+
|
|
12
|
+
/// Severity level
|
|
13
|
+
final String severity;
|
|
14
|
+
|
|
15
|
+
/// Rule category
|
|
16
|
+
final String category;
|
|
17
|
+
|
|
18
|
+
/// Whether the rule is enabled
|
|
19
|
+
final bool enabled;
|
|
20
|
+
|
|
21
|
+
/// Rule-specific configuration
|
|
22
|
+
final Map<String, dynamic> config;
|
|
23
|
+
|
|
24
|
+
Rule({
|
|
25
|
+
required this.id,
|
|
26
|
+
this.name = '',
|
|
27
|
+
this.description = '',
|
|
28
|
+
this.severity = 'warning',
|
|
29
|
+
this.category = 'common',
|
|
30
|
+
this.enabled = true,
|
|
31
|
+
this.config = const {},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/// Get a config value with default
|
|
35
|
+
T getConfig<T>(String key, T defaultValue) {
|
|
36
|
+
final value = config[key];
|
|
37
|
+
if (value == null) return defaultValue;
|
|
38
|
+
if (value is T) return value;
|
|
39
|
+
return defaultValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Get threshold value (common for complexity rules)
|
|
43
|
+
int get threshold => getConfig('threshold', 10);
|
|
44
|
+
|
|
45
|
+
/// Get max depth (common for nesting rules)
|
|
46
|
+
int get maxDepth => getConfig('maxDepth', 4);
|
|
47
|
+
|
|
48
|
+
factory Rule.fromJson(Map<String, dynamic> json) => Rule(
|
|
49
|
+
id: json['id'] as String,
|
|
50
|
+
name: json['name'] as String? ?? '',
|
|
51
|
+
description: json['description'] as String? ?? '',
|
|
52
|
+
severity: json['severity'] as String? ?? 'warning',
|
|
53
|
+
category: json['category'] as String? ?? 'common',
|
|
54
|
+
enabled: json['enabled'] as bool? ?? true,
|
|
55
|
+
config: json['config'] as Map<String, dynamic>? ?? {},
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
Map<String, dynamic> toJson() => {
|
|
59
|
+
'id': id,
|
|
60
|
+
'name': name,
|
|
61
|
+
'description': description,
|
|
62
|
+
'severity': severity,
|
|
63
|
+
'category': category,
|
|
64
|
+
'enabled': enabled,
|
|
65
|
+
if (config.isNotEmpty) 'config': config,
|
|
66
|
+
};
|
|
67
|
+
}
|