updates to the deps tool (#61)
diff --git a/pkgs/corpus/README.md b/pkgs/corpus/README.md
index 73ee42a..7973970 100644
--- a/pkgs/corpus/README.md
+++ b/pkgs/corpus/README.md
@@ -1,38 +1,44 @@
+Welcome! There are two tools in this repo - a `deps` tool and a `usage` tool. To
+use them, clone this repo, cd to `pkgs/corpus`, and run one of the two tools
+below.
-## What's this?
+## bin/deps.dart
+
+This is a tool to calculate information about which packages depend on a target
+package.
+
+It queries pub.dev for the packages that use `<package-name>` and generates a
+`.csv` file with the dependent information. This is useful for things like
+understanding which packages might be impacted by version changes to the target
+package. For an example of the dependency report, see
+[matcher.csv](doc/matcher.csv).
+
+### Usage
+
+```
+usage: dart bin/deps.dart [options] <package-name>
+
+options:
+-h, --help Print this usage information.
+ --package-limit=<count> Limit the number of packages to return data for.
+ --include-old Include packages that haven't been published in the last year.
+ --include-dev-deps Include usages from dev dependencies.
+```
+
+## bin/usage.dart
This is a tool to calculate the API usage for a package - what parts of a
package's API are typically used by other Dart packages and applications.
-It is run from the command line:
-
-```
-dart bin/api_usage.dart <package-name>
-```
-
It queries pub.dev for the packages that use `<package-name>`, downloads the
referencing packages, analyzes them, and determines which portions of the target
package's API are used. For an example usage report, see
[collection.md](doc/collection.md).
-## Usage
-
-To use this tool, clone the repo, cd to `packages/corpus`, and run:
+### Usage
```
-dart bin/api_usage.dart <package-name>
-```
-
-Some available options are:
-
-- `--package-limit`: limit the number of packages that are used for analysis
-- `--show-src-references`: when there are references into a package's `lib/src/`
- directory (something that's generally not intended to be part of a package's
- public API), this option will include which package is using the `src/`
- library in the output
-
-```
-usage: dart bin/api_usage.dart [options] <package-name>
+usage: dart bin/usage.dart [options] <package-name>
options:
-h, --help Print this usage information.
diff --git a/pkgs/corpus/bin/deps.dart b/pkgs/corpus/bin/deps.dart
index dd17a35..be25c2d 100644
--- a/pkgs/corpus/bin/deps.dart
+++ b/pkgs/corpus/bin/deps.dart
@@ -2,8 +2,7 @@
// 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.
-/// Outputs information in CSV format for all the dependencies of a given
-/// package.
+/// Outputs information in CSV format for all the dependents of a given package.
import 'dart:io';
@@ -11,8 +10,16 @@
import 'package:cli_util/cli_logging.dart';
import 'package:corpus/packages.dart';
import 'package:corpus/pub.dart';
+import 'package:path/path.dart' as path;
+import 'package:pub_semver/pub_semver.dart';
-// TODO: show the 'discontinued' property
+const packageLimitFlag = 'package-limit';
+const includeOldFlag = 'include-old';
+const includeDevDepsFlag = 'include-dev-deps';
+const requireSdk212 = true;
+
+// TODO: Turn this and 'usage.dart' into a combined CommandRunner tool.
+// TODO: Move the bulk of the implementation into lib/ (to facilitate testing).
void main(List<String> args) async {
var argParser = createArgParser();
@@ -33,8 +40,9 @@
}
final packageName = argResults.rest.first;
- var packageLimit = argResults['package-limit'] as String?;
- var excludeOld = argResults['exclude-old'] as bool;
+ var packageLimit = argResults[packageLimitFlag] as String?;
+ var includeOld = argResults[includeOldFlag] as bool;
+ var includeDevDeps = argResults[includeDevDepsFlag] as bool;
var log = Logger.standard();
@@ -52,10 +60,7 @@
var limit = packageLimit == null ? null : int.parse(packageLimit);
- var packages = await pub.dependenciesOf(
- packageName,
- limit: limit == null ? null : limit * 2,
- );
+ var packageStream = pub.popularDependenciesOf(packageName);
progress.finish(showTiming: true);
@@ -63,17 +68,34 @@
var count = 0;
- for (var package in packages) {
- progress = log.progress(' $package');
+ await for (var package in packageStream) {
var usage = await getPackageUsageInfo(pub, package);
- progress.finish();
- if (excludeOld) {
- if (!usage.packageInfo.publishedDate.isAfter(dateOneYearAgo)) {
+ if (usage.packageOptions.isDiscontinued) {
+ continue;
+ }
+
+ if (!includeOld &&
+ !usage.packageInfo.publishedDate.isAfter(dateOneYearAgo)) {
+ continue;
+ }
+
+ var constraintType = package.constraintType(targetPackage.name);
+ if (!includeDevDeps && constraintType == 'dev') {
+ continue;
+ }
+
+ var sdkConstraint = usage.packageInfo.sdkConstraint;
+ if (requireSdk212 && sdkConstraint != null) {
+ // We only want packages that support 2.12 and later. As a close proxy, we
+ // skip packages that allow 2.11.0.
+ var constraint = VersionConstraint.parse(sdkConstraint);
+ if (constraint.allows(Version.parse('2.11.0'))) {
continue;
}
}
+ log.stdout(' $package');
usageInfos.add(usage);
count++;
@@ -101,12 +123,12 @@
PackageUsageInfo(this.packageInfo, this.packageOptions, this.packageScore);
}
-Future<PackageUsageInfo> getPackageUsageInfo(Pub pub, String package) async {
- var packageInfo = await pub.getPackageInfo(package);
- var packageOptions = await pub.getPackageOptions(packageInfo.name);
- var packageScore = await pub.getPackageScore(packageInfo.name);
+Future<PackageUsageInfo> getPackageUsageInfo(
+ Pub pub, PackageInfo package) async {
+ var packageOptions = await pub.getPackageOptions(package.name);
+ var packageScore = await pub.getPackageScore(package.name);
- return PackageUsageInfo(packageInfo, packageOptions, packageScore);
+ return PackageUsageInfo(package, packageOptions, packageScore);
}
File generateCsvReport(
@@ -115,29 +137,39 @@
) {
var buf = StringBuffer();
+ var columns = [
+ Column('Package', (usage) => usage.packageInfo.name),
+ Column('Version', (usage) => usage.packageInfo.version),
+ Column('Publish Days', (usage) => daysOld(usage.packageInfo.publishedDate)),
+ Column('Score', (usage) {
+ var score = usage.packageScore;
+ return printDouble(score.grantedPoints * 100 / score.maxPoints);
+ }),
+ Column('Popularity',
+ (usage) => printDouble(usage.packageScore.popularityScore * 100)),
+ Column('Likes', (usage) => '${usage.packageScore.likeCount}'),
+ Column('Constraint',
+ (usage) => '${usage.packageInfo.constraintFor(targetPackage.name)}'),
+ Column('Dep Type',
+ (usage) => '${usage.packageInfo.constraintType(targetPackage.name)}'),
+ Column('SDK', (usage) => '${usage.packageInfo.sdkConstraint}'),
+ Column('Repo', (usage) => '${usage.packageInfo.repo}'),
+ ];
+
buf.writeln('${targetPackage.name} ${targetPackage.version}');
buf.writeln();
- buf.writeln('Package,Version,Last Published (days),Popularity,Quality Score,'
- 'SDK Constraint,Package Constraint,Constraint Type,Likes,Repo');
+ buf.writeln(columns.map((c) => c.title).join(','));
+
+ // Sort the packages by the number of likes.
+ usageInfos.sort((usage1, usage2) {
+ return usage2.packageScore.likeCount - usage1.packageScore.likeCount;
+ });
for (var usage in usageInfos) {
- var package = usage.packageInfo;
- var score = usage.packageScore;
- buf.writeln(
- '${package.name},'
- '${package.version},'
- '${daysOld(package.publishedDate)},'
- '${printDouble(score.popularityScore * 100)},'
- '${printDouble(score.grantedPoints * 100 / score.maxPoints)},'
- '${package.sdkConstraint ?? ''},'
- '${package.constraintFor(targetPackage.name) ?? ''},'
- '${package.constraintType(targetPackage.name) ?? ''},'
- '${score.likeCount},'
- '${package.repo ?? ''}',
- );
+ buf.writeln(columns.map((c) => c.fn(usage)).join(','));
}
- var file = File('reports/${targetPackage.name}.csv');
+ var file = File(path.join('reports', '${targetPackage.name}.csv'));
file.parent.createSync();
file.writeAsStringSync(buf.toString());
return file;
@@ -152,14 +184,19 @@
help: 'Print this usage information.',
);
parser.addOption(
- 'package-limit',
- help: 'Limit the number of packages usage data is collected from.',
+ packageLimitFlag,
+ help: 'Limit the number of packages to return data for.',
valueHelp: 'count',
);
parser.addFlag(
- 'exclude-old',
+ includeOldFlag,
negatable: false,
- help: 'Exclude packages that haven\'t been published in the last year.',
+ help: "Include packages that haven't been published in the last year.",
+ );
+ parser.addFlag(
+ includeDevDepsFlag,
+ negatable: false,
+ help: 'Include usages from dev dependencies.',
);
return parser;
}
@@ -179,3 +216,10 @@
}
String printDouble(double value) => '${value.round()}';
+
+class Column {
+ final String title;
+ final String Function(PackageUsageInfo) fn;
+
+ Column(this.title, this.fn);
+}
diff --git a/pkgs/corpus/bin/api_usage.dart b/pkgs/corpus/bin/usage.dart
similarity index 93%
rename from pkgs/corpus/bin/api_usage.dart
rename to pkgs/corpus/bin/usage.dart
index 980413e..3d6416b 100644
--- a/pkgs/corpus/bin/api_usage.dart
+++ b/pkgs/corpus/bin/usage.dart
@@ -5,7 +5,7 @@
import 'dart:io';
import 'package:args/args.dart';
-import 'package:corpus/api_usage.dart';
+import 'package:corpus/usage.dart';
void main(List<String> args) async {
var argParser = _createArgParser();
@@ -60,7 +60,7 @@
}
void _printUsage(ArgParser argParser) {
- print('usage: dart bin/api_usage.dart [options] <package-name>');
+ print('usage: dart bin/usage.dart [options] <package-name>');
print('');
print('options:');
print(argParser.usage);
diff --git a/pkgs/corpus/doc/matcher.csv b/pkgs/corpus/doc/matcher.csv
new file mode 100644
index 0000000..8701ad5
--- /dev/null
+++ b/pkgs/corpus/doc/matcher.csv
@@ -0,0 +1,34 @@
+matcher 0.12.14
+
+Package,Version,Publish Days,Score,Popularity,Likes,Constraint,Dep Type,SDK,Repo
+mockito,5.3.2,140,93,99,930,^0.12.10,regular,>=2.17.0-0 <3.0.0,https://github.com/dart-lang/mockito
+mocktail,0.3.0,344,100,98,698,^0.12.10,regular,>=2.12.0 <3.0.0,https://github.com/felangel/mocktail
+quiver,3.2.1,50,93,99,434,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/google/quiver-dart
+dcli,2.0.1,5,93,91,166,^0.12.14,regular,>=2.19.0 <3.0.0,https://github.com/onepub-dev/dcli
+code_builder,4.4.0,50,100,96,154,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/dart-lang/code_builder
+mockingjay,0.3.0,310,100,93,67,^0.12.10,regular,>=2.12.0 <3.0.0,https://github.com/VeryGoodOpenSource/mockingjay
+leak_tracker,2.0.1,13,93,88,54,^0.12.13,regular,>=2.18.0 <3.0.0,https://github.com/dart-lang/leak_tracker
+webdriver,3.0.2,22,79,100,35,^0.12.10,regular,>=2.18.0 <3.0.0,https://github.com/google/webdriver.dart
+gherkin,3.1.0,223,93,86,33,^0.12.11,regular,>=2.15.0 <3.0.0,https://github.com/jonsamwell/dart_gherkin
+angel3_framework,7.0.3,126,93,77,33,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/dukefirehawk/angel/tree/master/packages/framework
+test_api,0.4.18,28,86,99,10,>=0.12.11 <0.12.15,regular,>=2.18.0 <3.0.0,https://github.com/dart-lang/test/tree/master/pkgs/test_api
+rx,0.2.0,4,100,78,8,^0.12.0,regular,>=2.17.0 <3.0.0,https://github.com/renggli/dart-rx
+build_test,2.1.6,20,86,93,8,^0.12.0,regular,>=2.18.0 <3.0.0,https://github.com/dart-lang/build/tree/master/build_test
+typed_result,1.0.0,21,100,54,4,^0.12.14,regular,>=2.18.2 <3.0.0,https://github.com/lucastsantos/typed_result
+flutter_lwa,2.1.0,46,93,82,3,^0.12.10,regular,>=2.12.0 <3.0.0,https://github.com/ayvazj/flutter_lwa
+built_value_test,8.4.3,29,93,58,3,^0.12.0,regular,>=2.12.0 <3.0.0,https://github.com/google/built_value.dart/tree/master/built_value_test
+angel3_validate,7.0.1,68,100,71,2,^0.12.0,regular,>=2.17.0 <3.0.0,https://github.com/dukefirehawk/angel/tree/master/packages/validate
+conduit_test,4.2.2,8,100,70,2,^0.12.12,regular,>=2.19.0 <3.0.0,https://github.com/conduit-dart/conduit
+test_descriptor,2.0.1,113,79,83,2,^0.12.10,regular,>=2.12.0 <3.0.0,https://github.com/dart-lang/test_descriptor
+ngpageloader,5.0.0,184,86,6,2,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/angulardart-community/ngpageloader
+test_core,0.4.23,1,93,100,1,any,regular,>=2.18.0 <3.0.0,https://github.com/dart-lang/test/tree/master/pkgs/test_core
+jael3,7.0.0,165,93,57,1,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/dukefirehawk/angel/tree/master/packages/jael/jael
+dartbag,0.6.0,8,93,31,1,^0.12.14,regular,>=2.19.0 <3.0.0,https://github.com/jamesderlin/dartbag
+critical_test,7.0.1,85,64,0,1,0.12.13,regular,>=2.18.0 <3.0.0,https://github.com/bsutton/critical_tester
+dev_test,0.15.7+1,14,100,79,0,>=0.12.10 <2.0.0,regular,>=2.18.0 <3.0.0,https://github.com/tekartik/dev_test.dart/tree/master/dev_test
+mock_exceptions,0.8.2,37,100,69,0,^0.12.12,regular,>=2.18.6 <3.0.0,https://github.com/atn832/mock_exceptions
+belatuk_combinator,4.0.0,218,100,49,0,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/dart-backend/belatuk-common-utilities/tree/main/packages/combinator
+angel3_test,7.0.0,166,100,39,0,^0.12.10,regular,>=2.17.0 <3.0.0,https://github.com/dukefirehawk/angel/tree/master/packages/test
+tridev_test,1.0.0,109,93,0,0,>=0.12.3 <0.14.0,regular,>=2.12.0 <3.0.0,https://github.com/tridev-dart/tridev
+flutter_lwa_platform_interface,2.1.0,46,71,41,0,^0.12.10,regular,>=2.12.0 <3.0.0,https://github.com/ayvazj/flutter_lwa
+endaft_core,0.0.3-dev.45,97,71,22,0,^0.12.12,regular,>=2.18.0 <3.0.0,https://github.com/endaft/endaft-core
diff --git a/pkgs/corpus/lib/pub.dart b/pkgs/corpus/lib/pub.dart
index 42a5b24..9c89f4c 100644
--- a/pkgs/corpus/lib/pub.dart
+++ b/pkgs/corpus/lib/pub.dart
@@ -43,17 +43,6 @@
);
}
- Future<List<String>> dependenciesOf(
- String packageName, {
- int? limit,
- }) async {
- return await _packageNamesForSearch(
- 'dependency:$packageName',
- limit: limit,
- sort: 'top',
- ).toList();
- }
-
Future<PackageInfo> getPackageInfo(String pkgName) async {
final json = await _getJson(Uri.https('pub.dev', 'api/packages/$pkgName'));
@@ -104,44 +93,6 @@
}
}
- Stream<String> _packageNamesForSearch(
- String query, {
- int page = 1,
- int? limit,
- String? sort,
- }) async* {
- final uri = Uri.parse('https://pub.dev/api/search');
-
- var count = 0;
-
- for (;;) {
- final targetUri = uri.replace(queryParameters: {
- if (query.isNotEmpty) 'q': query,
- 'page': page.toString(),
- if (sort != null) 'sort': sort,
- });
-
- final map = await _getJson(targetUri);
-
- for (var packageName in (map['packages'] as List)
- .cast<Map<String, dynamic>>()
- .map((e) => e['package'] as String?)) {
- count++;
- yield packageName!;
- }
-
- if (map.containsKey('next')) {
- page = page + 1;
- } else {
- break;
- }
-
- if (limit != null && count >= limit) {
- break;
- }
- }
- }
-
Future<Map<String, dynamic>> _getJson(Uri uri) async {
final result = await _client.get(uri);
if (result.statusCode == 200) {
@@ -285,8 +236,8 @@
PackageScore.from(this.json);
- int get grantedPoints => json['grantedPoints'] as int;
- int get maxPoints => json['maxPoints'] as int;
- int get likeCount => json['likeCount'] as int;
- double get popularityScore => json['popularityScore'] as double;
+ int get grantedPoints => json['grantedPoints'] as int? ?? 0;
+ int get maxPoints => json['maxPoints'] as int? ?? 140;
+ int get likeCount => json['likeCount'] as int? ?? 0;
+ double get popularityScore => json['popularityScore'] as double? ?? 0.0;
}
diff --git a/pkgs/corpus/lib/api_usage.dart b/pkgs/corpus/lib/usage.dart
similarity index 100%
rename from pkgs/corpus/lib/api_usage.dart
rename to pkgs/corpus/lib/usage.dart