Future trustworthyDependency()

in lib/src/report/dependencies.dart [26:245]


Future<ReportSection> trustworthyDependency(PackageContext context) async {
  final pubspec = context.pubspec;
  final packageDir = context.packageDir;
  final toolEnvironment = context.toolEnvironment;

  Future<Subsection> dependencies() async {
    final issues = <Issue>[];
    var bodyPrefix = '';
    var points = 10;
    var status = ReportStatus.passed;
    if (context.pubspecAllowsCurrentSdk) {
      try {
        final outdated = Outdated.fromJson(await toolEnvironment.runPubOutdated(
          packageDir,
          args: [
            '--json',
            '--up-to-date',
            '--no-dev-dependencies',
            '--no-dependency-overrides',
          ],
          usesFlutter: context.usesFlutter,
        ));
        final outdatedVersions = <String, List<OutdatedVersionDescription>>{};
        for (final p in outdated.packages) {
          outdatedVersions[p.package] =
              await computeOutdatedVersions(context, p);
        }
        String constraint(Dependency dependency) {
          if (dependency is HostedDependency) {
            return '`${dependency.version}`';
          } else if (dependency is SdkDependency) {
            return '`${dependency.sdk}`';
          } else if (dependency is GitDependency) {
            return '`${dependency.ref}`';
          } else if (dependency is PathDependency) {
            return '`${dependency.path}`';
          } else {
            return '-';
          }
        }

        String makeTable(List<List<String>> rows) {
          return [
            ['Package', 'Constraint', 'Compatible', 'Latest'],
            [':-', ':-', ':-', ':-'],
            ...rows,
          ].map((r) => '|${r.join('|')}|').join('\n');
        }

        final links = <String>[];
        String linkToPackage(String pkg) {
          final link = '[`$pkg`]: https://pub.dev/packages/$pkg';
          if (!links.contains(link)) {
            links.add(link);
          }
          return '[`$pkg`]';
        }

        final depsTable = outdated.packages
            .where((p) => pubspec.dependencies.containsKey(p.package))
            .map((p) => [
                  linkToPackage(p.package),
                  constraint(pubspec.dependencies[p.package]!),
                  p.upgradable?.version ?? '-',
                  if (outdatedVersions.containsKey(p.package) &&
                      outdatedVersions[p.package]!.isNotEmpty)
                    '**${p.latest?.version ?? '-'}**'
                  else
                    p.latest?.version ?? '-',
                ])
            .toList();

        final transitiveTable = outdated.packages
            .where((p) => !pubspec.dependencies.containsKey(p.package))
            // See: https://github.com/dart-lang/pub/issues/2552
            .where((p) => p.upgradable != null)
            .map((p) => [
                  linkToPackage(p.package),
                  '-',
                  p.upgradable?.version ?? '-',
                  p.latest?.version ?? '-',
                ])
            .toList();

        bodyPrefix = [
          // If we have deps show table
          if (depsTable.isNotEmpty) ...[
            makeTable(depsTable),
            '',
          ] else ...[
            'No dependencies.',
            '',
          ],
          // If we have transitive deps too
          if (transitiveTable.isNotEmpty) ...[
            '<details><summary>Transitive dependencies</summary>',
            '',
            makeTable(transitiveTable),
            '</details>',
            '',
          ],
          'To reproduce run `dart pub outdated --no-dev-dependencies --up-to-date --no-dependency-overrides`.',
          '',
          if (links.isNotEmpty) ...[
            ...links,
            '',
          ],
        ].join('\n');
        for (final l in outdatedVersions.values) {
          if (l.isNotEmpty) {
            final worst = maxBy<OutdatedVersionDescription>(
                l, (a, b) => a.status.index - b.status.index);
            issues.add(worst.issue);
            if (worst.status == OutdatedStatus.outdated) {
              points = 0;
              status = ReportStatus.failed;
            } else if (worst.status == OutdatedStatus.outdatedByPreview ||
                worst.status == OutdatedStatus.outdatedByRecent) {
              // TODO(sigurdm): Could we find some way of indicating that
              // points will be lost soon?
              status = ReportStatus.passed;
            }
          }
        }
      } on ToolException catch (e) {
        issues.add(Issue(
            'Could not run `${context.usesFlutter ? 'flutter' : 'dart'} pub outdated`: ${e.message}'));
        points = 0;
        status = ReportStatus.failed;
      }
    } else {
      issues.add(_unsupportedDartSdk(context,
          command: '${context.usesFlutter ? 'flutter' : 'dart'} pub outdated'));
      points = 0;
      status = ReportStatus.failed;
    }

    return Subsection(
      'All of the package dependencies are supported in the latest version',
      issues,
      points,
      10,
      status,
      bodyPrefix: bodyPrefix,
    );
  }

  Future<Subsection> sdkSupport() async {
    final issues = <Issue>[];
    final sdkConstraint = pubspec.dartSdkConstraint;
    if (sdkConstraint == null) {
      issues.add(Issue('Pubspec.yaml does not have an sdk version constraint.',
          suggestion: 'Try adding an sdk constraint to your `pubspec.yaml`'));
    } else if (!context.pubspecAllowsCurrentSdk) {
      issues.add(_unsupportedDartSdk(context,
          suggestion: 'Try widening the upper boundary of the constraint.'));
    }

    final runtimeInfo = toolEnvironment.runtimeInfo;
    final usesFlutter = pubspec.usesFlutter;

    if (usesFlutter) {
      if (!runtimeInfo.hasFlutter) {
        issues.add(Issue(
            'Found no Flutter in your PATH. Could not determine the current Flutter version.'));
      } else {
        final flutterDartVersion =
            Version.parse(runtimeInfo.flutterInternalDartSdkVersion!);
        final allowsCurrentFlutterDart =
            sdkConstraint?.allows(flutterDartVersion) ?? false;

        if (!allowsCurrentFlutterDart) {
          issues.add(
            Issue(
              'The current SDK constraint does not allow the Dart version used by the latest stable Flutter ($flutterDartVersion)',
              span: tryGetSpanFromYamlMap(pubspec.environment, 'sdk'),
            ),
          );
        } else {
          // TODO(sigurdm): this will not work well locally (installed version will
          // not be latest). Perhaps we should query somewhere for the latest version.
          final currentFlutterVersion = runtimeInfo.flutterVersion == null
              ? null
              : Version.parse(runtimeInfo.flutterVersion!);
          final flutterConstraint = pubspec.flutterSdkConstraint;
          if (flutterConstraint != null &&
              currentFlutterVersion != null &&
              !flutterConstraint.allows(currentFlutterVersion)) {
            issues.add(
              Issue(
                'The current flutter constraint does not allow the latest Flutter ($currentFlutterVersion)',
                span: tryGetSpanFromYamlMap(pubspec.environment, 'flutter'),
              ),
            );
          }
        }
      }
    }
    final status = issues.isEmpty ? ReportStatus.passed : ReportStatus.failed;
    final points = issues.isEmpty ? 10 : 0;
    return Subsection(
      'Package supports latest stable Dart and Flutter SDKs',
      issues,
      points,
      10,
      status,
    );
  }

  final dependencySection = await dependencies();
  final sdkSection = await sdkSupport();
  final subsections = [dependencySection, sdkSection];
  return makeSection(
    id: ReportSectionId.dependency,
    title: 'Support up-to-date dependencies',
    maxPoints: 20,
    subsections: subsections,
    basePath: packageDir,
  );
}