run method

  1. @override
Future<void> run(
  1. List<String> args, {
  2. required Logger logger,
})
override

The execution body of the command.

Implementation

@override
Future<void> run(List<String> args, {required Logger logger}) async {
  final finder = ProjectFinder();
  final projectRoot = finder.findProjectRoot();
  final project = FlutterProject(projectRoot);

  final configManager = ConfigManager(projectRoot);
  if (!configManager.configExists()) {
    logger.error('No deep link configuration found. Please run "deeplink init" first.');
    return;
  }

  final config = configManager.load();
  logger.println('=== Deep Link Integration Doctor Report ===\n');

  var allPassed = true;

  // 1. Check Configuration Scheme
  if (config.schemes.isEmpty) {
    logger.error('No scheme configured in ${Constants.configFile}');
    allPassed = false;
  } else {
    logger.success('Configuration schemes: ${config.schemes.join(", ")}');
  }

  // 2. Check Android Manifest
  if (FileUtils.exists(project.androidManifestPath)) {
    final manifestContent = FileUtils.readAsString(project.androidManifestPath);
    var manifestOk = true;
    for (final scheme in config.schemes) {
      if (!manifestContent.contains('android:scheme="$scheme"')) {
        logger.error('AndroidManifest.xml: Missing intent-filter data tag for scheme "$scheme".');
        manifestOk = false;
        allPassed = false;
      }
    }
    if (config.androidAppLinks) {
      if (!manifestContent.contains('android:autoVerify="true"')) {
        logger.warning('AndroidManifest.xml: Android App Links enabled, but missing android:autoVerify="true" attribute.');
      }
    }
    if (manifestOk) {
      logger.success('AndroidManifest.xml is configured correctly.');
    }
  } else {
    logger.error('AndroidManifest.xml not found at ${project.androidManifestPath}');
    allPassed = false;
  }

  // 3. Check MainActivity code injection
  try {
    final mainActivityInfo = project.findMainActivity();
    final content = FileUtils.readAsString(mainActivityInfo.path);
    if (content.contains('custom_deeplink') || content.contains('DEEPLINK_CHANNEL')) {
      logger.success('MainActivity (${mainActivityInfo.language.name}) has MethodChannel code injected.');
    } else {
      logger.error('MainActivity (${mainActivityInfo.language.name}): Missing MethodChannel and deep linking hooks.');
      allPassed = false;
    }
  } catch (e) {
    logger.error('MainActivity verification failed: ${e.toString()}');
    allPassed = false;
  }

  // 4. Check iOS Info.plist
  if (FileUtils.exists(project.iosPlistPath)) {
    final plistContent = FileUtils.readAsString(project.iosPlistPath);
    var plistOk = true;
    for (final scheme in config.schemes) {
      if (!plistContent.contains('<string>$scheme</string>')) {
        logger.error('Info.plist: Missing CFBundleURLSchemes entry for scheme "$scheme".');
        plistOk = false;
        allPassed = false;
      }
    }
    if (plistOk) {
      logger.success('Info.plist is configured correctly.');
    }
  } else {
    logger.error('Info.plist not found at ${project.iosPlistPath}');
    allPassed = false;
  }

  // 5. Check iOS AppDelegate Swift file
  if (FileUtils.exists(project.iosAppDelegatePath)) {
    final content = FileUtils.readAsString(project.iosAppDelegatePath);
    if (content.contains('custom_deeplink') && content.contains('FlutterMethodChannel')) {
      logger.success('AppDelegate.swift has MethodChannel and url delegates injected.');
    } else {
      logger.error('AppDelegate.swift: Missing MethodChannel or delegate handlers.');
      allPassed = false;
    }
  } else {
    logger.error('AppDelegate.swift not found at ${project.iosAppDelegatePath}');
    allPassed = false;
  }

  // 6. Check Generated Flutter files
  final expectedFiles = [
    'deep_link_service.dart',
    'deep_link_route.dart',
    'deep_link_parser.dart',
    'deep_link_routes.dart',
    'deep_link_constants.dart',
    'deep_link_utils.dart',
    if (config.generateDeepLinkManager) 'deep_link_manager.dart',
  ];

  var flutterFilesOk = true;
  for (final filename in expectedFiles) {
    final path = PathUtils.join(projectRoot, Constants.libDeepLinkDir, filename);
    if (!FileUtils.exists(path)) {
      logger.error('Flutter Deep Link file missing: $filename');
      flutterFilesOk = false;
      allPassed = false;
    }
  }
  if (flutterFilesOk) {
    logger.success('All required Flutter deep linking source files exist.');
  }

  logger.println('');
  if (allPassed) {
    logger.success('Doctor found NO issues. Your deep linking configuration is healthy.');
  } else {
    logger.warning('Doctor found issues that need fixing. Run "deeplink init" or "deeplink regenerate" to resolve them.');
  }
}