| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library pub.command.list; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:collection'; | |
| 9 | |
| 10 import '../ascii_tree.dart' as tree; | |
| 11 import '../command.dart'; | |
| 12 import '../log.dart' as log; | |
| 13 import '../package.dart'; | |
| 14 import '../package_graph.dart'; | |
| 15 import '../utils.dart'; | |
| 16 | |
| 17 /// Handles the `deps` pub command. | |
| 18 class DepsCommand extends PubCommand { | |
| 19 String get name => "deps"; | |
| 20 String get description => "Print package dependencies."; | |
| 21 List<String> get aliases => const ["dependencies", "tab"]; | |
| 22 String get invocation => "pub deps"; | |
| 23 String get docUrl => "http://dartlang.org/tools/pub/cmd/pub-deps.html"; | |
| 24 bool get takesArguments => false; | |
| 25 | |
| 26 /// The loaded package graph. | |
| 27 PackageGraph _graph; | |
| 28 | |
| 29 /// The [StringBuffer] used to accumulate the output. | |
| 30 StringBuffer _buffer; | |
| 31 | |
| 32 DepsCommand() { | |
| 33 argParser.addOption( | |
| 34 "style", | |
| 35 abbr: "s", | |
| 36 help: "How output should be displayed.", | |
| 37 allowed: ["compact", "tree", "list"], | |
| 38 defaultsTo: "tree"); | |
| 39 } | |
| 40 | |
| 41 Future run() { | |
| 42 return entrypoint.loadPackageGraph().then((graph) { | |
| 43 _graph = graph; | |
| 44 _buffer = new StringBuffer(); | |
| 45 | |
| 46 _buffer.writeln(_labelPackage(entrypoint.root)); | |
| 47 | |
| 48 switch (argResults["style"]) { | |
| 49 case "compact": | |
| 50 _outputCompact(); | |
| 51 break; | |
| 52 case "list": | |
| 53 _outputList(); | |
| 54 break; | |
| 55 case "tree": | |
| 56 _outputTree(); | |
| 57 break; | |
| 58 } | |
| 59 | |
| 60 log.message(_buffer); | |
| 61 }); | |
| 62 } | |
| 63 | |
| 64 /// Outputs a list of all of the package's immediate, dev, override, and | |
| 65 /// transitive dependencies. | |
| 66 /// | |
| 67 /// For each dependency listed, *that* package's immediate dependencies are | |
| 68 /// shown. Unlike [_outputList], this prints all of these dependencies on one | |
| 69 /// line. | |
| 70 void _outputCompact() { | |
| 71 var root = entrypoint.root; | |
| 72 _outputCompactPackages( | |
| 73 "dependencies", | |
| 74 root.dependencies.map((dep) => dep.name)); | |
| 75 _outputCompactPackages( | |
| 76 "dev dependencies", | |
| 77 root.devDependencies.map((dep) => dep.name)); | |
| 78 _outputCompactPackages( | |
| 79 "dependency overrides", | |
| 80 root.dependencyOverrides.map((dep) => dep.name)); | |
| 81 | |
| 82 var transitive = _getTransitiveDependencies(); | |
| 83 _outputCompactPackages("transitive dependencies", transitive); | |
| 84 } | |
| 85 | |
| 86 /// Outputs one section of packages in the compact output. | |
| 87 _outputCompactPackages(String section, Iterable<String> names) { | |
| 88 if (names.isEmpty) return; | |
| 89 | |
| 90 _buffer.writeln(); | |
| 91 _buffer.writeln("$section:"); | |
| 92 for (var name in ordered(names)) { | |
| 93 var package = _graph.packages[name]; | |
| 94 | |
| 95 _buffer.write("- ${_labelPackage(package)}"); | |
| 96 if (package.dependencies.isEmpty) { | |
| 97 _buffer.writeln(); | |
| 98 } else { | |
| 99 var depNames = package.dependencies.map((dep) => dep.name); | |
| 100 var depsList = "[${depNames.join(' ')}]"; | |
| 101 _buffer.writeln(" ${log.gray(depsList)}"); | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 /// Outputs a list of all of the package's immediate, dev, override, and | |
| 107 /// transitive dependencies. | |
| 108 /// | |
| 109 /// For each dependency listed, *that* package's immediate dependencies are | |
| 110 /// shown. | |
| 111 void _outputList() { | |
| 112 var root = entrypoint.root; | |
| 113 _outputListSection( | |
| 114 "dependencies", | |
| 115 root.dependencies.map((dep) => dep.name)); | |
| 116 _outputListSection( | |
| 117 "dev dependencies", | |
| 118 root.devDependencies.map((dep) => dep.name)); | |
| 119 _outputListSection( | |
| 120 "dependency overrides", | |
| 121 root.dependencyOverrides.map((dep) => dep.name)); | |
| 122 | |
| 123 var transitive = _getTransitiveDependencies(); | |
| 124 if (transitive.isEmpty) return; | |
| 125 | |
| 126 _outputListSection("transitive dependencies", ordered(transitive)); | |
| 127 } | |
| 128 | |
| 129 /// Outputs one section of packages in the list output. | |
| 130 _outputListSection(String name, Iterable<String> deps) { | |
| 131 if (deps.isEmpty) return; | |
| 132 | |
| 133 _buffer.writeln(); | |
| 134 _buffer.writeln("$name:"); | |
| 135 | |
| 136 for (var name in deps) { | |
| 137 var package = _graph.packages[name]; | |
| 138 _buffer.writeln("- ${_labelPackage(package)}"); | |
| 139 | |
| 140 for (var dep in package.dependencies) { | |
| 141 _buffer.writeln( | |
| 142 " - ${log.bold(dep.name)} ${log.gray(dep.constraint)}"); | |
| 143 } | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 /// Generates a dependency tree for the root package. | |
| 148 /// | |
| 149 /// If a package is encountered more than once (i.e. a shared or circular | |
| 150 /// dependency), later ones are not traversed. This is done in breadth-first | |
| 151 /// fashion so that a package will always be expanded at the shallowest | |
| 152 /// depth that it appears at. | |
| 153 void _outputTree() { | |
| 154 // The work list for the breadth-first traversal. It contains the package | |
| 155 // being added to the tree, and the parent map that will receive that | |
| 156 // package. | |
| 157 var toWalk = new Queue<Pair<Package, Map>>(); | |
| 158 var visited = new Set<String>(); | |
| 159 | |
| 160 // Start with the root dependencies. | |
| 161 var packageTree = {}; | |
| 162 for (var dep in entrypoint.root.immediateDependencies) { | |
| 163 toWalk.add(new Pair(_graph.packages[dep.name], packageTree)); | |
| 164 } | |
| 165 | |
| 166 // Do a breadth-first walk to the dependency graph. | |
| 167 while (toWalk.isNotEmpty) { | |
| 168 var pair = toWalk.removeFirst(); | |
| 169 var package = pair.first; | |
| 170 var map = pair.last; | |
| 171 | |
| 172 if (visited.contains(package.name)) { | |
| 173 map[log.gray('${package.name}...')] = {}; | |
| 174 continue; | |
| 175 } | |
| 176 | |
| 177 visited.add(package.name); | |
| 178 | |
| 179 // Populate the map with this package's dependencies. | |
| 180 var childMap = {}; | |
| 181 map[_labelPackage(package)] = childMap; | |
| 182 | |
| 183 for (var dep in package.dependencies) { | |
| 184 toWalk.add(new Pair(_graph.packages[dep.name], childMap)); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 _buffer.write(tree.fromMap(packageTree, showAllChildren: true)); | |
| 189 } | |
| 190 | |
| 191 String _labelPackage(Package package) => | |
| 192 "${log.bold(package.name)} ${package.version}"; | |
| 193 | |
| 194 /// Gets the names of the non-immediate dependencies of the root package. | |
| 195 Set<String> _getTransitiveDependencies() { | |
| 196 var transitive = _graph.packages.keys.toSet(); | |
| 197 var root = entrypoint.root; | |
| 198 transitive.remove(root.name); | |
| 199 transitive.removeAll(root.dependencies.map((dep) => dep.name)); | |
| 200 transitive.removeAll(root.devDependencies.map((dep) => dep.name)); | |
| 201 transitive.removeAll(root.dependencyOverrides.map((dep) => dep.name)); | |
| 202 return transitive; | |
| 203 } | |
| 204 } | |
| OLD | NEW |