pub deps --json (#2896)
diff --git a/lib/src/command/deps.dart b/lib/src/command/deps.dart
index 98e68a3..d061b96 100644
--- a/lib/src/command/deps.dart
+++ b/lib/src/command/deps.dart
@@ -3,8 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
-
-import 'package:path/path.dart' as p;
+import 'dart:convert';
import '../ascii_tree.dart' as tree;
import '../command.dart';
@@ -53,6 +52,10 @@
argParser.addFlag('executables',
negatable: false, help: 'List all available executables.');
+ argParser.addFlag('json',
+ negatable: false,
+ help: 'Output dependency information in a json format.');
+
argParser.addOption('directory',
abbr: 'C', help: 'Run this in the directory<dir>.', valueHelp: 'dir');
}
@@ -64,26 +67,98 @@
_buffer = StringBuffer();
- if (argResults['executables']) {
- _outputExecutables();
- } else {
- for (var sdk in sdks.values) {
- if (!sdk.isAvailable) continue;
- _buffer.writeln("${log.bold('${sdk.name} SDK')} ${sdk.version}");
+ if (argResults['json']) {
+ if (argResults.wasParsed('dev')) {
+ usageException(
+ 'Cannot combine --json and --dev.\nThe json output contains the dependency type in the output.');
}
+ if (argResults.wasParsed('executables')) {
+ usageException(
+ 'Cannot combine --json and --executables.\nThe json output always lists available executables.');
+ }
+ if (argResults.wasParsed('style')) {
+ usageException('Cannot combine --json and --style.');
+ }
+ final visited = <String>[];
+ final toVisit = [entrypoint.root.name];
+ final packagesJson = <dynamic>[];
+ while (toVisit.isNotEmpty) {
+ final current = toVisit.removeLast();
+ if (visited.contains(current)) continue;
+ visited.add(current);
+ final currentPackage = entrypoint.packageGraph.packages[current];
+ final next = (current == entrypoint.root.name
+ ? entrypoint.root.immediateDependencies
+ : currentPackage.dependencies)
+ .keys
+ .toList();
+ final dependencyType = entrypoint.root.dependencyType(current);
+ final kind = currentPackage == entrypoint.root
+ ? 'root'
+ : (dependencyType == DependencyType.direct
+ ? 'direct'
+ : (dependencyType == DependencyType.dev
+ ? 'dev'
+ : 'transitive'));
+ final source =
+ entrypoint.packageGraph.lockFile.packages[current]?.source?.name ??
+ 'root';
+ packagesJson.add({
+ 'name': current,
+ 'version': currentPackage.version.toString(),
+ 'kind': kind,
+ 'source': source,
+ 'dependencies': next
+ });
+ toVisit.addAll(next);
+ }
+ var executables = [
+ for (final package in [
+ entrypoint.root,
+ ...entrypoint.root.immediateDependencies.keys
+ .map((name) => entrypoint.packageGraph.packages[name])
+ ])
+ ...package.executableNames.map((name) => package == entrypoint.root
+ ? ':$name'
+ : (package.name == name ? name : '${package.name}:$name'))
+ ];
- _buffer.writeln(_labelPackage(entrypoint.root));
+ _buffer.writeln(
+ JsonEncoder.withIndent(' ').convert(
+ {
+ 'root': entrypoint.root.name,
+ 'packages': packagesJson,
+ 'sdks': [
+ for (var sdk in sdks.values)
+ if (sdk.version != null)
+ {'name': sdk.name, 'version': sdk.version.toString()}
+ ],
+ 'executables': executables
+ },
+ ),
+ );
+ } else {
+ if (argResults['executables']) {
+ _outputExecutables();
+ } else {
+ for (var sdk in sdks.values) {
+ if (!sdk.isAvailable) continue;
+ _buffer.writeln("${log.bold('${sdk.name} SDK')} ${sdk.version}");
+ }
- switch (argResults['style']) {
- case 'compact':
- _outputCompact();
- break;
- case 'list':
- _outputList();
- break;
- case 'tree':
- _outputTree();
- break;
+ _buffer.writeln(_labelPackage(entrypoint.root));
+
+ switch (argResults['style']) {
+ case 'compact':
+ _outputCompact();
+ break;
+ case 'list':
+ _outputList();
+ break;
+ case 'tree':
+ _outputTree();
+ break;
+ }
}
}
@@ -268,34 +343,13 @@
];
for (var package in packages) {
- var executables = _getExecutablesFor(package);
+ var executables = package.executableNames;
if (executables.isNotEmpty) {
_buffer.writeln(_formatExecutables(package.name, executables.toList()));
}
}
}
- /// Returns `true` if [path] looks like a Dart entrypoint.
- bool _isDartExecutable(String path) {
- try {
- var unit = analysisContextManager.parse(path);
- return isEntrypoint(unit);
- } on AnalyzerErrorGroup {
- return false;
- }
- }
-
- /// Lists all Dart files in the `bin` directory of the [package].
- ///
- /// Returns file names without extensions.
- Iterable<String> _getExecutablesFor(Package package) {
- var packagePath = p.normalize(p.absolute(package.dir));
- analysisContextManager.createContextsForDirectory(packagePath);
- return package.executablePaths
- .where((e) => _isDartExecutable(p.absolute(package.dir, e)))
- .map(p.basenameWithoutExtension);
- }
-
/// Returns formatted string that lists [executables] for the [packageName].
/// Examples:
///
diff --git a/lib/src/package.dart b/lib/src/package.dart
index 55ce4fe..88a2909 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -84,6 +84,9 @@
.toList();
}
+ List<String> get executableNames =>
+ executablePaths.map(p.basenameWithoutExtension).toList();
+
/// Returns the path to the README file at the root of the entrypoint, or null
/// if no README file is found.
///
diff --git a/test/deps/executables_test.dart b/test/deps/executables_test.dart
index 933189f..88b1a88 100644
--- a/test/deps/executables_test.dart
+++ b/test/deps/executables_test.dart
@@ -2,226 +2,163 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:pub/src/ascii_tree.dart' as tree;
+import 'package:pub/src/io.dart';
import 'package:test/test.dart';
import '../descriptor.dart' as d;
+import '../golden_file.dart';
import '../test_pub.dart';
const _validMain = 'main() {}';
+const _invalidMain = 'main() {';
+
+Future<void> variations(String name) async {
+ final buffer = StringBuffer();
+ buffer.writeln(
+ tree.fromFiles(listDir(d.sandbox, recursive: true), baseDir: d.sandbox));
+
+ await pubGet();
+ await runPubIntoBuffer(['deps', '--executables'], buffer);
+ await runPubIntoBuffer(['deps', '--executables', '--dev'], buffer);
+ // The json ouput also lists the exectuables.
+ await runPubIntoBuffer(['deps', '--json'], buffer);
+ // The easiest way to update the golden files is to delete them and rerun the
+ // test.
+ expectMatchesGoldenFile(buffer.toString(), 'test/deps/goldens/$name.txt');
+}
void main() {
- Future<void> Function() _testExecutablesOutput(output, {bool dev = true}) =>
- () async {
- await pubGet();
- await runPub(
- args: ['deps', '--executables', if (dev) '--dev' else '--no-dev'],
- output: output);
- };
-
- Future<void> Function() _testAllDepsOutput(output) =>
- _testExecutablesOutput(output);
- Future<void> Function() _testNonDevDepsOutput(output) =>
- _testExecutablesOutput(output, dev: false);
-
- group('lists nothing when no executables found', () {
- setUp(() async {
- await d.dir(appPath, [d.appPubspec()]).create();
- });
-
- test('all dependencies', _testAllDepsOutput('\n'));
- test('non-dev dependencies', _testNonDevDepsOutput('\n'));
+ test('skips non-Dart executables', () async {
+ await d.dir(appPath, [
+ d.appPubspec(),
+ d.dir('bin', [d.file('foo.py'), d.file('bar.sh')])
+ ]).create();
+ await variations('non_dart_executables');
});
- group('skips non-Dart executables', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [d.file('foo.py'), d.file('bar.sh')])
- ]).create();
- });
-
- test('all dependencies', _testAllDepsOutput('\n'));
- test('non-dev dependencies', _testNonDevDepsOutput('\n'));
+ test('lists Dart executables, even without entrypoints', () async {
+ await d.dir(appPath, [
+ d.appPubspec(),
+ d.dir(
+ 'bin',
+ [d.file('foo.dart', _validMain), d.file('bar.dart', _invalidMain)],
+ )
+ ]).create();
+ await variations('dart_executables');
});
- group('skips Dart executables which are not parsable', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [d.file('foo.dart', 'main() {')])
- ]).create();
- });
-
- test('all dependencies', _testAllDepsOutput('\n'));
- test('non-dev dependencies', _testNonDevDepsOutput('\n'));
+ test('skips executables in sub directories', () async {
+ await d.dir(appPath, [
+ d.appPubspec(),
+ d.dir('bin', [
+ d.file('foo.dart', _validMain),
+ d.dir('sub', [d.file('bar.dart', _validMain)])
+ ])
+ ]).create();
+ await variations('nothing_in_sub_drectories');
});
- group('skips Dart executables without entrypoints', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec(),
- d.dir(
- 'bin', [d.file('foo.dart'), d.file('bar.dart', 'main(x, y, z) {}')])
- ]).create();
- });
+ test('lists executables from a dependency', () async {
+ await d.dir('foo', [
+ d.libPubspec('foo', '1.0.0'),
+ d.dir('bin', [d.file('bar.dart', _validMain)])
+ ]).create();
- test('all dependencies', _testAllDepsOutput('\n'));
- test('non-dev dependencies', _testNonDevDepsOutput('\n'));
+ await d.dir(appPath, [
+ d.appPubspec({
+ 'foo': {'path': '../foo'}
+ })
+ ]).create();
+
+ await variations('from_dependency');
});
- group('lists valid Dart executables with entrypoints', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin',
- [d.file('foo.dart', _validMain), d.file('bar.dart', _validMain)])
- ]).create();
- });
+ test('lists executables only from immediate dependencies', () async {
+ await d.dir(appPath, [
+ d.appPubspec({
+ 'foo': {'path': '../foo'}
+ })
+ ]).create();
- test('all dependencies', _testAllDepsOutput('myapp: bar, foo'));
- test('non-dev dependencies', _testNonDevDepsOutput('myapp: bar, foo'));
+ await d.dir('foo', [
+ d.libPubspec('foo', '1.0.0', deps: {
+ 'baz': {'path': '../baz'}
+ }),
+ d.dir('bin', [d.file('bar.dart', _validMain)])
+ ]).create();
+
+ await d.dir('baz', [
+ d.libPubspec('baz', '1.0.0'),
+ d.dir('bin', [d.file('qux.dart', _validMain)])
+ ]).create();
+
+ await variations('only_immediate');
});
- group('skips executables in sub directories', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec(),
- d.dir('bin', [
- d.file('foo.dart', _validMain),
- d.dir('sub', [d.file('bar.dart', _validMain)])
- ])
- ]).create();
- });
+ test('applies formatting before printing executables', () async {
+ await d.dir(appPath, [
+ d.appPubspec({
+ 'foo': {'path': '../foo'},
+ 'bar': {'path': '../bar'}
+ }),
+ d.dir('bin', [d.file('myapp.dart', _validMain)])
+ ]).create();
- test('all dependencies', _testAllDepsOutput('myapp:foo'));
- test('non-dev dependencies', _testNonDevDepsOutput('myapp:foo'));
+ await d.dir('foo', [
+ d.libPubspec('foo', '1.0.0'),
+ d.dir('bin',
+ [d.file('baz.dart', _validMain), d.file('foo.dart', _validMain)])
+ ]).create();
+
+ await d.dir('bar', [
+ d.libPubspec('bar', '1.0.0'),
+ d.dir('bin', [d.file('qux.dart', _validMain)])
+ ]).create();
+
+ await variations('formatting');
});
- group('lists executables from a dependency', () {
- setUp(() async {
- await d.dir('foo', [
- d.libPubspec('foo', '1.0.0'),
- d.dir('bin', [d.file('bar.dart', _validMain)])
- ]).create();
+ test('dev dependencies', () async {
+ await d.dir('foo', [
+ d.libPubspec('foo', '1.0.0'),
+ d.dir('bin', [d.file('bar.dart', _validMain)])
+ ]).create();
- await d.dir(appPath, [
- d.appPubspec({
+ await d.dir(appPath, [
+ d.pubspec({
+ 'name': 'myapp',
+ 'dev_dependencies': {
'foo': {'path': '../foo'}
- })
- ]).create();
- });
-
- test('all dependencies', _testAllDepsOutput('foo:bar'));
- test('non-dev dependencies', _testNonDevDepsOutput('foo:bar'));
+ }
+ })
+ ]).create();
+ await variations('dev_dependencies');
});
- group('lists executables only from immediate dependencies', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec({
- 'foo': {'path': '../foo'}
- })
- ]).create();
+ test('overriden dependencies executables', () async {
+ await d.dir('foo-1.0', [
+ d.libPubspec('foo', '1.0.0'),
+ d.dir('bin', [d.file('bar.dart', _validMain)])
+ ]).create();
- await d.dir('foo', [
- d.libPubspec('foo', '1.0.0', deps: {
- 'baz': {'path': '../baz'}
- }),
- d.dir('bin', [d.file('bar.dart', _validMain)])
- ]).create();
+ await d.dir('foo-2.0', [
+ d.libPubspec('foo', '2.0.0'),
+ d.dir('bin',
+ [d.file('bar.dart', _validMain), d.file('baz.dart', _validMain)])
+ ]).create();
- await d.dir('baz', [
- d.libPubspec('baz', '1.0.0'),
- d.dir('bin', [d.file('qux.dart', _validMain)])
- ]).create();
- });
-
- test('all dependencies', _testAllDepsOutput('foo:bar'));
- test('non-dev dependencies', _testNonDevDepsOutput('foo:bar'));
- });
-
- group('applies formatting before printing executables', () {
- setUp(() async {
- await d.dir(appPath, [
- d.appPubspec({
- 'foo': {'path': '../foo'},
- 'bar': {'path': '../bar'}
- }),
- d.dir('bin', [d.file('myapp.dart', _validMain)])
- ]).create();
-
- await d.dir('foo', [
- d.libPubspec('foo', '1.0.0'),
- d.dir('bin',
- [d.file('baz.dart', _validMain), d.file('foo.dart', _validMain)])
- ]).create();
-
- await d.dir('bar', [
- d.libPubspec('bar', '1.0.0'),
- d.dir('bin', [d.file('qux.dart', _validMain)])
- ]).create();
- });
-
- test('all dependencies', _testAllDepsOutput('''
- myapp
- foo: foo, baz
- bar:qux'''));
- test('non-dev dependencies', _testNonDevDepsOutput('''
- myapp
- foo: foo, baz
- bar:qux'''));
- });
-
- group('dev dependencies', () {
- setUp(() async {
- await d.dir('foo', [
- d.libPubspec('foo', '1.0.0'),
- d.dir('bin', [d.file('bar.dart', _validMain)])
- ]).create();
-
- await d.dir(appPath, [
- d.pubspec({
- 'name': 'myapp',
- 'dev_dependencies': {
- 'foo': {'path': '../foo'}
- }
- })
- ]).create();
- });
-
- test('are listed if --dev flag is set', _testAllDepsOutput('foo:bar'));
- test('are skipped if --no-dev flag is set', _testNonDevDepsOutput('\n'));
- });
-
- group('overriden dependencies executables', () {
- setUp(() async {
- await d.dir('foo-1.0', [
- d.libPubspec('foo', '1.0.0'),
- d.dir('bin', [d.file('bar.dart', _validMain)])
- ]).create();
-
- await d.dir('foo-2.0', [
- d.libPubspec('foo', '2.0.0'),
- d.dir('bin',
- [d.file('bar.dart', _validMain), d.file('baz.dart', _validMain)])
- ]).create();
-
- await d.dir(appPath, [
- d.pubspec({
- 'name': 'myapp',
- 'dependencies': {
- 'foo': {'path': '../foo-1.0'}
- },
- 'dependency_overrides': {
- 'foo': {'path': '../foo-2.0'}
- }
- })
- ]).create();
- });
-
- test(
- 'are listed if --dev flag is set', _testAllDepsOutput('foo: bar, baz'));
- test('are listed if --no-dev flag is set',
- _testNonDevDepsOutput('foo: bar, baz'));
+ await d.dir(appPath, [
+ d.pubspec({
+ 'name': 'myapp',
+ 'dependencies': {
+ 'foo': {'path': '../foo-1.0'}
+ },
+ 'dependency_overrides': {
+ 'foo': {'path': '../foo-2.0'}
+ }
+ })
+ ]).create();
+ await variations('overrides');
});
}
diff --git a/test/deps/goldens/dart_executables.txt b/test/deps/goldens/dart_executables.txt
new file mode 100644
index 0000000..9284ceb
--- /dev/null
+++ b/test/deps/goldens/dart_executables.txt
@@ -0,0 +1,36 @@
+'-- myapp
+ |-- bin
+ | |-- bar.dart
+ | '-- foo.dart
+ '-- pubspec.yaml
+
+$ pub deps --executables
+myapp: bar, foo
+
+$ pub deps --executables --dev
+myapp: bar, foo
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ ":bar",
+ ":foo"
+ ]
+}
+
diff --git a/test/deps/goldens/dev_dependencies.txt b/test/deps/goldens/dev_dependencies.txt
new file mode 100644
index 0000000..3fb677c
--- /dev/null
+++ b/test/deps/goldens/dev_dependencies.txt
@@ -0,0 +1,45 @@
+|-- foo
+| |-- bin
+| | '-- bar.dart
+| '-- pubspec.yaml
+'-- myapp
+ '-- pubspec.yaml
+
+$ pub deps --executables
+foo:bar
+
+$ pub deps --executables --dev
+foo:bar
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": [
+ "foo"
+ ]
+ },
+ {
+ "name": "foo",
+ "version": "1.0.0",
+ "kind": "dev",
+ "source": "path",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ "foo:bar"
+ ]
+}
+
diff --git a/test/deps/goldens/formatting.txt b/test/deps/goldens/formatting.txt
new file mode 100644
index 0000000..ff5aec9
--- /dev/null
+++ b/test/deps/goldens/formatting.txt
@@ -0,0 +1,67 @@
+|-- bar
+| |-- bin
+| | '-- qux.dart
+| '-- pubspec.yaml
+|-- foo
+| |-- bin
+| | |-- baz.dart
+| | '-- foo.dart
+| '-- pubspec.yaml
+'-- myapp
+ |-- bin
+ | '-- myapp.dart
+ '-- pubspec.yaml
+
+$ pub deps --executables
+myapp
+foo: foo, baz
+bar:qux
+
+$ pub deps --executables --dev
+myapp
+foo: foo, baz
+bar:qux
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": [
+ "foo",
+ "bar"
+ ]
+ },
+ {
+ "name": "bar",
+ "version": "1.0.0",
+ "kind": "direct",
+ "source": "path",
+ "dependencies": []
+ },
+ {
+ "name": "foo",
+ "version": "1.0.0",
+ "kind": "direct",
+ "source": "path",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ ":myapp",
+ "foo:baz",
+ "foo",
+ "bar:qux"
+ ]
+}
+
diff --git a/test/deps/goldens/from_dependency.txt b/test/deps/goldens/from_dependency.txt
new file mode 100644
index 0000000..30836e5
--- /dev/null
+++ b/test/deps/goldens/from_dependency.txt
@@ -0,0 +1,45 @@
+|-- foo
+| |-- bin
+| | '-- bar.dart
+| '-- pubspec.yaml
+'-- myapp
+ '-- pubspec.yaml
+
+$ pub deps --executables
+foo:bar
+
+$ pub deps --executables --dev
+foo:bar
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": [
+ "foo"
+ ]
+ },
+ {
+ "name": "foo",
+ "version": "1.0.0",
+ "kind": "direct",
+ "source": "path",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ "foo:bar"
+ ]
+}
+
diff --git a/test/deps/goldens/non_dart_executables.txt b/test/deps/goldens/non_dart_executables.txt
new file mode 100644
index 0000000..646e21e
--- /dev/null
+++ b/test/deps/goldens/non_dart_executables.txt
@@ -0,0 +1,31 @@
+'-- myapp
+ |-- bin
+ | |-- bar.sh
+ | '-- foo.py
+ '-- pubspec.yaml
+
+$ pub deps --executables
+
+$ pub deps --executables --dev
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": []
+}
+
diff --git a/test/deps/goldens/nothing_in_sub_drectories.txt b/test/deps/goldens/nothing_in_sub_drectories.txt
new file mode 100644
index 0000000..db3c77c
--- /dev/null
+++ b/test/deps/goldens/nothing_in_sub_drectories.txt
@@ -0,0 +1,36 @@
+'-- myapp
+ |-- bin
+ | |-- foo.dart
+ | '-- sub
+ | '-- bar.dart
+ '-- pubspec.yaml
+
+$ pub deps --executables
+myapp:foo
+
+$ pub deps --executables --dev
+myapp:foo
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ ":foo"
+ ]
+}
+
diff --git a/test/deps/goldens/only_immediate.txt b/test/deps/goldens/only_immediate.txt
new file mode 100644
index 0000000..5e42925
--- /dev/null
+++ b/test/deps/goldens/only_immediate.txt
@@ -0,0 +1,58 @@
+|-- baz
+| |-- bin
+| | '-- qux.dart
+| '-- pubspec.yaml
+|-- foo
+| |-- bin
+| | '-- bar.dart
+| '-- pubspec.yaml
+'-- myapp
+ '-- pubspec.yaml
+
+$ pub deps --executables
+foo:bar
+
+$ pub deps --executables --dev
+foo:bar
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": [
+ "foo"
+ ]
+ },
+ {
+ "name": "foo",
+ "version": "1.0.0",
+ "kind": "direct",
+ "source": "path",
+ "dependencies": [
+ "baz"
+ ]
+ },
+ {
+ "name": "baz",
+ "version": "1.0.0",
+ "kind": "transitive",
+ "source": "path",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ "foo:bar"
+ ]
+}
+
diff --git a/test/deps/goldens/overrides.txt b/test/deps/goldens/overrides.txt
new file mode 100644
index 0000000..ab76e79
--- /dev/null
+++ b/test/deps/goldens/overrides.txt
@@ -0,0 +1,51 @@
+|-- foo-1.0
+| |-- bin
+| | '-- bar.dart
+| '-- pubspec.yaml
+|-- foo-2.0
+| |-- bin
+| | |-- bar.dart
+| | '-- baz.dart
+| '-- pubspec.yaml
+'-- myapp
+ '-- pubspec.yaml
+
+$ pub deps --executables
+foo: bar, baz
+
+$ pub deps --executables --dev
+foo: bar, baz
+
+$ pub deps --json
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": [
+ "foo"
+ ]
+ },
+ {
+ "name": "foo",
+ "version": "2.0.0",
+ "kind": "direct",
+ "source": "path",
+ "dependencies": []
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": [
+ "foo:bar",
+ "foo:baz"
+ ]
+}
+
diff --git a/test/deps_test.dart b/test/deps_test.dart
index d1ab44d..207e8e7 100644
--- a/test/deps_test.dart
+++ b/test/deps_test.dart
@@ -130,6 +130,128 @@
'-- myapp...
''');
});
+ test('in json form', () async {
+ await pubGet();
+ await runPub(args: ['deps', '--json'], output: '''
+{
+ "root": "myapp",
+ "packages": [
+ {
+ "name": "myapp",
+ "version": "0.0.0",
+ "kind": "root",
+ "source": "root",
+ "dependencies": [
+ "normal",
+ "overridden",
+ "from_path",
+ "unittest",
+ "override_only"
+ ]
+ },
+ {
+ "name": "override_only",
+ "version": "1.2.3",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": []
+ },
+ {
+ "name": "unittest",
+ "version": "1.2.3",
+ "kind": "dev",
+ "source": "hosted",
+ "dependencies": [
+ "shared",
+ "dev_only"
+ ]
+ },
+ {
+ "name": "dev_only",
+ "version": "1.2.3",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": []
+ },
+ {
+ "name": "shared",
+ "version": "1.2.3",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": [
+ "other"
+ ]
+ },
+ {
+ "name": "other",
+ "version": "1.0.0",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": [
+ "myapp"
+ ]
+ },
+ {
+ "name": "from_path",
+ "version": "1.2.3",
+ "kind": "direct",
+ "source": "path",
+ "dependencies": []
+ },
+ {
+ "name": "overridden",
+ "version": "2.0.0",
+ "kind": "direct",
+ "source": "hosted",
+ "dependencies": []
+ },
+ {
+ "name": "normal",
+ "version": "1.2.3",
+ "kind": "direct",
+ "source": "hosted",
+ "dependencies": [
+ "transitive",
+ "circular_a"
+ ]
+ },
+ {
+ "name": "circular_a",
+ "version": "1.2.3",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": [
+ "circular_b"
+ ]
+ },
+ {
+ "name": "circular_b",
+ "version": "1.2.3",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": [
+ "circular_a"
+ ]
+ },
+ {
+ "name": "transitive",
+ "version": "1.2.3",
+ "kind": "transitive",
+ "source": "hosted",
+ "dependencies": [
+ "shared"
+ ]
+ }
+ ],
+ "sdks": [
+ {
+ "name": "Dart",
+ "version": "0.1.2+3"
+ }
+ ],
+ "executables": []
+}''');
+ });
test('with the Flutter SDK, if applicable', () async {
await pubGet();