Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teach pub deps to show deps of all workspace packages #4198

Merged
merged 4 commits into from
Mar 25, 2024
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
23 changes: 17 additions & 6 deletions lib/src/ascii_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ String fromFiles(
}

// Walk the map recursively and render to a string.
return fromMap(root);
return fromMap(root, startingAtTop: false);
}

/// Draws a tree from a nested map. Given a map like:
Expand All @@ -108,9 +108,17 @@ String fromFiles(
/// barback
///
/// Items with no children should have an empty map as the value.
String fromMap(Map<String, Map> map) {
///
/// If [startingAtTop] is `false`, the tree will be shown as:
///
/// |-- analyzer
/// | '-- args
/// | | '-- collection
/// ' '---logging
/// '---barback
String fromMap(Map<String, Map> map, {bool startingAtTop = true}) {
var buffer = StringBuffer();
_draw(buffer, '', null, map);
_draw(buffer, '', null, map, depth: startingAtTop ? 0 : 1);
return buffer.toString();
}

Expand All @@ -119,10 +127,11 @@ void _drawLine(
String prefix,
bool isLastChild,
String? name,
bool isRoot,
) {
// Print lines.
buffer.write(prefix);
if (name != null) {
if (!isRoot) {
if (isLastChild) {
buffer.write(log.gray(emoji('└── ', "'-- ")));
} else {
Expand All @@ -147,22 +156,24 @@ void _draw(
Map<String, Map> children, {
bool showAllChildren = false,
bool isLast = false,
required int depth,
}) {
// Don't draw a line for the root node.
if (name != null) _drawLine(buffer, prefix, isLast, name);
if (name != null) _drawLine(buffer, prefix, isLast, name, depth <= 1);

// Recurse to the children.
var childNames = ordered(children.keys);

void drawChild(bool isLastChild, String child) {
var childPrefix = _getPrefix(name == null, isLast);
var childPrefix = _getPrefix(depth <= 1, isLast);
_draw(
buffer,
'$prefix$childPrefix',
child,
children[child] as Map<String, Map>,
showAllChildren: showAllChildren,
isLast: isLastChild,
depth: depth + 1,
);
}

Expand Down
145 changes: 88 additions & 57 deletions lib/src/command/deps.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ class DepsCommand extends PubCommand {
usageException('Cannot combine --json and --style.');
}
final visited = <String>[];
final toVisit = [entrypoint.workspaceRoot.name];
final workspacePackageNames = [
...entrypoint.workspaceRoot.transitiveWorkspace.map((p) => p.name),
];
final toVisit = [...workspacePackageNames];
final packagesJson = <dynamic>[];
final graph = await entrypoint.packageGraph;
while (toVisit.isNotEmpty) {
Expand All @@ -98,14 +101,15 @@ class DepsCommand extends PubCommand {
visited.add(current);
final currentPackage =
(await entrypoint.packageGraph).packages[current]!;
final next = (current == entrypoint.workspaceRoot.name
? entrypoint.workspaceRoot.immediateDependencies
final isRoot = workspacePackageNames.contains(currentPackage.name);
final next = (isRoot
? currentPackage.immediateDependencies
: currentPackage.dependencies)
.keys
.toList();
final dependencyType =
entrypoint.workspaceRoot.pubspec.dependencyType(current);
final kind = currentPackage == entrypoint.workspaceRoot
final kind = isRoot
? 'root'
: (dependencyType == DependencyType.direct
? 'direct'
Expand Down Expand Up @@ -159,8 +163,6 @@ class DepsCommand extends PubCommand {
buffer.writeln("${log.bold('${sdk.name} SDK')} ${sdk.version}");
}

buffer.writeln(_labelPackage(entrypoint.workspaceRoot));

switch (argResults['style']) {
case 'compact':
await _outputCompact(buffer);
Expand All @@ -187,24 +189,32 @@ class DepsCommand extends PubCommand {
Future<void> _outputCompact(
StringBuffer buffer,
) async {
var root = entrypoint.workspaceRoot;
await _outputCompactPackages(
'dependencies',
root.dependencies.keys,
buffer,
);
if (_includeDev) {
var first = true;
for (final root in entrypoint.workspaceRoot.transitiveWorkspace) {
if (!first) {
buffer.write('\n');
}
first = false;

buffer.writeln(_labelPackage(root));
await _outputCompactPackages(
'dependencies',
root.dependencies.keys,
buffer,
);
if (_includeDev) {
await _outputCompactPackages(
'dev dependencies',
root.devDependencies.keys,
buffer,
);
}
await _outputCompactPackages(
'dev dependencies',
root.devDependencies.keys,
'dependency overrides',
root.dependencyOverrides.keys,
buffer,
);
}
await _outputCompactPackages(
'dependency overrides',
root.dependencyOverrides.keys,
buffer,
);

var transitive = await _getTransitiveDependencies();
await _outputCompactPackages('transitive dependencies', transitive, buffer);
Expand Down Expand Up @@ -240,20 +250,28 @@ class DepsCommand extends PubCommand {
/// For each dependency listed, *that* package's immediate dependencies are
/// shown.
Future<void> _outputList(StringBuffer buffer) async {
var root = entrypoint.workspaceRoot;
await _outputListSection('dependencies', root.dependencies.keys, buffer);
if (_includeDev) {
var first = true;
for (final root in entrypoint.workspaceRoot.transitiveWorkspace) {
if (!first) {
buffer.write('\n');
}
first = false;

buffer.writeln(_labelPackage(root));
await _outputListSection('dependencies', root.dependencies.keys, buffer);
if (_includeDev) {
await _outputListSection(
'dev dependencies',
root.devDependencies.keys,
buffer,
);
}
await _outputListSection(
'dev dependencies',
root.devDependencies.keys,
'dependency overrides',
root.dependencyOverrides.keys,
buffer,
);
}
await _outputListSection(
'dependency overrides',
root.dependencyOverrides.keys,
buffer,
);

var transitive = await _getTransitiveDependencies();
if (transitive.isEmpty) return;
Expand Down Expand Up @@ -301,57 +319,66 @@ class DepsCommand extends PubCommand {
// being added to the tree, and the parent map that will receive that
// package.
var toWalk = Queue<(Package, Map<String, Map>)>();
var visited = <String>{entrypoint.workspaceRoot.name};
var visited = <String>{};

// Start with the root dependencies.
var packageTree = <String, Map>{};
final workspacePackageNames = [
...entrypoint.workspaceRoot.transitiveWorkspace.map((p) => p.name),
];
var immediateDependencies =
entrypoint.workspaceRoot.immediateDependencies.keys.toSet();
if (!_includeDev) {
immediateDependencies
.removeAll(entrypoint.workspaceRoot.devDependencies.keys);
}
for (var name in immediateDependencies) {
for (var name in workspacePackageNames) {
toWalk.add((await _getPackage(name), packageTree));
}

// Do a breadth-first walk to the dependency graph.
while (toWalk.isNotEmpty) {
var pair = toWalk.removeFirst();
var (package, map) = pair;
final (package, map) = toWalk.removeFirst();

if (visited.contains(package.name)) {
if (!visited.add(package.name)) {
map[log.gray('${package.name}...')] = <String, Map>{};
continue;
}

visited.add(package.name);

// Populate the map with this package's dependencies.
var childMap = <String, Map>{};
map[_labelPackage(package)] = childMap;

for (var dep in package.dependencies.values) {
toWalk.add((await _getPackage(dep.name), childMap));
final isRoot = workspacePackageNames.contains(package.name);
final children = [
...isRoot
? package.immediateDependencies.keys
: package.dependencies.keys,
];
if (!_includeDev) {
children.removeWhere(package.devDependencies.keys.contains);
}
for (var dep in children) {
toWalk.add((await _getPackage(dep), childMap));
}
}

buffer.write(tree.fromMap(packageTree));
}

String _labelPackage(Package package) =>
'${log.bold(package.name)} ${package.version}';

/// Gets the names of the non-immediate dependencies of the root package.
/// Gets the names of the non-immediate dependencies of the workspace packages.
Future<Set<String>> _getTransitiveDependencies() async {
var transitive = await _getAllDependencies();
var root = entrypoint.workspaceRoot;
transitive.remove(root.name);
transitive.removeAll(root.dependencies.keys);
if (_includeDev) {
transitive.removeAll(root.devDependencies.keys);
for (final root in entrypoint.workspaceRoot.transitiveWorkspace) {
transitive.remove(root.name);
transitive.removeAll(root.dependencies.keys);
if (_includeDev) {
transitive.removeAll(root.devDependencies.keys);
}
transitive.removeAll(root.dependencyOverrides.keys);
}
transitive.removeAll(root.dependencyOverrides.keys);
return transitive;
}

Expand All @@ -361,8 +388,12 @@ class DepsCommand extends PubCommand {
return graph.packages.keys.toSet();
}

var nonDevDependencies = entrypoint.workspaceRoot.dependencies.keys.toList()
..addAll(entrypoint.workspaceRoot.dependencyOverrides.keys);
var nonDevDependencies = [
for (final package in entrypoint.workspaceRoot.transitiveWorkspace) ...[
...package.dependencies.keys,
...package.dependencyOverrides.keys,
],
];
return nonDevDependencies
.expand(graph.transitiveDependencies)
.map((package) => package.name)
Expand All @@ -385,14 +416,14 @@ class DepsCommand extends PubCommand {
/// Outputs all executables reachable from [entrypoint].
Future<void> _outputExecutables(StringBuffer buffer) async {
final graph = await entrypoint.packageGraph;
var packages = [
entrypoint.workspaceRoot,
...(_includeDev
? entrypoint.workspaceRoot.immediateDependencies
: entrypoint.workspaceRoot.dependencies)
.keys
.map((name) => graph.packages[name]!),
];
final packages = {
for (final p in entrypoint.workspaceRoot.transitiveWorkspace) ...[
graph.packages[p.name]!,
...(_includeDev ? p.immediateDependencies : p.dependencies)
.keys
.map((name) => graph.packages[name]!),
],
};

for (var package in packages) {
var executables = package.executableNames;
Expand Down
2 changes: 1 addition & 1 deletion test/ascii_tree_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ void main() {
},
};

ctx.expectNextSection(tree.fromMap(map));
ctx.expectNextSection(tree.fromMap(map, startingAtTop: false));
});
}
Loading