Newer
Older
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// 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_semver/pub_semver.dart';
import '../entrypoint.dart';
import '../exceptions.dart';
import '../log.dart' as log;
import '../package_name.dart';
import '../source/git.dart';
import '../source/hosted.dart';
import '../source/path.dart';
import '../source/sdk.dart';
import '../validator.dart';
/// The first Dart SDK version that supported caret constraints.
final _firstCaretVersion = new Version.parse("1.8.0-dev.3.0");
/// The first Dart SDK version that supported Git path dependencies.
final _firstGitPathVersion = new Version.parse("1.25.0-dev.5.0");
/// A validator that validates a package's dependencies.
class DependencyValidator extends Validator {
DependencyValidator(Entrypoint entrypoint) : super(entrypoint);
var hasCaretDep = false;
for (var dependency in entrypoint.root.pubspec.dependencies) {
if (dependency.name == "flutter") {
_warnAboutFlutterSdk(dependency);
} else if (dependency.source is! HostedSource) {
await _warnAboutSource(dependency);
if (dependency.source is GitSource &&
dependency.description['path'] != '.') {
validateSdkConstraint(_firstGitPathVersion,
"Older versions of pub don't support Git path dependencies.");
}
_warnAboutNoConstraint(dependency);
_warnAboutSingleVersionConstraint(dependency);
} else if (constraint is VersionRange) {
if (constraint.min == null) {
_warnAboutNoConstraintLowerBound(dependency);
_warnAboutNoConstraintUpperBound(dependency);
}
hasCaretDep = hasCaretDep || constraint.toString().startsWith("^");
if (hasCaretDep) {
validateSdkConstraint(_firstCaretVersion,
"Older versions of pub don't support ^ version constraints.");
/// Warn about improper dependencies on Flutter.
void _warnAboutFlutterSdk(PackageRange dep) {
if (dep.source is SdkSource) 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 use the hosted source.
Future _warnAboutSource(PackageRange dep) async {
var ids = await entrypoint.cache.hosted
.getVersions(entrypoint.cache.sources.hosted.refFor(dep.name));
versions = ids.map((id) => id.version).toList();
} on ApplicationException catch (_) {
versions = [];
}
var constraint;
var primary = Version.primary(versions);
if (primary != null) {
constraint = "^$primary";
} else {
constraint = dep.constraint.toString();
if (!dep.constraint.isAny && dep.constraint is! Version) {
constraint = '"$constraint"';
rnystrom@google.com
committed
}
}
// Path sources are errors. Other sources are just warnings.
var messages = dep.source is PathSource ? errors : warnings;
rnystrom@google.com
committed
messages.add('Don\'t depend on "${dep.name}" from the ${dep.source} '
'\n'
'dependencies:\n'
' ${dep.name}: $constraint\n'
'\n'
'Using the hosted source ensures that everyone can download your '
}
/// Warn that dependencies should have version constraints.
void _warnAboutNoConstraint(PackageRange dep) {
rnystrom@google.com
committed
var message = 'Your dependency on "${dep.name}" should have a version '
var locked = entrypoint.lockFile.packages[dep.name];
if (locked != null) {
message = '$message For example:\n'
' ${dep.name}: ^${locked.version}\n';
}
warnings.add("$message\n"
'Without a constraint, you\'re promising to support ${log.bold("all")} '
/// 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. '
'\n'
'dependencies:\n'
' ${dep.name}: ^${dep.constraint}\n'
'\n'
'Constraints that are too tight will make it difficult for people to '
'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) {
var 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")} '
/// Warn that dependencies should have upper bounds on their constraints.
void _warnAboutNoConstraintUpperBound(PackageRange dep) {
var constraint;
if ((dep.constraint as VersionRange).includeMin) {
constraint = "^${(dep.constraint as VersionRange).min}";
} else {
constraint = '"${dep.constraint} '
'<${(dep.constraint as VersionRange).min.nextBreaking}"';
}
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}.');