| // 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 'dart:async'; |
| |
| import 'package:pub_semver/pub_semver.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) { |
| ArgumentError.checkNotNull(original, 'original'); |
| |
| return Pubspec( |
| original.name, |
| version: original.version, |
| sdkConstraints: original.sdkConstraints, |
| dependencies: original.dependencies.values, |
| devDependencies: [], // explicitly give empty list, to prevent lazy parsing |
| dependencyOverrides: original.dependencyOverrides.values, |
| ); |
| } |
| |
| /// Returns a new [Pubspec] without [original]'s dependency_overrides. |
| Pubspec stripDependencyOverrides(Pubspec original) { |
| ArgumentError.checkNotNull(original, 'original'); |
| |
| return Pubspec( |
| original.name, |
| version: original.version, |
| sdkConstraints: original.sdkConstraints, |
| dependencies: original.dependencies.values, |
| devDependencies: original.devDependencies.values, |
| dependencyOverrides: [], |
| ); |
| } |
| |
| Future<Pubspec> constrainedToAtLeastNullSafetyPubspec( |
| Pubspec original, SystemCache cache) async { |
| /// Get the first version of [package] opting in to null-safety. |
| Future<VersionConstraint> constrainToFirstWithNullSafety( |
| PackageRange packageRange) async { |
| final ref = packageRange.toRef(); |
| final available = await cache.getVersions(ref); |
| if (available.isEmpty) { |
| return stripUpperBound(packageRange.constraint); |
| } |
| |
| available.sort((x, y) => x.version.compareTo(y.version)); |
| |
| for (final p in available) { |
| final pubspec = await cache.describe(p); |
| if (pubspec.languageVersion.supportsNullSafety) { |
| return VersionRange(min: p.version, includeMin: true); |
| } |
| } |
| return stripUpperBound(packageRange.constraint); |
| } |
| |
| Future<List<PackageRange>> allConstrainedToAtLeastNullSafety( |
| Map<String, PackageRange> constrained, |
| ) async { |
| final result = await Future.wait(constrained.keys.map((name) async { |
| final packageRange = constrained[name]!; |
| var unconstrainedRange = packageRange; |
| |
| /// We only need to remove the upper bound if it is a hosted package. |
| if (packageRange.description is HostedDescription) { |
| unconstrainedRange = PackageRange( |
| packageRange.toRef(), |
| await constrainToFirstWithNullSafety(packageRange), |
| ); |
| } |
| return unconstrainedRange; |
| })); |
| |
| return result; |
| } |
| |
| final constrainedLists = await Future.wait([ |
| allConstrainedToAtLeastNullSafety(original.dependencies), |
| allConstrainedToAtLeastNullSafety(original.devDependencies), |
| ]); |
| |
| return Pubspec( |
| original.name, |
| version: original.version, |
| sdkConstraints: original.sdkConstraints, |
| dependencies: constrainedLists[0], |
| devDependencies: constrainedLists[1], |
| dependencyOverrides: original.dependencyOverrides.values, |
| ); |
| } |
| |
| /// Returns new pubspec with the same dependencies as [original] but with the |
| /// upper bounds of the constraints removed. |
| /// |
| /// If [stripOnly] is provided, only the packages whose names are in |
| /// [stripOnly] will have their upper bounds removed. If [stripOnly] is |
| /// not specified or empty, then all packages will have their upper bounds |
| /// removed. |
| Pubspec stripVersionUpperBounds(Pubspec original, |
| {Iterable<String>? stripOnly}) { |
| ArgumentError.checkNotNull(original, 'original'); |
| stripOnly ??= []; |
| |
| List<PackageRange> stripUpperBounds( |
| 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(), |
| stripUpperBound(packageRange.constraint), |
| ); |
| } |
| result.add(unconstrainedRange); |
| } |
| |
| return result; |
| } |
| |
| return Pubspec( |
| original.name, |
| version: original.version, |
| sdkConstraints: original.sdkConstraints, |
| dependencies: stripUpperBounds(original.dependencies), |
| devDependencies: stripUpperBounds(original.devDependencies), |
| dependencyOverrides: original.dependencyOverrides.values, |
| ); |
| } |
| |
| /// 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; |
| } |