Allow package_config.json to contain flutter_gen (#2830)

Allow package_config.json to contain flutter_gen

Also append trailing '/' to packageUri when loading package_config.json if not already present.
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index a56f92d..878051b 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -597,11 +597,11 @@
     // Check that [packagePathsMapping] does not contain more packages than what
     // is required. This could lead to import statements working, when they are
     // not supposed to work.
-    final hasExtraMappings = packagePathsMapping.keys.every((packageName) {
+    final hasExtraMappings = !packagePathsMapping.keys.every((packageName) {
       return packageName == root.name ||
           lockFile.packages.containsKey(packageName);
     });
-    if (!hasExtraMappings) {
+    if (hasExtraMappings) {
       return false;
     }
 
@@ -620,8 +620,7 @@
       }
 
       final source = cache.source(lockFileId.source);
-      final lockFilePackagePath =
-          p.join(root.dir, source.getDirectory(lockFileId));
+      final lockFilePackagePath = root.path(source.getDirectory(lockFileId));
 
       // Make sure that the packagePath agrees with the lock file about the
       // path to the package.
@@ -720,8 +719,18 @@
     }
 
     final packagePathsMapping = <String, String>{};
-    for (final pkg in cfg.packages) {
-      // Pub always sets packageUri = lib/
+
+    // We allow the package called 'flutter_gen' to be injected into
+    // package_config.
+    //
+    // This is somewhat a hack. But it allows flutter to generate code in a
+    // package as it likes.
+    //
+    // See https://github.com/flutter/flutter/issues/73870 .
+    final packagesToCheck =
+        cfg.packages.where((package) => package.name != 'flutter_gen');
+    for (final pkg in packagesToCheck) {
+      // Pub always makes a packageUri of lib/
       if (pkg.packageUri == null || pkg.packageUri.toString() != 'lib/') {
         badPackageConfig();
       }
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index 340860b..20775a6 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -213,11 +213,14 @@
     }
 
     Uri packageUri;
-    final packageUriRaw = root['packageUri'];
+    var packageUriRaw = root['packageUri'];
     if (packageUriRaw != null) {
       if (packageUriRaw is! String) {
         _throw('packageUri', 'must be a string');
       }
+      if (!packageUriRaw.endsWith('/')) {
+        packageUriRaw = '$packageUriRaw/';
+      }
       try {
         packageUri = Uri.parse(packageUriRaw);
       } on FormatException {
diff --git a/test/must_pub_get_test.dart b/test/must_pub_get_test.dart
index abecb74..6ad636f 100644
--- a/test/must_pub_get_test.dart
+++ b/test/must_pub_get_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
 import 'dart:io';
 
 import 'package:path/path.dart' as p;
@@ -30,6 +31,32 @@
     await pubGet();
   });
 
+  test(
+      'does not require a pub get if a `flutter_gen` package is injected into package_config.json',
+      () async {
+    await d.dir('bar', [
+      d.pubspec({'name': 'bar'})
+    ]).create();
+    await d.dir(appPath, [
+      d.appPubspec({
+        'bar': {'path': '../bar'}
+      })
+    ]).create();
+
+    await pubGet();
+
+    final packageConfig =
+        p.join(d.sandbox, 'myapp', '.dart_tool', 'package_config.json');
+    final contents = json.decode(readTextFile(packageConfig));
+    contents['packages'].add({
+      'name': 'flutter_gen',
+      'rootUri': '.dart_tool/flutter_gen',
+      'languageVersion': '2.8',
+    });
+    writeTextFile(packageConfig, json.encode(contents));
+
+    await runPub(args: ['run', 'bin/script.dart'], output: 'hello!');
+  });
   group('requires the user to run pub get first if', () {
     group("there's no lockfile", () {
       setUp(() {