import 'dart:io'; import 'package:mason/mason.dart'; import 'package:yaml/yaml.dart'; void run(HookContext context) async { final logger = context.logger; final String pathUrl = context.vars['path_url'] ?? ''; final String functionName = context.vars['function_name'] ?? ''; final String requestType = (context.vars['request_type'] as String).toLowerCase(); final String requestBody = context.vars['request_body'] as String; if (requestType == 'post' && requestBody.isEmpty) { logger.err('❌ Error: POST requests require a Request Body type.'); return; } final cName = functionName.camelCase; final bool hasData = requestBody.isNotEmpty; final String paramType = hasData ? requestBody : 'dynamic'; // Auto-Detect Project Name String projectName = 'your_project'; final pubspecFile = File('pubspec.yaml'); if (pubspecFile.existsSync()) { final doc = loadYaml(pubspecFile.readAsStringSync()); projectName = doc['name'] ?? projectName; } final progress = logger.progress('🚀 Injecting logic...'); final filesToUpdate = [ 'lib/Grounded/comms/CommsDirections.dart', 'lib/Grounded/comms/Comms.dart', 'lib/Grounded/comms/ConnectComms.dart', 'lib/Grounded/informatics/AppDataManager.dart' ]; // Find Import Path and Filename String? importLine; String? importFileName; if (hasData && !['String','int','double','bool'].contains(requestBody)) { final result = await _findClassData(requestBody, projectName); importLine = result?['line']; importFileName = result?['fileName']; } final String sharedParams = hasData ? '$paramType request' : ''; final String callParams = hasData ? 'request' : ''; // 1. CommsDirections await _inject(filesToUpdate[0], "String ${cName}Path = '$pathUrl';", logger, isClass: false, duplicateCheck: "${cName}Path"); // 2. comms.dart final String dioData = requestType == 'post' ? 'data: request' : 'options: Options(headers: dio.options.headers)'; final String commsMethod = ''' @override Future $cName($sharedParams) async { Pair navigation = await getRequestHeaders(${cName}Path, ""); dio.options.headers = navigation.value; return await dio.$requestType( navigation.key, $dioData ); }'''; await _inject(filesToUpdate[1], commsMethod, logger, importLine: importLine, importFileName: importFileName, duplicateCheck: "Future $cName"); // 3. ConnectComms.dart await _inject(filesToUpdate[2], " Future $cName($sharedParams);", logger, importLine: importLine, importFileName: importFileName, duplicateCheck: "Future $cName"); // 4. AppDataManager.dart final String dataManagerMethod = ''' @override Future $cName($sharedParams) async { return await connectComms.$cName($callParams); }'''; await _inject(filesToUpdate[3], dataManagerMethod, logger, importLine: importLine, importFileName: importFileName, duplicateCheck: "Future $cName"); progress.complete('✨ Injection & Deduplication complete.'); final shouldFormat = logger.confirm('🎨 Run "dart format" on updated files?', defaultValue: false); if (shouldFormat) { for (final path in filesToUpdate) { await Process.run('dart', ['format', path]); } logger.success('✅ Files formatted.'); } } /// Returns both the full import line and just the filename (e.g. user_request.dart) Future?> _findClassData(String className, String projectName) async { final directory = Directory('lib'); if (!directory.existsSync()) return null; final List files = directory.listSync(recursive: true); for (var file in files) { if (file is File && file.path.endsWith('.dart')) { final content = await file.readAsString(); if (content.contains('class $className') || content.contains('enum $className')) { final fileName = file.path.split('/').last; final packagePath = file.path.replaceFirst('lib/Grounded/', ''); return { 'line': "import 'package:$projectName/$packagePath';", 'fileName': fileName, }; } } } return null; } Future _inject(String path, String content, Logger logger, {bool isClass = true, String? importLine, String? importFileName, String? duplicateCheck}) async { final file = File(path); if (!await file.exists()) return; String fileContent = await file.readAsString(); // 1. Prevent Duplicate Method/Variable if (duplicateCheck != null && fileContent.contains(duplicateCheck)) { return; // Silently skip methods that exist } // 2. Smart Import Check: Look for the filename inside any import statement if (importLine != null && importFileName != null) { // Regex matches: import 'any/path/filename.dart'; or "any/path/filename.dart"; final importRegex = RegExp("import ['\"].*?$importFileName['\"];"); if (!importRegex.hasMatch(fileContent)) { fileContent = "$importLine\n$fileContent"; } } if (isClass) { final lastBrace = fileContent.lastIndexOf('}'); if (lastBrace != -1) { fileContent = fileContent.substring(0, lastBrace).trimRight() + '\n\n$content\n' + fileContent.substring(lastBrace); } else { fileContent += '\n\n$content'; } } else { fileContent = fileContent.trimRight() + '\n\n$content\n'; } await file.writeAsString(fileContent); }