blob: 646114c931b59ebf36c9fb8f0929e57b9d9042da [file] [log] [blame]
// Copyright (c) 2020, 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:collection/collection.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package.dart';
import 'package_name.dart';
import 'pubspec.dart';
import 'source/hosted.dart';
import 'system_cache.dart';
/// Returns a new [Pubspec] without [original]'s dev_dependencies.
Pubspec stripDevDependencies(Pubspec original) {
return original.copyWith(devDependencies: []);
}
/// Returns a new [Pubspec] without [original]'s dependency_overrides.
Pubspec stripDependencyOverrides(Pubspec original) {
return original.copyWith(dependencyOverrides: []);
}
/// Returns new pubspec with the same dependencies as [original] but with the
/// the bounds of the constraints removed.
///
/// If [stripLower] is `false` (the default) only the upper bound is removed.
///
/// If [stripOnly] is provided, only the packages whose names are in [stripOnly]
/// will have their bounds removed. If [stripOnly] is not specified or empty,
/// then all packages will have their bounds removed.
Pubspec stripVersionBounds(
Pubspec original, {
Iterable<String>? stripOnly,
bool stripLowerBound = false,
}) {
stripOnly ??= [];
List<PackageRange> stripBounds(
Map<String, PackageRange> constrained,
) {
final result = <PackageRange>[];
for (final name in constrained.keys) {
final packageRange = constrained[name]!;
var unconstrainedRange = packageRange;
if (stripOnly!.isEmpty || stripOnly.contains(packageRange.name)) {
unconstrainedRange = PackageRange(
packageRange.toRef(),
stripLowerBound
? VersionConstraint.any
: stripUpperBound(packageRange.constraint),
);
}
result.add(unconstrainedRange);
}
return result;
}
return original.copyWith(
dependencies: stripBounds(original.dependencies),
devDependencies: stripBounds(original.devDependencies),
);
}
/// Returns a pubspec with the same dependencies as [original] but with all
/// version constraints replaced by `>=c` where `c`, is the member of `current`
/// that has same name as the dependency.
Pubspec atLeastCurrent(Pubspec original, List<PackageId> current) {
List<PackageRange> fixBounds(
Map<String, PackageRange> constrained,
) {
final result = <PackageRange>[];
for (final name in constrained.keys) {
final packageRange = constrained[name]!;
final currentVersion = current.firstWhereOrNull((id) => id.name == name);
if (currentVersion == null) {
result.add(packageRange);
} else {
result.add(
packageRange.toRef().withConstraint(
VersionRange(min: currentVersion.version, includeMin: true),
),
);
}
}
return result;
}
return original.copyWith(
dependencies: fixBounds(original.dependencies),
devDependencies: fixBounds(original.devDependencies),
);
}
/// Removes the upper bound of [constraint]. If [constraint] is the
/// empty version constraint, [VersionConstraint.empty] will be returned.
VersionConstraint stripUpperBound(VersionConstraint constraint) {
ArgumentError.checkNotNull(constraint, 'constraint');
/// A [VersionConstraint] has to either be a [VersionRange], [VersionUnion],
/// or the empty [VersionConstraint].
if (constraint is VersionRange) {
return VersionRange(min: constraint.min, includeMin: constraint.includeMin);
}
if (constraint is VersionUnion) {
if (constraint.ranges.isEmpty) return VersionConstraint.empty;
final firstRange = constraint.ranges.first;
return VersionRange(min: firstRange.min, includeMin: firstRange.includeMin);
}
assert(constraint == VersionConstraint.empty, 'unknown constraint type');
/// If it gets here, [constraint] is the empty version constraint, so we
/// just return an empty version constraint.
return VersionConstraint.empty;
}
/// Returns a somewhat normalized version the description of a dependency with a
/// version constraint (what comes after the version name in a dependencies
/// section) as a json-style object.
///
/// Will use just the constraint for dependencies hosted at the default host.
///
/// Relative paths will be relative to [relativeEntrypoint].
///
/// The syntax used for hosted will depend on the language version of
/// [relativeEntrypoint]
Object pubspecDescription(
PackageRange range,
SystemCache cache,
Package receivingPackage,
) {
final description = range.description;
final constraint = range.constraint;
if (description is HostedDescription &&
description.url == cache.hosted.defaultUrl) {
return constraint.toString();
} else {
return {
range.source.name: description.serializeForPubspec(
containingDir: receivingPackage.dir,
languageVersion: receivingPackage.pubspec.languageVersion,
),
if (!constraint.isAny) 'version': constraint.toString(),
};
}
}