Normalize pub.dartlang.org to pub.dev when reading lockfile (#3754)

Cherry-pick of 3ce46a32e14b76555de70afa9dc1281ab999159c
diff --git a/lib/src/command/global_activate.dart b/lib/src/command/global_activate.dart
index 53353ae..505ebe4 100644
--- a/lib/src/command/global_activate.dart
+++ b/lib/src/command/global_activate.dart
@@ -7,7 +7,7 @@
 import 'package:pub_semver/pub_semver.dart';
 
 import '../command.dart';
-import '../source/hosted.dart';
+import '../package_name.dart';
 import '../utils.dart';
 
 /// Handles the `global activate` pub command.
@@ -69,14 +69,6 @@
     }
 
     final overwrite = argResults['overwrite'] as bool;
-    Uri? hostedUrl;
-    if (argResults.wasParsed('hosted-url')) {
-      try {
-        hostedUrl = validateAndNormalizeHostedUrl(argResults['hosted-url']);
-      } on FormatException catch (e) {
-        usageException('Invalid hosted-url: $e');
-      }
-    }
 
     Iterable<String> args = argResults.rest;
 
@@ -115,6 +107,13 @@
       case 'hosted':
         var package = readArg('No package to activate given.');
 
+        PackageRef ref;
+        try {
+          ref = cache.hosted.refFor(package, url: argResults['hosted-url']);
+        } on FormatException catch (e) {
+          usageException('Invalid hosted-url: $e');
+        }
+
         // Parse the version constraint, if there is one.
         var constraint = VersionConstraint.any;
         if (args.isNotEmpty) {
@@ -127,11 +126,9 @@
 
         validateNoExtraArgs();
         return globals.activateHosted(
-          package,
-          constraint,
+          ref.withConstraint(constraint),
           executables,
           overwriteBinStubs: overwrite,
-          url: hostedUrl?.toString(),
         );
 
       case 'path':
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 089016a..27c46ec 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -139,16 +139,16 @@
   /// [url] is an optional custom pub server URL. If not null, the package to be
   /// activated will be fetched from this URL instead of the default pub URL.
   Future<void> activateHosted(
-    String name,
-    VersionConstraint constraint,
+    PackageRange range,
     List<String>? executables, {
     required bool overwriteBinStubs,
     String? url,
   }) async {
     await _installInCache(
-        cache.hosted.refFor(name, url: url).withConstraint(constraint),
-        executables,
-        overwriteBinStubs: overwriteBinStubs);
+      range,
+      executables,
+      overwriteBinStubs: overwriteBinStubs,
+    );
   }
 
   /// Makes the local package at [path] globally active.
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index 2d481e4..6f590e1 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -51,7 +51,7 @@
 /// backwards compatibility with `pubspec.lock`-files which contain
 /// `https://pub.dartlang.org`.
 ///
-/// Throws [FormatException] if there is anything wrong [hostedUrl].
+/// Throws [FormatException] if there is anything wrong with [hostedUrl].
 ///
 /// [1]: ../../../doc/repository-spec-v2.md
 Uri validateAndNormalizeHostedUrl(String hostedUrl) {
@@ -194,8 +194,8 @@
   /// Returns a reference to a hosted package named [name].
   ///
   /// If [url] is passed, it's the URL of the pub server from which the package
-  /// should be downloaded. [url] most be normalized and validated using
-  /// [validateAndNormalizeHostedUrl].
+  /// should be downloaded. [url] will be normalized and validated using
+  /// [validateAndNormalizeHostedUrl]. This can throw a [FormatException].
   PackageRef refFor(String name, {String? url}) {
     final d = HostedDescription(name, url ?? defaultUrl);
     return PackageRef(name, d);
@@ -258,7 +258,7 @@
       name,
       version,
       ResolvedHostedDescription(
-        HostedDescription(name, Uri.parse(url).toString()),
+        HostedDescription(name, url),
         sha256: sha256 == null ? null : hexDecode(sha256),
       ),
     );
@@ -291,8 +291,7 @@
       // environment, we throw an error if something that looks like a URI is
       // used as a package name.
       if (canUseShorthandSyntax) {
-        return HostedDescription(
-            packageName, validateAndNormalizeHostedUrl(description).toString());
+        return HostedDescription(packageName, description);
       } else {
         if (_looksLikePackageName.hasMatch(description)) {
           // Valid use of `hosted: package` dependency with an old SDK
@@ -319,14 +318,11 @@
           'a minimum Dart SDK constraint of ${LanguageVersion.firstVersionWithShorterHostedSyntax}.0 or higher.');
     }
 
-    var url = defaultUrl;
     final u = description['url'];
-    if (u != null) {
-      if (u is! String) {
-        throw FormatException("The 'url' key must be a string value.");
-      }
-      url = validateAndNormalizeHostedUrl(u).toString();
+    if (u != null && u is! String) {
+      throw FormatException("The 'url' key must be a string value.");
     }
+    final url = u ?? defaultUrl;
 
     return HostedDescription(name, url);
   }
@@ -915,7 +911,7 @@
               package.name,
               package.version,
               ResolvedHostedDescription(
-                HostedDescription(package.name, url),
+                HostedDescription._(package.name, url),
                 sha256: null,
               ),
             );
@@ -1178,10 +1174,7 @@
         pubspec.name,
         pubspec.version,
         ResolvedHostedDescription(
-          HostedDescription(
-            pubspec.name,
-            validateAndNormalizeHostedUrl(cache.hosted.defaultUrl).toString(),
-          ),
+          HostedDescription(pubspec.name, defaultUrl),
           sha256: contentHash,
         ),
       );
@@ -1283,7 +1276,12 @@
   final String packageName;
   final String url;
 
-  HostedDescription(this.packageName, this.url);
+  HostedDescription._(this.packageName, this.url);
+  factory HostedDescription(String packageName, String url) =>
+      HostedDescription._(
+        packageName,
+        validateAndNormalizeHostedUrl(url).toString(),
+      );
 
   @override
   int get hashCode => Object.hash(packageName, url);
diff --git a/test/lock_file_test.dart b/test/lock_file_test.dart
index 20e3976..7e01e7f 100644
--- a/test/lock_file_test.dart
+++ b/test/lock_file_test.dart
@@ -205,6 +205,41 @@
         }, throwsFormatException);
       });
 
+      test('Reads pub.dartlang.org as pub.dev in hosted descriptions', () {
+        final lockfile = LockFile.parse(
+          '''
+packages:
+  characters:
+    dependency: transitive
+    description:
+      name: characters
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.2.1"
+  retry:
+    dependency: transitive
+    description:
+      name: retry
+      url: "https://pub.dev"
+      sha256:
+    source: hosted
+    version: "1.0.0"
+''',
+          sources,
+        );
+        void expectComesFromPubDev(String name) {
+          final description = lockfile.packages[name]!.description.description
+              as HostedDescription;
+          expect(
+            description.url,
+            'https://pub.dev',
+          );
+        }
+
+        expectComesFromPubDev('characters');
+        expectComesFromPubDev('retry');
+      });
+
       test('ignores extra stuff in file', () {
         LockFile.parse('''
 extra: