Only call Package.listFiles once per publish. (#3346)
diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart
index 916f12d..a20fbdf 100644
--- a/lib/src/command/lish.dart
+++ b/lib/src/command/lish.dart
@@ -229,8 +229,8 @@
createTarGz(files, baseDir: entrypoint.root.dir).toBytes();
// Validate the package.
- var isValid =
- await _validate(packageBytesFuture.then((bytes) => bytes.length));
+ var isValid = await _validate(
+ packageBytesFuture.then((bytes) => bytes.length), files);
if (!isValid) {
overrideExitCode(exit_codes.DATA);
return;
@@ -251,7 +251,7 @@
/// Validates the package. Completes to false if the upload should not
/// proceed.
- Future<bool> _validate(Future<int> packageSize) async {
+ Future<bool> _validate(Future<int> packageSize, List<String> files) async {
final hints = <String>[];
final warnings = <String>[];
final errors = <String>[];
@@ -260,6 +260,7 @@
entrypoint,
packageSize,
host,
+ files,
hints: hints,
warnings: warnings,
errors: errors,
diff --git a/lib/src/package.dart b/lib/src/package.dart
index e3b7981..4d90311 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -4,7 +4,6 @@
import 'dart:io';
-import 'package:collection/collection.dart' show IterableExtension;
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
@@ -18,9 +17,6 @@
import 'system_cache.dart';
import 'utils.dart';
-final _readmeRegexp = RegExp(r'^README($|\.)', caseSensitive: false);
-final _changelogRegexp = RegExp(r'^CHANGELOG($|\.)', caseSensitive: false);
-
/// A named, versioned, unit of code and resource reuse.
class Package {
/// Compares [a] and [b] orders them by name then version number.
@@ -98,34 +94,6 @@
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.
- ///
- /// If multiple READMEs are found, this uses the same conventions as
- /// pub.dartlang.org for choosing the primary one: the README with the fewest
- /// extensions that is lexically ordered first is chosen.
- String? get readmePath {
- var readmes = listFiles(recursive: false)
- .map(p.basename)
- .where((entry) => entry.contains(_readmeRegexp));
- if (readmes.isEmpty) return null;
-
- return p.join(dir, readmes.reduce((readme1, readme2) {
- var extensions1 = '.'.allMatches(readme1).length;
- var extensions2 = '.'.allMatches(readme2).length;
- var comparison = extensions1.compareTo(extensions2);
- if (comparison == 0) comparison = readme1.compareTo(readme2);
- return (comparison <= 0) ? readme1 : readme2;
- }));
- }
-
- /// Returns the path to the CHANGELOG file at the root of the entrypoint, or
- /// null if no CHANGELOG file is found.
- String? get changelogPath {
- return listFiles(recursive: false).firstWhereOrNull(
- (entry) => p.basename(entry).contains(_changelogRegexp));
- }
-
/// Returns whether or not this package is in a Git repo.
late final bool inGitRepo = computeInGitRepoCache();
diff --git a/lib/src/validator.dart b/lib/src/validator.dart
index 1b07560..ef04bc4 100644
--- a/lib/src/validator.dart
+++ b/lib/src/validator.dart
@@ -5,6 +5,7 @@
import 'dart:async';
import 'package:meta/meta.dart';
+import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'entrypoint.dart';
@@ -42,9 +43,6 @@
/// package not to be uploaded; warnings will require the user to confirm the
/// upload.
abstract class Validator {
- /// The entrypoint that's being validated.
- final Entrypoint entrypoint;
-
/// The accumulated errors for this validator.
///
/// Filled by calling [validate].
@@ -60,11 +58,15 @@
/// Filled by calling [validate].
final hints = <String>[];
- Validator(this.entrypoint);
+ late ValidationContext context;
+ Entrypoint get entrypoint => context.entrypoint;
+ int get packageSize => context.packageSize;
+ Uri get serverUrl => context.serverUrl;
+ List<String> get files => context.files;
/// Validates the entrypoint, adding any errors and warnings to [errors] and
/// [warnings], respectively.
- Future validate();
+ Future<void> validate();
/// Adds an error if the package's SDK constraint doesn't exclude Dart SDK
/// versions older than [firstSdkVersion].
@@ -114,45 +116,55 @@
/// Run all validators on the [entrypoint] package and print their results.
///
+ /// [files] should be the result of `entrypoint.root.listFiles()`.
+ ///
/// When the future completes [hints] [warnings] amd [errors] will have been
/// appended with the reported hints warnings and errors respectively.
///
/// [packageSize], if passed, should complete to the size of the tarred
/// package, in bytes. This is used to validate that it's not too big to
/// upload to the server.
- static Future<void> runAll(
- Entrypoint entrypoint, Future<int> packageSize, Uri? serverUrl,
+ static Future<void> runAll(Entrypoint entrypoint, Future<int> packageSize,
+ Uri serverUrl, List<String> files,
{required List<String> hints,
required List<String> warnings,
- required List<String> errors}) {
+ required List<String> errors}) async {
var validators = [
- GitignoreValidator(entrypoint),
- PubspecValidator(entrypoint),
- LicenseValidator(entrypoint),
- NameValidator(entrypoint),
- PubspecFieldValidator(entrypoint),
- DependencyValidator(entrypoint),
- DependencyOverrideValidator(entrypoint),
- DeprecatedFieldsValidator(entrypoint),
- DirectoryValidator(entrypoint),
- ExecutableValidator(entrypoint),
- CompiledDartdocValidator(entrypoint),
- ReadmeValidator(entrypoint),
- ChangelogValidator(entrypoint),
- SdkConstraintValidator(entrypoint),
- StrictDependenciesValidator(entrypoint),
- FlutterConstraintValidator(entrypoint),
- FlutterPluginFormatValidator(entrypoint),
- LanguageVersionValidator(entrypoint),
- RelativeVersionNumberingValidator(entrypoint, serverUrl),
- NullSafetyMixedModeValidator(entrypoint),
- PubspecTypoValidator(entrypoint),
- LeakDetectionValidator(entrypoint),
+ GitignoreValidator(),
+ PubspecValidator(),
+ LicenseValidator(),
+ NameValidator(),
+ PubspecFieldValidator(),
+ DependencyValidator(),
+ DependencyOverrideValidator(),
+ DeprecatedFieldsValidator(),
+ DirectoryValidator(),
+ ExecutableValidator(),
+ CompiledDartdocValidator(),
+ ReadmeValidator(),
+ ChangelogValidator(),
+ SdkConstraintValidator(),
+ StrictDependenciesValidator(),
+ FlutterConstraintValidator(),
+ FlutterPluginFormatValidator(),
+ LanguageVersionValidator(),
+ RelativeVersionNumberingValidator(),
+ NullSafetyMixedModeValidator(),
+ PubspecTypoValidator(),
+ LeakDetectionValidator(),
+ SizeValidator(),
];
- validators.add(SizeValidator(entrypoint, packageSize));
- return Future.wait(validators.map((validator) => validator.validate()))
- .then((_) {
+ final context = ValidationContext(
+ entrypoint,
+ await packageSize,
+ serverUrl,
+ files,
+ );
+ return await Future.wait(validators.map((validator) async {
+ validator.context = context;
+ await validator.validate();
+ })).then((_) {
hints.addAll([for (final validator in validators) ...validator.hints]);
warnings
.addAll([for (final validator in validators) ...validator.warnings]);
@@ -190,4 +202,28 @@
}
});
}
+
+ /// Returns the [files] that are inside [dir] (relative to the package
+ /// entrypoint).
+ // TODO(sigurdm): Consider moving this to a more central location.
+ List<String> filesBeneath(String dir, {required bool recursive}) {
+ final base = p.canonicalize(p.join(entrypoint.root.dir, dir));
+ return files
+ .where(
+ recursive
+ ? (file) => p.canonicalize(file).startsWith(base)
+ : (file) => p.canonicalize(p.dirname(file)) == base,
+ )
+ .toList();
+ }
+}
+
+class ValidationContext {
+ final Entrypoint entrypoint;
+ final int packageSize;
+ final Uri serverUrl;
+ final List<String> files;
+
+ ValidationContext(
+ this.entrypoint, this.packageSize, this.serverUrl, this.files);
}
diff --git a/lib/src/validator/changelog.dart b/lib/src/validator/changelog.dart
index 614eba2..bb897a7 100644
--- a/lib/src/validator/changelog.dart
+++ b/lib/src/validator/changelog.dart
@@ -5,53 +5,52 @@
import 'dart:async';
import 'dart:convert';
+import 'package:collection/collection.dart';
import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../io.dart';
import '../validator.dart';
+final _changelogRegexp = RegExp(r'^CHANGELOG($|\.)', caseSensitive: false);
+
/// A validator that validates a package's changelog file.
class ChangelogValidator extends Validator {
- ChangelogValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
- Future validate() {
- return Future.sync(() {
- final changelog = entrypoint.root.changelogPath;
+ Future<void> validate() async {
+ final changelog = filesBeneath('.', recursive: false).firstWhereOrNull(
+ (entry) => p.basename(entry).contains(_changelogRegexp));
- if (changelog == null) {
- warnings.add('Please add a `CHANGELOG.md` to your package. '
- 'See https://dart.dev/tools/pub/publishing#important-files.');
- return;
- }
+ if (changelog == null) {
+ warnings.add('Please add a `CHANGELOG.md` to your package. '
+ 'See https://dart.dev/tools/pub/publishing#important-files.');
+ return;
+ }
- if (p.basename(changelog) != 'CHANGELOG.md') {
- warnings.add('Please consider renaming $changelog to `CHANGELOG.md`. '
- 'See https://dart.dev/tools/pub/publishing#important-files.');
- }
+ if (p.basename(changelog) != 'CHANGELOG.md') {
+ warnings.add('Please consider renaming $changelog to `CHANGELOG.md`. '
+ 'See https://dart.dev/tools/pub/publishing#important-files.');
+ }
- var bytes = readBinaryFile(changelog);
- String contents;
+ var bytes = readBinaryFile(changelog);
+ String contents;
- try {
- // utf8.decode doesn't allow invalid UTF-8.
- contents = utf8.decode(bytes);
- } on FormatException catch (_) {
- warnings.add('$changelog contains invalid UTF-8.\n'
- 'This will cause it to be displayed incorrectly on '
- 'the Pub site (https://pub.dev).');
- // Failed to decode contents, so there's nothing else to check.
- return;
- }
+ try {
+ // utf8.decode doesn't allow invalid UTF-8.
+ contents = utf8.decode(bytes);
+ } on FormatException catch (_) {
+ warnings.add('$changelog contains invalid UTF-8.\n'
+ 'This will cause it to be displayed incorrectly on '
+ 'the Pub site (https://pub.dev).');
+ // Failed to decode contents, so there's nothing else to check.
+ return;
+ }
- final version = entrypoint.root.pubspec.version.toString();
+ final version = entrypoint.root.pubspec.version.toString();
- if (!contents.contains(version)) {
- warnings.add("$changelog doesn't mention current version ($version).\n"
- 'Consider updating it with notes on this version prior to '
- 'publication.');
- }
- });
+ if (!contents.contains(version)) {
+ warnings.add("$changelog doesn't mention current version ($version).\n"
+ 'Consider updating it with notes on this version prior to '
+ 'publication.');
+ }
}
}
diff --git a/lib/src/validator/compiled_dartdoc.dart b/lib/src/validator/compiled_dartdoc.dart
index 3f3376a..ae25f83 100644
--- a/lib/src/validator/compiled_dartdoc.dart
+++ b/lib/src/validator/compiled_dartdoc.dart
@@ -6,19 +6,16 @@
import 'package:path/path.dart' as path;
-import '../entrypoint.dart';
import '../io.dart';
import '../validator.dart';
/// Validates that a package doesn't contain compiled Dartdoc
/// output.
class CompiledDartdocValidator extends Validator {
- CompiledDartdocValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() {
return Future.sync(() {
- for (var entry in entrypoint.root.listFiles()) {
+ for (var entry in files) {
if (path.basename(entry) != 'nav.json') continue;
var dir = path.dirname(entry);
diff --git a/lib/src/validator/dependency.dart b/lib/src/validator/dependency.dart
index fd8a058..40e9c2a 100644
--- a/lib/src/validator/dependency.dart
+++ b/lib/src/validator/dependency.dart
@@ -6,7 +6,6 @@
import 'package:pub_semver/pub_semver.dart';
-import '../entrypoint.dart';
import '../exceptions.dart';
import '../log.dart' as log;
import '../package_name.dart';
@@ -25,13 +24,202 @@
/// A validator that validates a package's dependencies.
class DependencyValidator extends Validator {
- /// Whether any dependency has a caret constraint.
- var _hasCaretDep = false;
-
- DependencyValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() async {
+ /// Whether any dependency has a caret constraint.
+ var _hasCaretDep = false;
+
+ /// Emit an error for dependencies from unknown SDKs or without appropriate
+ /// constraints on the Dart SDK.
+ void _warnAboutSdkSource(PackageRange dep) {
+ var identifier = (dep.description as SdkDescription).sdk;
+ var sdk = sdks[identifier];
+ if (sdk == null) {
+ errors.add('Unknown SDK "$identifier" for dependency "${dep.name}".');
+ return;
+ }
+
+ validateSdkConstraint(sdk.firstPubVersion,
+ "Older versions of pub don't support the ${sdk.name} SDK.");
+ }
+
+ /// Warn that dependencies should use the hosted source.
+ Future _warnAboutSource(PackageRange dep) async {
+ List<Version> versions;
+ try {
+ var ids = await entrypoint.cache
+ .getVersions(entrypoint.cache.hosted.refFor(dep.name));
+ versions = ids.map((id) => id.version).toList();
+ } on ApplicationException catch (_) {
+ versions = [];
+ }
+
+ String constraint;
+ if (versions.isNotEmpty) {
+ constraint = '^${Version.primary(versions)}';
+ } else {
+ constraint = dep.constraint.toString();
+ if (!dep.constraint.isAny && dep.constraint is! Version) {
+ constraint = '"$constraint"';
+ }
+ }
+
+ // Path sources are errors. Other sources are just warnings.
+ var messages = dep.source is PathSource ? errors : warnings;
+
+ messages.add('Don\'t depend on "${dep.name}" from the ${dep.source} '
+ 'source. Use the hosted source instead. For example:\n'
+ '\n'
+ 'dependencies:\n'
+ ' ${dep.name}: $constraint\n'
+ '\n'
+ 'Using the hosted source ensures that everyone can download your '
+ 'package\'s dependencies along with your package.');
+ }
+
+ /// Warn about improper dependencies on Flutter.
+ void _warnAboutFlutterSdk(PackageRange dep) {
+ if (dep.source is SdkSource) {
+ _warnAboutSdkSource(dep);
+ return;
+ }
+
+ errors.add('Don\'t depend on "${dep.name}" from the ${dep.source} '
+ 'source. Use the SDK source instead. For example:\n'
+ '\n'
+ 'dependencies:\n'
+ ' ${dep.name}:\n'
+ ' sdk: ${dep.constraint}\n'
+ '\n'
+ 'The Flutter SDK is downloaded and managed outside of pub.');
+ }
+
+ /// Warn that dependencies should have version constraints.
+ void _warnAboutNoConstraint(PackageRange dep) {
+ var message = 'Your dependency on "${dep.name}" should have a version '
+ 'constraint.';
+ var locked = entrypoint.lockFile.packages[dep.name];
+ if (locked != null) {
+ message = '$message For example:\n'
+ '\n'
+ 'dependencies:\n'
+ ' ${dep.name}: ^${locked.version}\n';
+ }
+ warnings.add('$message\n'
+ 'Without a constraint, you\'re promising to support ${log.bold("all")} '
+ 'future versions of "${dep.name}".');
+ }
+
+ /// Warn that dependencies should allow more than a single version.
+ void _warnAboutSingleVersionConstraint(PackageRange dep) {
+ warnings.add(
+ 'Your dependency on "${dep.name}" should allow more than one version. '
+ 'For example:\n'
+ '\n'
+ 'dependencies:\n'
+ ' ${dep.name}: ^${dep.constraint}\n'
+ '\n'
+ 'Constraints that are too tight will make it difficult for people to '
+ 'use your package\n'
+ 'along with other packages that also depend on "${dep.name}".');
+ }
+
+ /// Warn that dependencies should have lower bounds on their constraints.
+ void _warnAboutNoConstraintLowerBound(PackageRange dep) {
+ var message =
+ 'Your dependency on "${dep.name}" should have a lower bound.';
+ var locked = entrypoint.lockFile.packages[dep.name];
+ if (locked != null) {
+ String constraint;
+ if (locked.version == (dep.constraint as VersionRange).max) {
+ constraint = '^${locked.version}';
+ } else {
+ constraint = '">=${locked.version} ${dep.constraint}"';
+ }
+
+ message = '$message For example:\n'
+ '\n'
+ 'dependencies:\n'
+ ' ${dep.name}: $constraint\n';
+ }
+ warnings.add('$message\n'
+ 'Without a constraint, you\'re promising to support ${log.bold("all")} '
+ 'previous versions of "${dep.name}".');
+ }
+
+ /// Warn that dependencies should have upper bounds on their constraints.
+ void _warnAboutNoConstraintUpperBound(PackageRange dep) {
+ String constraint;
+ if ((dep.constraint as VersionRange).includeMin) {
+ constraint = '^${(dep.constraint as VersionRange).min}';
+ } else {
+ constraint = '"${dep.constraint} '
+ '<${(dep.constraint as VersionRange).min!.nextBreaking}"';
+ }
+ // TODO: Handle the case where `dep.constraint.min` is null.
+
+ warnings.add(
+ 'Your dependency on "${dep.name}" should have an upper bound. For '
+ 'example:\n'
+ '\n'
+ 'dependencies:\n'
+ ' ${dep.name}: $constraint\n'
+ '\n'
+ 'Without an upper bound, you\'re promising to support '
+ '${log.bold("all")} future versions of ${dep.name}.');
+ }
+
+ void _warnAboutPrerelease(String dependencyName, VersionRange constraint) {
+ final packageVersion = entrypoint.root.version;
+ if (constraint.min != null &&
+ constraint.min!.isPreRelease &&
+ !packageVersion.isPreRelease) {
+ warnings.add('Packages dependent on a pre-release of another package '
+ 'should themselves be published as a pre-release version. '
+ 'If this package needs $dependencyName version ${constraint.min}, '
+ 'consider publishing the package as a pre-release instead.\n'
+ 'See https://dart.dev/tools/pub/publishing#publishing-prereleases '
+ 'For more information on pre-releases.');
+ }
+ }
+
+ /// Validates all dependencies in [dependencies].
+ Future _validateDependencies(Iterable<PackageRange> dependencies) async {
+ for (var dependency in dependencies) {
+ var constraint = dependency.constraint;
+ if (dependency.name == 'flutter') {
+ _warnAboutFlutterSdk(dependency);
+ } else if (dependency.source is SdkSource) {
+ _warnAboutSdkSource(dependency);
+ } else if (dependency.source is! HostedSource) {
+ await _warnAboutSource(dependency);
+
+ final description = dependency.description;
+ if (description is GitDescription && description.path != '.') {
+ validateSdkConstraint(_firstGitPathVersion,
+ "Older versions of pub don't support Git path dependencies.");
+ }
+ } else {
+ if (constraint.isAny) {
+ _warnAboutNoConstraint(dependency);
+ } else if (constraint is VersionRange) {
+ if (constraint is Version) {
+ _warnAboutSingleVersionConstraint(dependency);
+ } else {
+ _warnAboutPrerelease(dependency.name, constraint);
+ if (constraint.min == null) {
+ _warnAboutNoConstraintLowerBound(dependency);
+ } else if (constraint.max == null) {
+ _warnAboutNoConstraintUpperBound(dependency);
+ }
+ }
+ _hasCaretDep =
+ _hasCaretDep || constraint.toString().startsWith('^');
+ }
+ }
+ }
+ }
+
await _validateDependencies(entrypoint.root.pubspec.dependencies.values);
if (_hasCaretDep) {
@@ -39,197 +227,4 @@
"Older versions of pub don't support ^ version constraints.");
}
}
-
- /// Validates all dependencies in [dependencies].
- Future _validateDependencies(Iterable<PackageRange> dependencies) async {
- for (var dependency in dependencies) {
- var constraint = dependency.constraint;
- if (dependency.name == 'flutter') {
- _warnAboutFlutterSdk(dependency);
- } else if (dependency.source is SdkSource) {
- _warnAboutSdkSource(dependency);
- } else if (dependency.source is HostedSource) {
- if (constraint.isAny) {
- _warnAboutNoConstraint(dependency);
- } else if (constraint is VersionRange) {
- if (constraint is Version) {
- _warnAboutSingleVersionConstraint(dependency);
- } else {
- _warnAboutPrerelease(dependency.name, constraint);
- if (constraint.min == null) {
- _warnAboutNoConstraintLowerBound(dependency);
- } else if (constraint.max == null) {
- _warnAboutNoConstraintUpperBound(dependency);
- }
- }
- _hasCaretDep = _hasCaretDep || constraint.toString().startsWith('^');
- }
- } else {
- await _warnAboutSource(dependency);
- final description = dependency.description;
-
- if (description is GitDescription && description.path != '.') {
- validateSdkConstraint(_firstGitPathVersion,
- "Older versions of pub don't support Git path dependencies.");
- }
- }
- }
- }
-
- /// Warn about improper dependencies on Flutter.
- void _warnAboutFlutterSdk(PackageRange dep) {
- if (dep.source is SdkSource) {
- _warnAboutSdkSource(dep);
- return;
- }
-
- errors.add('Don\'t depend on "${dep.name}" from the ${dep.source} '
- 'source. Use the SDK source instead. For example:\n'
- '\n'
- 'dependencies:\n'
- ' ${dep.name}:\n'
- ' sdk: ${dep.constraint}\n'
- '\n'
- 'The Flutter SDK is downloaded and managed outside of pub.');
- }
-
- /// Emit an error for dependencies from unknown SDKs or without appropriate
- /// constraints on the Dart SDK.
- void _warnAboutSdkSource(PackageRange dep) {
- final description = dep.description;
- if (description is! SdkDescription) {
- throw ArgumentError('Wrong source');
- }
- var identifier = description.sdk;
- var sdk = sdks[identifier];
- if (sdk == null) {
- errors.add('Unknown SDK "$identifier" for dependency "${dep.name}".');
- return;
- }
-
- validateSdkConstraint(sdk.firstPubVersion,
- "Older versions of pub don't support the ${sdk.name} SDK.");
- }
-
- /// Warn that dependencies should use the hosted source.
- Future _warnAboutSource(PackageRange dep) async {
- List<Version> versions;
- try {
- var ids = await entrypoint.cache
- .getVersions(entrypoint.cache.hosted.refFor(dep.name));
- versions = ids.map((id) => id.version).toList();
- } on ApplicationException catch (_) {
- versions = [];
- }
-
- late String constraint;
- if (versions.isNotEmpty) {
- constraint = '^${Version.primary(versions)}';
- } else {
- constraint = dep.constraint.toString();
- if (!dep.constraint.isAny && dep.constraint is! Version) {
- constraint = '"$constraint"';
- }
- }
-
- // Path sources are errors. Other sources are just warnings.
- var messages = dep.source is PathSource ? errors : warnings;
-
- messages.add('Don\'t depend on "${dep.name}" from the ${dep.source} '
- 'source. Use the hosted source instead. For example:\n'
- '\n'
- 'dependencies:\n'
- ' ${dep.name}: $constraint\n'
- '\n'
- 'Using the hosted source ensures that everyone can download your '
- 'package\'s dependencies along with your package.');
- }
-
- /// Warn that dependencies should have version constraints.
- void _warnAboutNoConstraint(PackageRange dep) {
- var message = 'Your dependency on "${dep.name}" should have a version '
- 'constraint.';
- var locked = entrypoint.lockFile.packages[dep.name];
- if (locked != null) {
- message = '$message For example:\n'
- '\n'
- 'dependencies:\n'
- ' ${dep.name}: ^${locked.version}\n';
- }
- warnings.add('$message\n'
- 'Without a constraint, you\'re promising to support ${log.bold("all")} '
- 'future versions of "${dep.name}".');
- }
-
- /// Warn that dependencies should allow more than a single version.
- void _warnAboutSingleVersionConstraint(PackageRange dep) {
- warnings.add(
- 'Your dependency on "${dep.name}" should allow more than one version. '
- 'For example:\n'
- '\n'
- 'dependencies:\n'
- ' ${dep.name}: ^${dep.constraint}\n'
- '\n'
- 'Constraints that are too tight will make it difficult for people to '
- 'use your package\n'
- 'along with other packages that also depend on "${dep.name}".');
- }
-
- /// Warn that dependencies should have lower bounds on their constraints.
- void _warnAboutNoConstraintLowerBound(PackageRange dep) {
- var message = 'Your dependency on "${dep.name}" should have a lower bound.';
- var locked = entrypoint.lockFile.packages[dep.name];
- if (locked != null) {
- String constraint;
- if (locked.version == (dep.constraint as VersionRange).max) {
- constraint = '^${locked.version}';
- } else {
- constraint = '">=${locked.version} ${dep.constraint}"';
- }
-
- message = '$message For example:\n'
- '\n'
- 'dependencies:\n'
- ' ${dep.name}: $constraint\n';
- }
- warnings.add('$message\n'
- 'Without a constraint, you\'re promising to support ${log.bold("all")} '
- 'previous versions of "${dep.name}".');
- }
-
- /// Warn that dependencies should have upper bounds on their constraints.
- void _warnAboutNoConstraintUpperBound(PackageRange dep) {
- String constraint;
- if ((dep.constraint as VersionRange).includeMin) {
- constraint = '^${(dep.constraint as VersionRange).min}';
- } else {
- constraint = '"${dep.constraint} '
- '<${(dep.constraint as VersionRange).min!.nextBreaking}"';
- }
- // TODO: Handle the case where `dep.constraint.min` is null.
-
- warnings
- .add('Your dependency on "${dep.name}" should have an upper bound. For '
- 'example:\n'
- '\n'
- 'dependencies:\n'
- ' ${dep.name}: $constraint\n'
- '\n'
- 'Without an upper bound, you\'re promising to support '
- '${log.bold("all")} future versions of ${dep.name}.');
- }
-
- void _warnAboutPrerelease(String dependencyName, VersionRange constraint) {
- final packageVersion = entrypoint.root.version;
- if (constraint.min != null &&
- constraint.min!.isPreRelease &&
- !packageVersion.isPreRelease) {
- warnings.add('Packages dependent on a pre-release of another package '
- 'should themselves be published as a pre-release version. '
- 'If this package needs $dependencyName version ${constraint.min}, '
- 'consider publishing the package as a pre-release instead.\n'
- 'See https://dart.dev/tools/pub/publishing#publishing-prereleases '
- 'For more information on pre-releases.');
- }
- }
}
diff --git a/lib/src/validator/dependency_override.dart b/lib/src/validator/dependency_override.dart
index 24e4e84..b6a49a0 100644
--- a/lib/src/validator/dependency_override.dart
+++ b/lib/src/validator/dependency_override.dart
@@ -6,14 +6,11 @@
import 'package:collection/collection.dart';
-import '../entrypoint.dart';
import '../validator.dart';
/// A validator that validates a package's dependencies overrides (or the
/// absence thereof).
class DependencyOverrideValidator extends Validator {
- DependencyOverrideValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() {
var overridden = MapKeySet(entrypoint.root.dependencyOverrides);
diff --git a/lib/src/validator/deprecated_fields.dart b/lib/src/validator/deprecated_fields.dart
index e1aee4f..942c8f5 100644
--- a/lib/src/validator/deprecated_fields.dart
+++ b/lib/src/validator/deprecated_fields.dart
@@ -4,14 +4,11 @@
import 'dart:async';
-import '../entrypoint.dart';
import '../validator.dart';
/// A validator that validates that a pubspec is not including deprecated fields
/// which are no longer read.
class DeprecatedFieldsValidator extends Validator {
- DeprecatedFieldsValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() async {
if (entrypoint.root.pubspec.fields.containsKey('transformers')) {
diff --git a/lib/src/validator/directory.dart b/lib/src/validator/directory.dart
index c487dc9..ae8ac57 100644
--- a/lib/src/validator/directory.dart
+++ b/lib/src/validator/directory.dart
@@ -6,14 +6,11 @@
import 'package:path/path.dart' as path;
-import '../entrypoint.dart';
import '../io.dart';
import '../validator.dart';
/// A validator that validates a package's top-level directories.
class DirectoryValidator extends Validator {
- DirectoryValidator(Entrypoint entrypoint) : super(entrypoint);
-
static final _pluralNames = [
'benchmarks',
'docs',
@@ -27,7 +24,7 @@
@override
Future<void> validate() async {
final visited = <String>{};
- for (final file in entrypoint.root.listFiles()) {
+ for (final file in files) {
// Find the topmost directory name of [file].
final dir = path.join(entrypoint.root.dir,
path.split(path.relative(file, from: entrypoint.root.dir)).first);
diff --git a/lib/src/validator/executable.dart b/lib/src/validator/executable.dart
index 2163e8c..b7679eb 100644
--- a/lib/src/validator/executable.dart
+++ b/lib/src/validator/executable.dart
@@ -6,20 +6,15 @@
import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../validator.dart';
/// Validates that a package's pubspec doesn't contain executables that
/// reference non-existent scripts.
class ExecutableValidator extends Validator {
- ExecutableValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() async {
- var binFiles = entrypoint.root
- .listFiles(beneath: 'bin', recursive: false)
- .map(entrypoint.root.relative)
- .toList();
+ final binFiles =
+ filesBeneath('bin', recursive: false).map(entrypoint.root.relative);
entrypoint.root.pubspec.executables.forEach((executable, script) {
var scriptPath = p.join('bin', '$script.dart');
diff --git a/lib/src/validator/flutter_constraint.dart b/lib/src/validator/flutter_constraint.dart
index d689a22..1b1e224 100644
--- a/lib/src/validator/flutter_constraint.dart
+++ b/lib/src/validator/flutter_constraint.dart
@@ -6,12 +6,10 @@
import 'package:pub_semver/pub_semver.dart';
-import '../entrypoint.dart';
import '../validator.dart';
/// Validates that a package's flutter constraint doesn't contain an upper bound
class FlutterConstraintValidator extends Validator {
- FlutterConstraintValidator(Entrypoint entrypoint) : super(entrypoint);
static const explanationUrl =
'https://dart.dev/go/flutter-upper-bound-deprecation';
diff --git a/lib/src/validator/flutter_plugin_format.dart b/lib/src/validator/flutter_plugin_format.dart
index 6f00f06..a56f7df 100644
--- a/lib/src/validator/flutter_plugin_format.dart
+++ b/lib/src/validator/flutter_plugin_format.dart
@@ -6,7 +6,6 @@
import 'package:pub_semver/pub_semver.dart';
-import '../entrypoint.dart';
import '../validator.dart';
const _pluginDocsUrl =
@@ -19,8 +18,6 @@
/// See:
/// https://flutter.dev/docs/development/packages-and-plugins/developing-packages
class FlutterPluginFormatValidator extends Validator {
- FlutterPluginFormatValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() async {
final pubspec = entrypoint.root.pubspec;
diff --git a/lib/src/validator/gitignore.dart b/lib/src/validator/gitignore.dart
index 532178b..809c537 100644
--- a/lib/src/validator/gitignore.dart
+++ b/lib/src/validator/gitignore.dart
@@ -7,7 +7,6 @@
import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../git.dart' as git;
import '../ignore.dart';
import '../io.dart';
@@ -19,8 +18,6 @@
/// .gitignore. These would be considered part of the package by previous
/// versions of pub.
class GitignoreValidator extends Validator {
- GitignoreValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future<void> validate() async {
if (entrypoint.root.inGitRepo) {
diff --git a/lib/src/validator/language_version.dart b/lib/src/validator/language_version.dart
index e52a92a..2041bcb 100644
--- a/lib/src/validator/language_version.dart
+++ b/lib/src/validator/language_version.dart
@@ -9,7 +9,6 @@
import 'package:stack_trace/stack_trace.dart';
import '../dart.dart';
-import '../entrypoint.dart';
import '../language_version.dart';
import '../log.dart' as log;
import '../utils.dart';
@@ -18,22 +17,18 @@
/// Validates that libraries do not opt into newer language versions than what
/// they declare in their pubspec.
class LanguageVersionValidator extends Validator {
- final AnalysisContextManager analysisContextManager =
- AnalysisContextManager();
-
- LanguageVersionValidator(Entrypoint entrypoint) : super(entrypoint) {
- var packagePath = p.normalize(p.absolute(entrypoint.root.dir));
- analysisContextManager.createContextsForDirectory(packagePath);
- }
-
@override
Future validate() async {
+ var packagePath = p.normalize(p.absolute(entrypoint.root.dir));
+ final analysisContextManager = AnalysisContextManager()
+ ..createContextsForDirectory(packagePath);
+
final declaredLanguageVersion = entrypoint.root.pubspec.languageVersion;
- for (final path in ['lib', 'bin']
- .map((path) => entrypoint.root.listFiles(beneath: path))
- .expand((files) => files)
- .where((String file) => p.extension(file) == '.dart')) {
+ for (final path in ['lib', 'bin'].expand((path) {
+ return filesBeneath(path, recursive: true)
+ .where((file) => p.extension(file) == '.dart');
+ })) {
CompilationUnit unit;
try {
unit = analysisContextManager.parse(path);
diff --git a/lib/src/validator/leak_detection.dart b/lib/src/validator/leak_detection.dart
index 7a398d4..03100f8 100644
--- a/lib/src/validator/leak_detection.dart
+++ b/lib/src/validator/leak_detection.dart
@@ -12,7 +12,6 @@
import 'package:pool/pool.dart';
import 'package:source_span/source_span.dart';
-import '../entrypoint.dart';
import '../ignore.dart';
import '../validator.dart';
@@ -26,8 +25,6 @@
/// accidentally leaked.
@sealed
class LeakDetectionValidator extends Validator {
- LeakDetectionValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future<void> validate() async {
// Load `false_secrets` from `pubspec.yaml`.
@@ -37,7 +34,7 @@
);
final pool = Pool(20); // don't read more than 20 files concurrently!
- final leaks = await Future.wait(entrypoint.root.listFiles().map((f) async {
+ final leaks = await Future.wait(files.map((f) async {
final relPath = entrypoint.root.relative(f);
// Skip files matching patterns in `false_secrets`
diff --git a/lib/src/validator/license.dart b/lib/src/validator/license.dart
index 1a2c68a..993a48f 100644
--- a/lib/src/validator/license.dart
+++ b/lib/src/validator/license.dart
@@ -4,26 +4,23 @@
import 'dart:async';
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../validator.dart';
+final licenseLike =
+ RegExp(r'^(([a-zA-Z0-9]+[-_])?(LICENSE|COPYING)|UNLICENSE)(\..*)?$');
+
/// A validator that checks that a LICENSE-like file exists.
class LicenseValidator extends Validator {
- LicenseValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() {
return Future.sync(() {
- final licenseLike =
- RegExp(r'^(([a-zA-Z0-9]+[-_])?(LICENSE|COPYING)|UNLICENSE)(\..*)?$');
- final candidates = entrypoint.root
- .listFiles(recursive: false)
- .map(path.basename)
- .where(licenseLike.hasMatch);
+ final candidates = filesBeneath('.', recursive: false)
+ .where((file) => licenseLike.hasMatch(p.basename(file)));
if (candidates.isNotEmpty) {
- if (!candidates.contains('LICENSE')) {
+ if (!candidates
+ .any((candidate) => p.basename(candidate) == 'LICENSE')) {
final firstCandidate = candidates.first;
warnings.add('Please consider renaming $firstCandidate to `LICENSE`. '
'See https://dart.dev/tools/pub/publishing#important-files.');
diff --git a/lib/src/validator/name.dart b/lib/src/validator/name.dart
index 07d945c..bc02031 100644
--- a/lib/src/validator/name.dart
+++ b/lib/src/validator/name.dart
@@ -6,21 +6,18 @@
import 'package:path/path.dart' as path;
-import '../entrypoint.dart';
import '../utils.dart';
import '../validator.dart';
/// A validator that the name of a package is legal and matches the library name
/// in the case of a single library.
class NameValidator extends Validator {
- NameValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() {
return Future.sync(() {
_checkName(entrypoint.root.name);
- var libraries = _libraries;
+ var libraries = _libraries(files);
if (libraries.length == 1) {
var libName = path.basenameWithoutExtension(libraries[0]);
@@ -34,10 +31,9 @@
/// Returns a list of all libraries in the current package as paths relative
/// to the package's root directory.
- List<String> get _libraries {
+ List<String> _libraries(List<String> files) {
var libDir = entrypoint.root.path('lib');
- return entrypoint.root
- .listFiles(beneath: 'lib')
+ return filesBeneath('lib', recursive: true)
.map((file) => path.relative(file, from: path.dirname(libDir)))
.where((file) =>
!path.split(file).contains('src') &&
diff --git a/lib/src/validator/null_safety_mixed_mode.dart b/lib/src/validator/null_safety_mixed_mode.dart
index 10be199..36f9796 100644
--- a/lib/src/validator/null_safety_mixed_mode.dart
+++ b/lib/src/validator/null_safety_mixed_mode.dart
@@ -6,7 +6,6 @@
import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../null_safety_analysis.dart';
import '../package_name.dart';
import '../source/path.dart';
@@ -15,8 +14,6 @@
/// Gives a warning when publishing a new version, if this package opts into
/// null safety, but any of the dependencies do not.
class NullSafetyMixedModeValidator extends Validator {
- NullSafetyMixedModeValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future<void> validate() async {
final pubspec = entrypoint.root.pubspec;
diff --git a/lib/src/validator/pubspec.dart b/lib/src/validator/pubspec.dart
index 2a5e052..ef9cff8 100644
--- a/lib/src/validator/pubspec.dart
+++ b/lib/src/validator/pubspec.dart
@@ -6,7 +6,6 @@
import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../validator.dart';
/// Validates that a package's pubspec exists.
@@ -14,13 +13,10 @@
/// In most cases this is clearly true, since pub can't run without a pubspec,
/// but it's possible that the pubspec is gitignored.
class PubspecValidator extends Validator {
- PubspecValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() async {
- var files = entrypoint.root.listFiles(recursive: false);
- if (!files.any((file) =>
- p.canonicalize(file) == p.canonicalize(entrypoint.pubspecPath))) {
+ if (!filesBeneath('.', recursive: false)
+ .any((file) => p.basename(file) == 'pubspec.yaml')) {
errors.add('The pubspec is hidden, probably by .gitignore or pubignore.');
}
}
diff --git a/lib/src/validator/pubspec_field.dart b/lib/src/validator/pubspec_field.dart
index 8a5c23e..91d8da3 100644
--- a/lib/src/validator/pubspec_field.dart
+++ b/lib/src/validator/pubspec_field.dart
@@ -4,16 +4,13 @@
import 'dart:async';
-import '../entrypoint.dart';
import '../validator.dart';
/// A validator that checks that the pubspec has valid "author" and "homepage"
/// fields.
class PubspecFieldValidator extends Validator {
- PubspecFieldValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
- Future validate() {
+ Future<void> validate() {
_validateFieldIsString('description');
_validateFieldUrl('homepage');
_validateFieldUrl('repository');
diff --git a/lib/src/validator/pubspec_typo.dart b/lib/src/validator/pubspec_typo.dart
index 9b04292..0843bc4 100644
--- a/lib/src/validator/pubspec_typo.dart
+++ b/lib/src/validator/pubspec_typo.dart
@@ -2,14 +2,11 @@
// 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 '../entrypoint.dart';
import '../levenshtein.dart';
import '../validator.dart';
/// Validates that a package's pubspec does not contain any typos in its keys.
class PubspecTypoValidator extends Validator {
- PubspecTypoValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
Future validate() async {
final fields = entrypoint.root.pubspec.fields;
diff --git a/lib/src/validator/readme.dart b/lib/src/validator/readme.dart
index 85de479..3604232 100644
--- a/lib/src/validator/readme.dart
+++ b/lib/src/validator/readme.dart
@@ -5,40 +5,51 @@
import 'dart:async';
import 'dart:convert';
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as p;
-import '../entrypoint.dart';
import '../io.dart';
import '../validator.dart';
+final _readmeRegexp = RegExp(r'^README($|\.)', caseSensitive: false);
+
/// Validates that a package's README exists and is valid utf-8.
class ReadmeValidator extends Validator {
- ReadmeValidator(Entrypoint entrypoint) : super(entrypoint);
-
@override
- Future validate() {
- return Future.sync(() {
- var readme = entrypoint.root.readmePath;
- if (readme == null) {
- warnings
- .add('Please add a README.md file that describes your package.');
- return;
- }
+ Future<void> validate() async {
+ // Find the path to the README file at the root of the entrypoint.
+ //
+ // If multiple READMEs are found, this uses the same conventions as
+ // pub.dev for choosing the primary one: the README with the fewest
+ // extensions that is lexically ordered first is chosen.
+ final readmes = filesBeneath('.', recursive: false)
+ .where((file) => p.basename(file).contains(_readmeRegexp));
- if (path.basename(readme) != 'README.md') {
- warnings.add('Please consider renaming $readme to `README.md`. '
- 'See https://dart.dev/tools/pub/publishing#important-files.');
- }
+ if (readmes.isEmpty) {
+ warnings.add('Please add a README.md file that describes your package.');
+ return;
+ }
- var bytes = readBinaryFile(readme);
- try {
- // utf8.decode doesn't allow invalid UTF-8.
- utf8.decode(bytes);
- } on FormatException catch (_) {
- warnings.add('$readme contains invalid UTF-8.\n'
- 'This will cause it to be displayed incorrectly on '
- 'the Pub site (https://pub.dev).');
- }
+ final readme = readmes.reduce((readme1, readme2) {
+ final extensions1 = '.'.allMatches(p.basename(readme1)).length;
+ final extensions2 = '.'.allMatches(p.basename(readme2)).length;
+ var comparison = extensions1.compareTo(extensions2);
+ if (comparison == 0) comparison = readme1.compareTo(readme2);
+ return (comparison <= 0) ? readme1 : readme2;
});
+
+ if (p.basename(readme) != 'README.md') {
+ warnings.add('Please consider renaming $readme to `README.md`. '
+ 'See https://dart.dev/tools/pub/publishing#important-files.');
+ }
+
+ var bytes = readBinaryFile(readme);
+ try {
+ // utf8.decode doesn't allow invalid UTF-8.
+ utf8.decode(bytes);
+ } on FormatException catch (_) {
+ warnings.add('$readme contains invalid UTF-8.\n'
+ 'This will cause it to be displayed incorrectly on '
+ 'the Pub site (https://pub.dev).');
+ }
}
}
diff --git a/lib/src/validator/relative_version_numbering.dart b/lib/src/validator/relative_version_numbering.dart
index 14c7334..1b99b79 100644
--- a/lib/src/validator/relative_version_numbering.dart
+++ b/lib/src/validator/relative_version_numbering.dart
@@ -6,7 +6,6 @@
import 'package:collection/collection.dart' show IterableExtension;
-import '../entrypoint.dart';
import '../exceptions.dart';
import '../null_safety_analysis.dart';
import '../package_name.dart';
@@ -18,18 +17,13 @@
static const String semverUrl =
'https://dart.dev/tools/pub/versioning#semantic-versions';
- final Uri? _server;
-
- RelativeVersionNumberingValidator(Entrypoint entrypoint, this._server)
- : super(entrypoint);
-
@override
Future<void> validate() async {
final hostedSource = entrypoint.cache.hosted;
List<PackageId> existingVersions;
try {
existingVersions = await entrypoint.cache.getVersions(
- hostedSource.refFor(entrypoint.root.name, url: _server.toString()),
+ hostedSource.refFor(entrypoint.root.name, url: serverUrl.toString()),
);
} on PackageNotFoundException {
existingVersions = [];
diff --git a/lib/src/validator/sdk_constraint.dart b/lib/src/validator/sdk_constraint.dart
index ff25e53..736876b 100644
--- a/lib/src/validator/sdk_constraint.dart
+++ b/lib/src/validator/sdk_constraint.dart
@@ -6,7 +6,6 @@
import 'package:pub_semver/pub_semver.dart';
-import '../entrypoint.dart';
import '../sdk.dart';
import '../validator.dart';
@@ -18,8 +17,6 @@
/// * is not depending on a prerelease, unless the package itself is a
/// prerelease.
class SdkConstraintValidator extends Validator {
- SdkConstraintValidator(Entrypoint entrypoint) : super(entrypoint);
-
/// Get SDK version constraint from `pubspec.yaml` without any defaults or
/// overrides.
VersionConstraint _sdkConstraintFromPubspecYaml() {
diff --git a/lib/src/validator/size.dart b/lib/src/validator/size.dart
index e989518..ae32496 100644
--- a/lib/src/validator/size.dart
+++ b/lib/src/validator/size.dart
@@ -3,9 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
-import 'dart:math' as math;
-import '../entrypoint.dart';
import '../io.dart';
import '../validator.dart';
@@ -14,30 +12,24 @@
/// A validator that validates that a package isn't too big.
class SizeValidator extends Validator {
- final Future<int> packageSize;
-
- SizeValidator(Entrypoint entrypoint, this.packageSize) : super(entrypoint);
-
@override
- Future validate() {
- return packageSize.then((size) {
- if (size <= _maxSize) return;
- var sizeInMb = (size / math.pow(2, 20)).toStringAsPrecision(4);
- // Current implementation of Package.listFiles skips hidden files
- var ignoreExists = fileExists(entrypoint.root.path('.gitignore'));
+ Future<void> validate() async {
+ if (packageSize <= _maxSize) return;
+ var sizeInMb = (packageSize / (1 << 20)).toStringAsPrecision(4);
+ // Current implementation of Package.listFiles skips hidden files
+ var ignoreExists = fileExists(entrypoint.root.path('.gitignore'));
- var error = StringBuffer('Your package is $sizeInMb MB. Hosted '
- 'packages must be smaller than 100 MB.');
+ var error = StringBuffer('Your package is $sizeInMb MB. Hosted '
+ 'packages must be smaller than 100 MB.');
- if (ignoreExists && !entrypoint.root.inGitRepo) {
- error.write(' Your .gitignore has no effect since your project '
- 'does not appear to be in version control.');
- } else if (!ignoreExists && entrypoint.root.inGitRepo) {
- error.write(' Consider adding a .gitignore to avoid including '
- 'temporary files.');
- }
+ if (ignoreExists && !entrypoint.root.inGitRepo) {
+ error.write(' Your .gitignore has no effect since your project '
+ 'does not appear to be in version control.');
+ } else if (!ignoreExists && entrypoint.root.inGitRepo) {
+ error.write(' Consider adding a .gitignore to avoid including '
+ 'temporary files.');
+ }
- errors.add(error.toString());
- });
+ errors.add(error.toString());
}
}
diff --git a/lib/src/validator/strict_dependencies.dart b/lib/src/validator/strict_dependencies.dart
index 881c55c..84f7a09 100644
--- a/lib/src/validator/strict_dependencies.dart
+++ b/lib/src/validator/strict_dependencies.dart
@@ -11,7 +11,6 @@
import 'package:stack_trace/stack_trace.dart';
import '../dart.dart';
-import '../entrypoint.dart';
import '../io.dart';
import '../log.dart' as log;
import '../utils.dart';
@@ -19,19 +18,16 @@
/// Validates that Dart source files only import declared dependencies.
class StrictDependenciesValidator extends Validator {
- final AnalysisContextManager analysisContextManager =
- AnalysisContextManager();
-
- StrictDependenciesValidator(Entrypoint entrypoint) : super(entrypoint) {
- var packagePath = p.normalize(p.absolute(entrypoint.root.dir));
- analysisContextManager.createContextsForDirectory(packagePath);
- }
-
/// Lazily returns all dependency uses in [files].
///
/// Files that do not parse and directives that don't import or export
/// `package:` URLs are ignored.
Iterable<_Usage> _findPackages(Iterable<String> files) sync* {
+ final packagePath = p.normalize(p.absolute(entrypoint.root.dir));
+ final AnalysisContextManager analysisContextManager =
+ AnalysisContextManager();
+ analysisContextManager.createContextsForDirectory(packagePath);
+
for (var file in files) {
List<UriBasedDirective> directives;
var contents = readTextFile(file);
@@ -105,10 +101,16 @@
}
}
- Iterable<_Usage> _usagesBeneath(List<String> paths) => _findPackages(paths
- .map((path) => entrypoint.root.listFiles(beneath: path))
- .expand((files) => files)
- .where((String file) => p.extension(file) == '.dart'));
+ Iterable<_Usage> _usagesBeneath(List<String> paths) {
+ return _findPackages(
+ paths.expand(
+ (path) {
+ return filesBeneath(path, recursive: true)
+ .where((file) => p.extension(file) == '.dart');
+ },
+ ),
+ );
+ }
}
/// A parsed import or export directive in a D source file.
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 216dbe0..10b2a76 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -839,14 +839,23 @@
}
/// A function that creates a [Validator] subclass.
-typedef ValidatorCreator = Validator Function(Entrypoint entrypoint);
+typedef ValidatorCreator = Validator Function();
/// Schedules a single [Validator] to run on the [appPath].
///
/// Returns a scheduled Future that contains the validator after validation.
-Future<Validator> validatePackage(ValidatorCreator fn) async {
+Future<Validator> validatePackage(ValidatorCreator fn, int? size) async {
var cache = SystemCache(rootDir: _pathInSandbox(cachePath));
- var validator = fn(Entrypoint(_pathInSandbox(appPath), cache));
+ final entrypoint = Entrypoint(_pathInSandbox(appPath), cache);
+ var validator = fn();
+ validator.context = ValidationContext(
+ entrypoint,
+ await Future.value(size ?? 100),
+ _globalServer == null
+ ? Uri.parse('https://pub.dev')
+ : Uri.parse(globalServer.url),
+ entrypoint.root.listFiles(),
+ );
await validator.validate();
return validator;
}
diff --git a/test/validator/changelog_test.dart b/test/validator/changelog_test.dart
index 5bef662..397723d 100644
--- a/test/validator/changelog_test.dart
+++ b/test/validator/changelog_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/changelog.dart';
import 'package:test/test.dart';
@@ -11,7 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator changelog(Entrypoint entrypoint) => ChangelogValidator(entrypoint);
+Validator changelog() => ChangelogValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/compiled_dartdoc_test.dart b/test/validator/compiled_dartdoc_test.dart
index f1d3d6d..da02005 100644
--- a/test/validator/compiled_dartdoc_test.dart
+++ b/test/validator/compiled_dartdoc_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/compiled_dartdoc.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator compiledDartdoc(Entrypoint entrypoint) =>
- CompiledDartdocValidator(entrypoint);
+Validator compiledDartdoc() => CompiledDartdocValidator();
void main() {
setUp(d.validPackage.create);
diff --git a/test/validator/dependency_override_test.dart b/test/validator/dependency_override_test.dart
index 21a437d..b801ad9 100644
--- a/test/validator/dependency_override_test.dart
+++ b/test/validator/dependency_override_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/dependency_override.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator dependencyOverride(Entrypoint entrypoint) =>
- DependencyOverrideValidator(entrypoint);
+Validator dependencyOverride() => DependencyOverrideValidator();
void main() {
test(
diff --git a/test/validator/dependency_test.dart b/test/validator/dependency_test.dart
index 5a6380b..f90ed1d 100644
--- a/test/validator/dependency_test.dart
+++ b/test/validator/dependency_test.dart
@@ -6,6 +6,7 @@
import 'dart:convert';
import 'package:path/path.dart' as path;
+
import 'package:pub/src/exit_codes.dart';
import 'package:test/test.dart';
diff --git a/test/validator/deprecated_fields_test.dart b/test/validator/deprecated_fields_test.dart
index ee4551d..65f3ca0 100644
--- a/test/validator/deprecated_fields_test.dart
+++ b/test/validator/deprecated_fields_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/deprecated_fields.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator deprecatedFields(Entrypoint entrypoint) =>
- DeprecatedFieldsValidator(entrypoint);
+Validator deprecatedFields() => DeprecatedFieldsValidator();
void main() {
setUp(d.validPackage.create);
diff --git a/test/validator/directory_test.dart b/test/validator/directory_test.dart
index 88fa80a..1ccebde 100644
--- a/test/validator/directory_test.dart
+++ b/test/validator/directory_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/directory.dart';
import 'package:test/test.dart';
@@ -11,7 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator directory(Entrypoint entrypoint) => DirectoryValidator(entrypoint);
+Validator directory() => DirectoryValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/executable_test.dart b/test/validator/executable_test.dart
index d9973e7..c1f2a50 100644
--- a/test/validator/executable_test.dart
+++ b/test/validator/executable_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/executable.dart';
import 'package:test/test.dart';
@@ -11,7 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator executable(Entrypoint entrypoint) => ExecutableValidator(entrypoint);
+Validator executable() => ExecutableValidator();
void main() {
setUp(d.validPackage.create);
diff --git a/test/validator/flutter_plugin_format_test.dart b/test/validator/flutter_plugin_format_test.dart
index 0c24bda..0a5b28c 100644
--- a/test/validator/flutter_plugin_format_test.dart
+++ b/test/validator/flutter_plugin_format_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/flutter_plugin_format.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator flutterPluginFormat(Entrypoint entrypoint) =>
- FlutterPluginFormatValidator(entrypoint);
+Validator flutterPluginFormat() => FlutterPluginFormatValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/language_version_test.dart b/test/validator/language_version_test.dart
index 39c9465..fc2351e 100644
--- a/test/validator/language_version_test.dart
+++ b/test/validator/language_version_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/language_version.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator validator(Entrypoint entrypoint) =>
- LanguageVersionValidator(entrypoint);
+Validator validator() => LanguageVersionValidator();
Future<void> setup(
{required String sdkConstraint, String? libraryLanguageVersion}) async {
diff --git a/test/validator/leak_detection_test.dart b/test/validator/leak_detection_test.dart
index c055e59..fc64c68 100644
--- a/test/validator/leak_detection_test.dart
+++ b/test/validator/leak_detection_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/leak_detection.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator leakDetection(Entrypoint entrypoint) =>
- LeakDetectionValidator(entrypoint);
+Validator leakDetection() => LeakDetectionValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/license_test.dart b/test/validator/license_test.dart
index 8717c75..17aeec5 100644
--- a/test/validator/license_test.dart
+++ b/test/validator/license_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:path/path.dart' as path;
-import 'package:pub/src/entrypoint.dart';
import 'package:pub/src/io.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/license.dart';
@@ -13,7 +12,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator license(Entrypoint entrypoint) => LicenseValidator(entrypoint);
+Validator license() => LicenseValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/name_test.dart b/test/validator/name_test.dart
index 7cbb51c..2e8f8af 100644
--- a/test/validator/name_test.dart
+++ b/test/validator/name_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:path/path.dart' as path;
-import 'package:pub/src/entrypoint.dart';
import 'package:pub/src/io.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/name.dart';
@@ -13,7 +12,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator name(Entrypoint entrypoint) => NameValidator(entrypoint);
+Validator name() => NameValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/pubspec_field_test.dart b/test/validator/pubspec_field_test.dart
index 8e6fefc..43db483 100644
--- a/test/validator/pubspec_field_test.dart
+++ b/test/validator/pubspec_field_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/pubspec_field.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator pubspecField(Entrypoint entrypoint) =>
- PubspecFieldValidator(entrypoint);
+Validator pubspecField() => PubspecFieldValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/pubspec_test.dart b/test/validator/pubspec_test.dart
index 9942220..ea0f5af 100644
--- a/test/validator/pubspec_test.dart
+++ b/test/validator/pubspec_test.dart
@@ -13,7 +13,7 @@
test('should consider a package valid if it has a pubspec', () async {
await d.validPackage.create();
- await expectValidation((entrypoint) => PubspecValidator(entrypoint));
+ await expectValidation(() => PubspecValidator());
});
test('should consider a package invalid if it has a .gitignored pubspec',
@@ -22,7 +22,6 @@
await d.validPackage.create();
await repo.create();
- await expectValidation((entrypoint) => PubspecValidator(entrypoint),
- errors: isNotEmpty);
+ await expectValidation(() => PubspecValidator(), errors: isNotEmpty);
});
}
diff --git a/test/validator/pubspec_typo_test.dart b/test/validator/pubspec_typo_test.dart
index ab2a547..bbe7db3 100644
--- a/test/validator/pubspec_typo_test.dart
+++ b/test/validator/pubspec_typo_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/pubspec_typo.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator pubspecTypo(Entrypoint entrypoint) =>
- PubspecTypoValidator(entrypoint);
+Validator pubspecTypo() => PubspecTypoValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/readme_test.dart b/test/validator/readme_test.dart
index 6519c6f..17b0602 100644
--- a/test/validator/readme_test.dart
+++ b/test/validator/readme_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:path/path.dart' as p;
-import 'package:pub/src/entrypoint.dart';
import 'package:pub/src/io.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/readme.dart';
@@ -13,7 +12,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator readme(Entrypoint entrypoint) => ReadmeValidator(entrypoint);
+Validator readme() => ReadmeValidator();
void main() {
setUp(d.validPackage.create);
diff --git a/test/validator/relative_version_numbering_test.dart b/test/validator/relative_version_numbering_test.dart
index 2f3da5c..9a8bca0 100644
--- a/test/validator/relative_version_numbering_test.dart
+++ b/test/validator/relative_version_numbering_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/relative_version_numbering.dart';
import 'package:test/test.dart';
@@ -11,10 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator validator(Entrypoint entrypoint) => RelativeVersionNumberingValidator(
- entrypoint,
- Uri.parse(globalServer.url),
- );
+Validator validator() => RelativeVersionNumberingValidator();
Future<void> setup({required String sdkConstraint}) async {
await d.validPackage.create();
diff --git a/test/validator/sdk_constraint_test.dart b/test/validator/sdk_constraint_test.dart
index c24adf2..7e82aef 100644
--- a/test/validator/sdk_constraint_test.dart
+++ b/test/validator/sdk_constraint_test.dart
@@ -2,7 +2,6 @@
// 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/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/sdk_constraint.dart';
import 'package:test/test.dart';
@@ -11,8 +10,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator sdkConstraint(Entrypoint entrypoint) =>
- SdkConstraintValidator(entrypoint);
+Validator sdkConstraint() => SdkConstraintValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/size_test.dart b/test/validator/size_test.dart
index 3a22e60..ba5151d 100644
--- a/test/validator/size_test.dart
+++ b/test/validator/size_test.dart
@@ -11,13 +11,10 @@
import '../test_pub.dart';
import 'utils.dart';
-ValidatorCreator size(int size) {
- return (entrypoint) => SizeValidator(entrypoint, Future.value(size));
-}
-
Future<void> expectSizeValidationError(Matcher matcher) async {
await expectValidation(
- size(100 * 1048577 /*2^20 +1*/),
+ () => SizeValidator(),
+ size: 100 * (1 << 20) + 1,
errors: contains(matcher),
);
}
@@ -26,8 +23,8 @@
test('considers a package valid if it is <= 100 MB', () async {
await d.validPackage.create();
- await expectValidation(size(100));
- await expectValidation(size(100 * 1048576 /*2^20*/));
+ await expectValidation(() => SizeValidator(), size: 100);
+ await expectValidation(() => SizeValidator(), size: 100 * (1 << 20));
});
group('considers a package invalid if it is more than 100 MB', () {
diff --git a/test/validator/strict_dependencies_test.dart b/test/validator/strict_dependencies_test.dart
index 7b7962a..a6b0833 100644
--- a/test/validator/strict_dependencies_test.dart
+++ b/test/validator/strict_dependencies_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:path/path.dart' as path;
-import 'package:pub/src/entrypoint.dart';
import 'package:pub/src/validator.dart';
import 'package:pub/src/validator/strict_dependencies.dart';
import 'package:test/test.dart';
@@ -12,8 +11,7 @@
import '../test_pub.dart';
import 'utils.dart';
-Validator strictDeps(Entrypoint entrypoint) =>
- StrictDependenciesValidator(entrypoint);
+Validator strictDeps() => StrictDependenciesValidator();
void main() {
group('should consider a package valid if it', () {
diff --git a/test/validator/utils.dart b/test/validator/utils.dart
index 5123683..8a1c0e2 100644
--- a/test/validator/utils.dart
+++ b/test/validator/utils.dart
@@ -10,8 +10,8 @@
// That would make them more robust, and test actual end2end behaviour.
Future<void> expectValidation(ValidatorCreator fn,
- {hints, warnings, errors}) async {
- final validator = await validatePackage(fn);
+ {hints, warnings, errors, int? size}) async {
+ final validator = await validatePackage(fn, size);
expect(validator.errors, errors ?? isEmpty);
expect(validator.warnings, warnings ?? isEmpty);
expect(validator.hints, hints ?? isEmpty);