in lib/src/command/outdated.dart [117:283]
Future<void> runProtected() async {
final mode = <String, Mode>{
'outdated': _OutdatedMode(),
'null-safety': _NullSafetyMode(cache, entrypoint,
shouldShowSpinner: _shouldShowSpinner),
}[argResults['mode']]!;
final includeDevDependencies = argResults['dev-dependencies'];
final includeDependencyOverrides = argResults['dependency-overrides'];
if (argResults['json'] && argResults.wasParsed('transitive')) {
usageException('Cannot specify both `--json` and `--transitive`\n'
'The json report always includes transitive dependencies.');
}
final rootPubspec = includeDependencyOverrides
? entrypoint.root.pubspec
: stripDependencyOverrides(entrypoint.root.pubspec);
final upgradablePubspec = includeDevDependencies
? rootPubspec
: stripDevDependencies(rootPubspec);
final resolvablePubspec = await mode.resolvablePubspec(upgradablePubspec);
late List<PackageId> upgradablePackages;
late List<PackageId> resolvablePackages;
late bool hasUpgradableResolution;
late bool hasResolvableResolution;
await log.spinner('Resolving', () async {
final upgradablePackagesResult =
await _tryResolve(upgradablePubspec, cache);
hasUpgradableResolution = upgradablePackagesResult != null;
upgradablePackages = upgradablePackagesResult ?? [];
final resolvablePackagesResult =
await _tryResolve(resolvablePubspec, cache);
hasResolvableResolution = resolvablePackagesResult != null;
resolvablePackages = resolvablePackagesResult ?? [];
}, condition: _shouldShowSpinner);
// This list will be empty if there is no lock file.
final currentPackages = entrypoint.lockFile.packages.values;
/// The set of all dependencies (direct and transitive) that are in the
/// closure of the non-dev dependencies from the root in at least one of
/// the current, upgradable and resolvable resolutions.
final nonDevDependencies = <String>{
...await _nonDevDependencyClosure(entrypoint.root, currentPackages),
...await _nonDevDependencyClosure(entrypoint.root, upgradablePackages),
...await _nonDevDependencyClosure(entrypoint.root, resolvablePackages),
};
Future<_PackageDetails> analyzeDependency(PackageRef packageRef) async {
final name = packageRef.name;
final current = entrypoint.lockFile.packages[name];
final upgradable =
upgradablePackages.firstWhereOrNull((id) => id.name == name);
final resolvable =
resolvablePackages.firstWhereOrNull((id) => id.name == name);
// Find the latest version, and if it's overridden.
var latestIsOverridden = false;
PackageId? latest;
// If not overridden in current resolution we can use this
if (!entrypoint.root.pubspec.dependencyOverrides.containsKey(name)) {
latest ??= await _getLatest(current);
}
// If present as a dependency or dev_dependency we use this
latest ??= await _getLatest(rootPubspec.dependencies[name]);
latest ??= await _getLatest(rootPubspec.devDependencies[name]);
// If not overridden and present in either upgradable or resolvable we
// use this reference to find the latest
if (!upgradablePubspec.dependencyOverrides.containsKey(name)) {
latest ??= await _getLatest(upgradable);
}
if (!resolvablePubspec.dependencyOverrides.containsKey(name)) {
latest ??= await _getLatest(resolvable);
}
// Otherwise, we might simply not have a latest, when a transitive
// dependency is overridden the source can depend on which versions we
// are picking. This is not a problem on `pub.dev` because it does not
// allow 3rd party pub servers, but other servers might. Hence, we choose
// to fallback to using the overridden source for latest.
if (latest == null) {
latest ??= await _getLatest(current ?? upgradable ?? resolvable);
latestIsOverridden = true;
}
return _PackageDetails(
name,
await _describeVersion(
current,
entrypoint.root.pubspec.dependencyOverrides.containsKey(name),
),
await _describeVersion(
upgradable,
upgradablePubspec.dependencyOverrides.containsKey(name),
),
await _describeVersion(
resolvable,
resolvablePubspec.dependencyOverrides.containsKey(name),
),
await _describeVersion(
latest,
latestIsOverridden,
),
_kind(name, entrypoint, nonDevDependencies),
);
}
final rows = <_PackageDetails>[];
final visited = <String>{
entrypoint.root.name,
};
// Add all dependencies from the lockfile.
for (final id in [
...currentPackages,
...upgradablePackages,
...resolvablePackages
]) {
if (!visited.add(id.name)) continue;
rows.add(await analyzeDependency(id.toRef()));
}
if (!includeDevDependencies) {
rows.removeWhere((r) => r.kind == _DependencyKind.dev);
}
rows.sort();
final showAll = argResults['show-all'] || argResults['up-to-date'];
if (argResults['json']) {
await _outputJson(
rows,
mode,
showAll: showAll,
includeDevDependencies: includeDevDependencies,
);
} else {
if (argResults.wasParsed('color')) {
forceColors = argResults['color'];
}
final useColors =
argResults.wasParsed('color') ? argResults['color'] : canUseAnsiCodes;
await _outputHuman(rows, mode,
useColors: useColors,
showAll: showAll,
includeDevDependencies: includeDevDependencies,
lockFileExists: fileExists(entrypoint.lockFilePath),
hasDirectDependencies: rootPubspec.dependencies.values.any(
// Test if it contains non-SDK dependencies
(c) => c.source is! SdkSource,
),
hasDevDependencies: rootPubspec.devDependencies.values.any(
// Test if it contains non-SDK dependencies
(c) => c.source is! SdkSource,
),
showTransitiveDependencies: showTransitiveDependencies,
hasUpgradableResolution: hasUpgradableResolution,
hasResolvableResolution: hasResolvableResolution,
directory: path.normalize(directory));
}
}