analyze method

  1. @override
Future<DiagnosticResult> analyze(
  1. AnalyzerContext context
)
override

Public method or function.

Implementation

@override
/// Public method or function.
Future<DiagnosticResult> analyze(AnalyzerContext context) async {
  final startTime = DateTime.now();
  final issues = <DiagnosticIssue>[];
  final fs = context.fileSystem;
  final projectPath = context.projectPath;
  final pubspecPath = fs.join(projectPath, 'pubspec.yaml');

  if (!fs.exists(pubspecPath)) {
    return DiagnosticResult(
      analyzerName: name,
      status: CheckStatus.skipped,
      issues: issues,
      duration: DateTime.now().difference(startTime),
      timestamp: DateTime.now(),
    );
  }

  final pubspec = await PubspecParser.parseFromFile(pubspecPath, fs);
  if (pubspec == null) {
    return DiagnosticResult(
      analyzerName: name,
      status: CheckStatus.skipped,
      issues: issues,
      duration: DateTime.now().difference(startTime),
      timestamp: DateTime.now(),
    );
  }

  final installed = <FirebasePackage>{};
  final devDependencyPackages = <FirebasePackage>{};
  final versionIssues = <String, String>{};

  for (final pkg in FirebasePackage.all) {
    if (pubspec.hasDependency(pkg.packageName)) {
      installed.add(pkg);
      final version = pubspec.dependencyVersion(pkg.packageName)!;
      if (_hasVersionIssue(version)) {
        versionIssues[pkg.packageName] = version;
      }
    }

    if (pubspec.hasDevDependency(pkg.packageName)) {
      devDependencyPackages.add(pkg);
      final version = pubspec.devDependencies[pkg.packageName]!;
      if (_hasVersionIssue(version)) {
        versionIssues[pkg.packageName] = version;
      }
    }
  }

  final allFirebasePkgs = installed.union(devDependencyPackages);
  final hasNonCore = allFirebasePkgs.any((p) => p != FirebasePackage.core);
  final hasCore = allFirebasePkgs.contains(FirebasePackage.core);

  if (hasNonCore && !hasCore) {
    issues.add(
      DiagnosticIssue(
        severity: Severity.critical,
        code: 'FD200',
        title: 'Missing firebase_core',
        description:
            'Firebase packages are installed without firebase_core. '
            'firebase_core is required by all Firebase services.',
        recommendation:
            'Add firebase_core to your dependencies:\n'
            '  firebase_core: ^3.0.0',
        filePath: pubspecPath,
      ),
    );
  }

  for (final pkg in devDependencyPackages) {
    issues.add(
      DiagnosticIssue(
        severity: Severity.error,
        code: 'FD201',
        title: '${pkg.displayName} in dev_dependencies',
        description:
            '${pkg.packageName} is placed in dev_dependencies. '
            'Firebase packages should be in dependencies.',
        recommendation:
            'Move ${pkg.packageName} from dev_dependencies to dependencies.',
        filePath: pubspecPath,
      ),
    );
  }

  for (final entry in versionIssues.entries) {
    issues.add(
      DiagnosticIssue(
        severity: Severity.warning,
        code: 'FD202',
        title: 'Loose version constraint for ${entry.key}',
        description:
            '${entry.key} has a loose version constraint: "${entry.value}". '
            'This may lead to unexpected breaking changes.',
        recommendation:
            'Use a caret constraint like ^2.0.0 instead of "${entry.value}".',
        filePath: pubspecPath,
      ),
    );
  }

  final allPackageNames = installed.map((p) => p.packageName).toSet()
    ..addAll(devDependencyPackages.map((p) => p.packageName));

  final usesFirebase = hasCore || hasNonCore;
  if (usesFirebase && !allPackageNames.contains('firebase_analytics')) {
    issues.add(
      DiagnosticIssue(
        severity: Severity.info,
        code: 'FD203',
        title: 'firebase_analytics not installed',
        description:
            'firebase_analytics is recommended for understanding user engagement. Consider adding if you need analytics.',
        recommendation:
            'Add firebase_analytics to your dependencies:\n  firebase_analytics: ^11.0.0',
        filePath: pubspecPath,
      ),
    );
  }

  if (usesFirebase && !allPackageNames.contains('cloud_firestore')) {
    issues.add(
      DiagnosticIssue(
        severity: Severity.info,
        code: 'FD204',
        title: 'cloud_firestore not installed',
        description:
            'cloud_firestore is recommended for persistent data storage. Consider adding if you need Firestore.',
        recommendation:
            'Add cloud_firestore to your dependencies:\n  cloud_firestore: ^5.0.0',
        filePath: pubspecPath,
      ),
    );
  }

  if (usesFirebase && !allPackageNames.contains('firebase_auth')) {
    issues.add(
      DiagnosticIssue(
        severity: Severity.info,
        code: 'FD205',
        title: 'firebase_auth not installed',
        description:
            'firebase_auth is recommended for user authentication. Consider adding if you need Auth.',
        recommendation:
            'Add firebase_auth to your dependencies:\n  firebase_auth: ^5.0.0',
        filePath: pubspecPath,
      ),
    );
  }

  final hasCriticalOrError = issues.any(
    (i) => i.severity == Severity.critical || i.severity == Severity.error,
  );
  final hasWarning = issues.any((i) => i.severity == Severity.warning);

  CheckStatus status;
  if (hasCriticalOrError) {
    status = CheckStatus.failed;
  } else if (hasWarning) {
    status = CheckStatus.warning;
  } else {
    status = CheckStatus.passed;
  }

  return DiagnosticResult(
    analyzerName: name,
    status: status,
    issues: issues,
    duration: DateTime.now().difference(startTime),
    timestamp: DateTime.now(),
  );
}