Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 80 additions & 45 deletions lib/src/command/outdated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,24 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
'The json report always includes transitive dependencies.');
}

final rootPubspec = includeDependencyOverrides
? entrypoint.workspaceRoot.pubspec
: stripDependencyOverrides(entrypoint.workspaceRoot.pubspec);

final upgradablePubspec = includeDevDependencies
? rootPubspec
: stripDevDependencies(rootPubspec);
/// The workspace root with dependency overrides removed if requested.
final baseWorkspace = includeDependencyOverrides
? entrypoint.workspaceRoot
: entrypoint.workspaceRoot.transformWorkspace(
(package) => stripDependencyOverrides(package.pubspec),
);

final resolvablePubspec = await mode.resolvablePubspec(upgradablePubspec);
/// [baseWorkspace] with dev-dependencies removed if requested.
final upgradableWorkspace = includeDevDependencies
? baseWorkspace
: baseWorkspace.transformWorkspace(
(package) => stripDevDependencies(package.pubspec),
);

/// [upgradableWorkspace] with upper bounds removed.
final resolvableWorkspace = upgradableWorkspace.transformWorkspace(
(package) => mode.resolvablePubspec(package.pubspec),
);
late List<PackageId> upgradablePackages;
late List<PackageId> resolvablePackages;
late bool hasUpgradableResolution;
Expand All @@ -146,23 +154,15 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
'Resolving',
() async {
final upgradablePackagesResult = await _tryResolve(
Package(
upgradablePubspec,
entrypoint.workspaceRoot.dir,
entrypoint.workspaceRoot.workspaceChildren,
),
upgradableWorkspace,
cache,
lockFile: entrypoint.lockFile,
);
hasUpgradableResolution = upgradablePackagesResult != null;
upgradablePackages = upgradablePackagesResult ?? [];

final resolvablePackagesResult = await _tryResolve(
Package(
resolvablePubspec,
entrypoint.workspaceRoot.dir,
entrypoint.workspaceRoot.workspaceChildren,
),
resolvableWorkspace,
cache,
lockFile: entrypoint.lockFile,
);
Expand Down Expand Up @@ -206,8 +206,7 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
var latestIsOverridden = false;
PackageId? latest;
// If not overridden in current resolution we can use this
if (!entrypoint.workspaceRoot.pubspec.dependencyOverrides
.containsKey(name)) {
if (!hasOverride(entrypoint.workspaceRoot, name)) {
latest ??= await cache.getLatest(
current?.toRef(),
version: current?.version,
Expand All @@ -216,23 +215,27 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
}
// If present as a dependency or dev_dependency we use this
latest ??= await cache.getLatest(
rootPubspec.dependencies[name]?.toRef(),
allDependencies(baseWorkspace)
.firstWhereOrNull((r) => r.name == name)
?.toRef(),
allowPrereleases: prereleases,
);
latest ??= await cache.getLatest(
rootPubspec.devDependencies[name]?.toRef(),
allDevDependencies(baseWorkspace)
.firstWhereOrNull((r) => r.name == name)
?.toRef(),
allowPrereleases: prereleases,
);
// If not overridden and present in either upgradable or resolvable we
// use this reference to find the latest
if (!upgradablePubspec.dependencyOverrides.containsKey(name)) {
if (!hasOverride(upgradableWorkspace, name)) {
latest ??= await cache.getLatest(
upgradable?.toRef(),
version: upgradable?.version,
allowPrereleases: prereleases,
);
}
if (!resolvablePubspec.dependencyOverrides.containsKey(name)) {
if (!hasOverride(resolvableWorkspace, name)) {
latest ??= await cache.getLatest(
resolvable?.toRef(),
version: resolvable?.version,
Expand Down Expand Up @@ -278,12 +281,12 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');

final upgradableVersionDetails = await _describeVersion(
upgradable,
upgradablePubspec.dependencyOverrides.containsKey(name),
hasOverride(upgradableWorkspace, name),
);

final resolvableVersionDetails = await _describeVersion(
resolvable,
resolvablePubspec.dependencyOverrides.containsKey(name),
hasOverride(resolvableWorkspace, name),
);

final latestVersionDetails = await _describeVersion(
Expand Down Expand Up @@ -332,8 +335,9 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');

final rows = <_PackageDetails>[];

final visited = <String>{
entrypoint.workspaceRoot.name,
final visited = {
...entrypoint.workspaceRoot.transitiveWorkspace
.map((package) => package.name),
};
// Add all dependencies from the lockfile.
for (final id in [
Expand Down Expand Up @@ -361,20 +365,21 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
includeDevDependencies: includeDevDependencies,
);
} else {
bool isNotFromSdk(PackageRange range) => range.source is! SdkSource;
await _outputHuman(
rows,
mode,
useColors: canUseAnsiCodes,
showAll: showAll,
includeDevDependencies: includeDevDependencies,
lockFileExists: fileExists(entrypoint.lockFilePath),
hasDirectDependencies: rootPubspec.dependencies.values.any(
hasDirectDependencies: allDependencies(baseWorkspace).any(
// Test if it contains non-SDK dependencies
(c) => c.source is! SdkSource,
isNotFromSdk,
),
hasDevDependencies: rootPubspec.devDependencies.values.any(
hasDevDependencies: allDevDependencies(baseWorkspace).any(
// Test if it contains non-SDK dependencies
(c) => c.source is! SdkSource,
isNotFromSdk,
),
showTransitiveDependencies: showTransitiveDependencies,
hasUpgradableResolution: hasUpgradableResolution,
Expand Down Expand Up @@ -420,23 +425,27 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
}

/// Computes the closure of the graph of dependencies (not including
/// `dev_dependencies` from [root], given the package versions
/// in [resolution].
/// `dev_dependencies`) from all workspace packages in [workspaceRoot], given
/// the package versions in [resolution].
///
/// The [resolution] is allowed to be a partial (or empty) resolution not
/// satisfying all the dependencies of [root].
/// satisfying all the dependencies of [workspaceRoot].
Future<Set<String>> _nonDevDependencyClosure(
Package root,
Package workspaceRoot,
Iterable<PackageId> resolution,
) async {
final nameToId = {for (final id in resolution) id.name: id};

final nonDevDependencies = <String>{root.name};
final queue = [...root.dependencies.keys];
final result = <String>{
for (final p in workspaceRoot.transitiveWorkspace) p.name,
};
final queue = [
for (final p in workspaceRoot.transitiveWorkspace) ...p.dependencies.keys,
];

while (queue.isNotEmpty) {
final name = queue.removeLast();
if (!nonDevDependencies.add(name)) {
if (!result.add(name)) {
continue;
}

Expand All @@ -448,7 +457,7 @@ Consider using the Dart 2.19 sdk to migrate to null safety.''');
queue.addAll(pubspec.dependencies.keys);
}

return nonDevDependencies;
return result;
}
}

Expand Down Expand Up @@ -784,7 +793,7 @@ abstract class _Mode {
String get upgradeConstrained;
String get allSafe;

Future<Pubspec> resolvablePubspec(Pubspec pubspec);
Pubspec resolvablePubspec(Pubspec pubspec);
}

class _OutdatedMode implements _Mode {
Expand Down Expand Up @@ -873,8 +882,8 @@ Showing outdated packages$directoryDescription.
}

@override
Future<Pubspec> resolvablePubspec(Pubspec? pubspec) async {
return stripVersionBounds(pubspec!);
Pubspec resolvablePubspec(Pubspec pubspec) {
return stripVersionBounds(pubspec);
}
}

Expand Down Expand Up @@ -972,9 +981,9 @@ _DependencyKind _kind(
Entrypoint entrypoint,
Set<String> nonDevTransitive,
) {
if (entrypoint.workspaceRoot.dependencies.containsKey(name)) {
if (hasDependency(entrypoint.workspaceRoot, name)) {
return _DependencyKind.direct;
} else if (entrypoint.workspaceRoot.devDependencies.containsKey(name)) {
} else if (hasDevDependency(entrypoint.workspaceRoot, name)) {
return _DependencyKind.dev;
} else {
if (nonDevTransitive.contains(name)) {
Expand Down Expand Up @@ -1093,3 +1102,29 @@ class _FormattedString {

static String _noFormat(String x) => x;
}

/// Whether the package [name] is overridden anywhere in the workspace rooted at
/// [workspaceRoot].
bool hasOverride(Package workspaceRoot, String name) {
return workspaceRoot.allOverridesInWorkspace.containsKey(name);
}

/// Whether the package [name] is depended on directly anywhere in the workspace
/// rooted at [workspaceRoot].
bool hasDependency(Package workspaceRoot, String name) {
return workspaceRoot.transitiveWorkspace
.any((p) => p.dependencies.containsKey(name));
}

/// Whether the package [name] is dev-depended on directly anywhere in the workspace
/// rooted at [workspaceRoot].
bool hasDevDependency(Package workspaceRoot, String name) {
return workspaceRoot.transitiveWorkspace
.any((p) => p.devDependencies.containsKey(name));
}
Comment on lines +1106 to +1124
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these would be neat as extension methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll refactor these in a follow-up


Iterable<PackageRange> allDependencies(Package workspaceRoot) =>
workspaceRoot.transitiveWorkspace.expand((p) => p.dependencies.values);

Iterable<PackageRange> allDevDependencies(Package workspaceRoot) =>
workspaceRoot.transitiveWorkspace.expand((p) => p.devDependencies.values);
50 changes: 50 additions & 0 deletions test/outdated/outdated_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,56 @@ Future<void> main() async {
await ctx.runOutdatedTests();
});

testWithGolden('reports dependencies from all of workspace', (ctx) async {
final server = await servePackages();
server.serve('myapp', '1.2.4');
server.serve('dep', '0.9.0', deps: {'myapp': '^1.2.3'});
server.serve('dep', '0.8.0', deps: {'myapp': '^1.2.3'});
server.serve('dep', '1.0.0');
server.serve('dep_a', '0.9.0');
server.serve('dep_a', '1.0.0');
server.serve('dev_dep_a', '0.9.0');
server.serve('dev_dep_a', '1.0.0');

await d.dir(appPath, [
d.libPubspec(
'myapp',
'1.2.3',
deps: {'dep': '^0.9.0'},
extras: {
'workspace': ['pkgs/a'],
},
sdk: '^3.5.0',
),
d.dir('pkgs', [
d.dir('a', [
d.libPubspec(
'a',
'1.1.1',
deps: {'myapp': '^1.0.0', 'dep_a': '^0.9.0'},
devDeps: {'dev_dep_a': '^0.9.0'},
extras: {
'dependency_overrides': {'dep': '0.8.0'},
},
resolutionWorkspace: true,
),
]),
]),
]).create();

await pubGet(
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
);

server.serve('dep', '0.9.5');
server.serve('dep_a', '0.9.5');
server.serve('dev_dep_a', '0.9.5');

await ctx.runOutdatedTests(
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
);
});

testWithGolden('Handles SDK dependencies', (ctx) async {
await servePackages()
..serve(
Expand Down
Loading