4
4
5
5
import 'dart:collection' ;
6
6
7
+ import 'package:analyzer/analyzer.dart' as analyzer;
8
+ import 'package:path/path.dart' as p;
9
+
7
10
import '../ascii_tree.dart' as tree;
8
11
import '../command.dart' ;
12
+ import '../dart.dart' ;
9
13
import '../log.dart' as log;
10
14
import '../package.dart' ;
15
+ import '../package_name.dart' ;
11
16
import '../utils.dart' ;
12
17
18
+ /// Returns `true` if [path] looks like a Dart entrypoint.
19
+ bool _isDartExecutable (String path) {
20
+ if (p.extension (path) == '.dart' ) {
21
+ try {
22
+ var unit = analyzer.parseDartFile (path, parseFunctionBodies: false );
23
+ return isEntrypoint (unit);
24
+ } on analyzer.AnalyzerErrorGroup {
25
+ return false ;
26
+ }
27
+ }
28
+
29
+ return false ;
30
+ }
31
+
32
+ /// The immediate non-dev dependencies this package specifies in its pubspec.
33
+ List <PackageRange > _nonDevDependencies (Package package) {
34
+ var toSkip = package.devDependencies.map ((d) => d.name);
35
+ return package.immediateDependencies.where ((r) => ! toSkip.contains (r.name));
36
+ }
37
+
13
38
/// Handles the `deps` pub command.
14
39
class DepsCommand extends PubCommand {
15
40
String get name => "deps" ;
@@ -36,6 +61,9 @@ class DepsCommand extends PubCommand {
36
61
negatable: true ,
37
62
help: "Whether to include dev dependencies." ,
38
63
defaultsTo: true );
64
+
65
+ argParser.addFlag ("executables" ,
66
+ negatable: false , help: "List all available executables." );
39
67
}
40
68
41
69
void run () {
@@ -44,18 +72,22 @@ class DepsCommand extends PubCommand {
44
72
45
73
_buffer = new StringBuffer ();
46
74
47
- _buffer.writeln (_labelPackage (entrypoint.root));
48
-
49
- switch (argResults["style" ]) {
50
- case "compact" :
51
- _outputCompact ();
52
- break ;
53
- case "list" :
54
- _outputList ();
55
- break ;
56
- case "tree" :
57
- _outputTree ();
58
- break ;
75
+ if (argResults['executables' ]) {
76
+ _outputExecutables ();
77
+ } else {
78
+ _buffer.writeln (_labelPackage (entrypoint.root));
79
+
80
+ switch (argResults["style" ]) {
81
+ case "compact" :
82
+ _outputCompact ();
83
+ break ;
84
+ case "list" :
85
+ _outputList ();
86
+ break ;
87
+ case "tree" :
88
+ _outputTree ();
89
+ break ;
90
+ }
59
91
}
60
92
61
93
log.message (_buffer);
@@ -231,4 +263,61 @@ class DepsCommand extends PubCommand {
231
263
'was generated, please run "pub get" again.' );
232
264
return null ;
233
265
}
266
+
267
+ /// Outputs all executables reachable from [entrypoint] .
268
+ void _outputExecutables () {
269
+ var packages = []
270
+ ..add (entrypoint.root)
271
+ ..addAll ((_includeDev
272
+ ? entrypoint.root.immediateDependencies
273
+ : _nonDevDependencies (entrypoint.root))
274
+ .map ((dep) => entrypoint.packageGraph.packages[dep.name]));
275
+
276
+ for (var package in packages) {
277
+ var executables = _getExecutablesFor (package);
278
+ if (executables.isNotEmpty) {
279
+ _buffer.writeln (_formatExecutables (package.name, executables.toList ()));
280
+ }
281
+ }
282
+ }
283
+
284
+ /// Lists all Dart files in the `bin` directory of the [package] .
285
+ ///
286
+ /// Returns file names without extensions.
287
+ List <String > _getExecutablesFor (Package package) => package
288
+ .listFiles (beneath: 'bin' , recursive: false )
289
+ .where (_isDartExecutable)
290
+ .map (p.basenameWithoutExtension);
291
+
292
+ /// Returns formatted string that lists [executables] for the [packageName] .
293
+ /// Examples:
294
+ ///
295
+ /// _formatExecutables('foo', ['foo']) // -> 'foo'
296
+ /// _formatExecutables('foo', ['bar']) // -> 'foo:bar'
297
+ /// _formatExecutables('foo', ['bar', 'foo']) // -> 'foo: foo, bar'
298
+ ///
299
+ /// Note the leading space before first executable and sorting order in the
300
+ /// last example.
301
+ String _formatExecutables (String packageName, List <String > executables) {
302
+ if (executables.length == 1 ) {
303
+ // If executable matches the package name omit the name of executable in
304
+ // the output.
305
+ return executables.first != packageName
306
+ ? '${packageName }:${log .bold (executables .first )}'
307
+ : log.bold (executables.first);
308
+ }
309
+
310
+ // Sort executables to make executable that matches the package name to be
311
+ // the first in the list.
312
+ executables.sort ((e1, e2) {
313
+ if (e1 == packageName)
314
+ return - 1 ;
315
+ else if (e2 == packageName)
316
+ return 1 ;
317
+ else
318
+ return e1.compareTo (e2);
319
+ });
320
+
321
+ return '${packageName }: ${executables .map (log .bold ).join (', ' )}' ;
322
+ }
234
323
}
0 commit comments