Remove json_schema and cli_util deps from flutter_tool (#28040)

* remove json_schema dep

* remove unnecessary pin

* removed unused dep

* remove unused impport

* Fix incorrect condition
diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart
index 6eb5e43..5f7c5d8 100644
--- a/packages/flutter_tools/lib/src/commands/update_packages.dart
+++ b/packages/flutter_tools/lib/src/commands/update_packages.dart
@@ -23,7 +23,6 @@
 const Map<String, String> _kManuallyPinnedDependencies = <String, String>{
   // Add pinned packages here.
   'flutter_gallery_assets': '0.1.6', // See //examples/flutter_gallery/pubspec.yaml
-  'json_schema': '1.0.10',
 };
 
 class UpdatePackagesCommand extends FlutterCommand {
diff --git a/packages/flutter_tools/lib/src/flutter_manifest.dart b/packages/flutter_tools/lib/src/flutter_manifest.dart
index d9b878a..ac0bef1 100644
--- a/packages/flutter_tools/lib/src/flutter_manifest.dart
+++ b/packages/flutter_tools/lib/src/flutter_manifest.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:json_schema/json_schema.dart';
 import 'package:meta/meta.dart';
 import 'package:pub_semver/pub_semver.dart';
 import 'package:yaml/yaml.dart';
@@ -13,7 +12,6 @@
 import 'base/user_messages.dart';
 import 'base/utils.dart';
 import 'cache.dart';
-import 'convert.dart' as convert;
 import 'globals.dart';
 
 /// A wrapper around the `flutter` section in the `pubspec.yaml` file.
@@ -289,18 +287,152 @@
   );
 }
 
-Future<bool> _validate(dynamic manifest) async {
-  final String schemaPath = buildSchemaPath(fs);
+/// This method should be kept in sync with the schema in
+/// `$FLUTTER_ROOT/packages/flutter_tools/schema/pubspec_yaml.json`,
+/// but avoid introducing depdendencies on packages for simple validation.
+Future<bool> _validate(YamlMap manifest) async {
+  final List<String> errors = <String>[];
+  for (final MapEntry<dynamic, dynamic> kvp in manifest.entries) {
+    if (kvp.key is! String) {
+      errors.add('Expected YAML key to be a a string, but got ${kvp.key}.');
+      continue;
+    }
+    switch (kvp.key) {
+      case 'name':
+        if (kvp.value is! String) {
+          errors.add('Expected "${kvp.key}" to be a string, but got ${kvp.value}.');
+        }
+        break;
+      case 'flutter':
+        if (kvp.value == null) {
+          continue;
+        }
+        if (kvp.value is! YamlMap) {
+          errors.add('Expected "${kvp.key}" section to be an object or null, but got ${kvp.value}.');
+        }
+        _validateFlutter(kvp.value, errors);
+        break;
+      default:
+        // additionalProperties are allowed.
+        break;
+    }
+  }
 
-  final String schemaData = fs.file(schemaPath).readAsStringSync();
-  final Schema schema = await Schema.createSchema(
-      convert.json.decode(schemaData));
-  final Validator validator = Validator(schema);
-  if (validator.validate(manifest)) {
-    return true;
-  } else {
+  if (errors.isNotEmpty) {
     printStatus('Error detected in pubspec.yaml:', emphasis: true);
-    printError(validator.errors.join('\n'));
+    printError(errors.join('\n'));
     return false;
   }
+
+  return true;
 }
+
+void _validateFlutter(YamlMap yaml, List<String> errors) {
+  if (yaml == null || yaml.entries == null) {
+    return;
+  }
+  for (final MapEntry<dynamic, dynamic> kvp in yaml.entries) {
+    if (kvp.key is! String) {
+      errors.add('Expected YAML key to be a a string, but got ${kvp.key} (${kvp.value.runtimeType}).');
+      continue;
+    }
+    switch (kvp.key) {
+      case 'uses-material-design':
+        if (kvp.value is! bool) {
+          errors.add('Expected "${kvp.key}" to be a bool, but got ${kvp.value} (${kvp.value.runtimeType}).');
+        }
+        break;
+      case 'assets':
+      case 'services':
+        if (kvp.value is! YamlList || kvp.value[0] is! String) {
+          errors.add('Expected "${kvp.key}" to be a list, but got ${kvp.value} (${kvp.value.runtimeType}).');
+        }
+        break;
+      case 'fonts':
+        if (kvp.value is! YamlList || kvp.value[0] is! YamlMap) {
+          errors.add('Expected "${kvp.key}" to be a list, but got ${kvp.value} (${kvp.value.runtimeType}).');
+        }
+        _validateFonts(kvp.value, errors);
+        break;
+      case 'module':
+        if (kvp.value is! YamlMap) {
+          errors.add('Expected "${kvp.key}" to be an object, but got ${kvp.value} (${kvp.value.runtimeType}).');
+        }
+
+        if (kvp.value['androidPackage'] != null && kvp.value['androidPackage'] is! String) {
+          errors.add('The "androidPackage" value must be a string if set.');
+        }
+        if (kvp.value['iosBundleIdentifier'] != null && kvp.value['iosBundleIdentifier'] is! String) {
+          errors.add('The "iosBundleIdentifier" section must be a string if set.');
+        }
+        break;
+      case 'plugin':
+        if (kvp.value is! YamlMap) {
+          errors.add('Expected "${kvp.key}" to be an object, but got ${kvp.value} (${kvp.value.runtimeType}).');
+        }
+        if (kvp.value['androidPackage'] != null && kvp.value['androidPackage'] is! String) {
+          errors.add('The "androidPackage" must either be null or a string.');
+        }
+        if (kvp.value['iosPrefix'] != null && kvp.value['iosPrefix'] is! String) {
+          errors.add('The "iosPrefix" must eithe rbe null or a string.');
+        }
+        if (kvp.value['pluginClass'] != null && kvp.value['pluginClass'] is! String) {
+          errors.add('The "pluginClass" must either be null or a string..');
+        }
+        break;
+      default:
+        errors.add('Unexpected child "${kvp.key}" found under "flutter".');
+        break;
+    }
+  }
+}
+
+void _validateFonts(YamlList fonts, List<String> errors) {
+  if (fonts == null) {
+    return;
+  }
+  const Set<int> fontWeights = const <int>{
+    100, 200, 300, 400, 500, 600, 700, 800, 900,
+  };
+  for (final YamlMap fontMap in fonts) {
+    for (dynamic key in fontMap.keys.where((dynamic key) => key != 'family' && key != 'fonts')) {
+      errors.add('Unexpected child "$key" found under "fonts".');
+    }
+    if (fontMap['family'] != null && fontMap['family'] is! String) {
+      errors.add('Font family must either be null or a String.');
+    }
+    if (fontMap['fonts'] != null && fontMap['fonts'] is! YamlList) {
+      errors.add('Expected "fonts" to either be null or a list.');
+    }
+    if (fontMap['fonts'] == null) {
+      continue;
+    }
+    for (final YamlMap fontListItem in fontMap['fonts']) {
+      for (final MapEntry<dynamic, dynamic> kvp in fontListItem.entries) {
+        if (kvp.key is! String) {
+          errors.add('Expected "${kvp.key}" under "fonts" to be a string.');
+        }
+        switch(kvp.key) {
+          case 'asset':
+            if (kvp.value is! String) {
+              errors.add('Expected font asset ${kvp.value} ((${kvp.value.runtimeType})) to be a string.');
+            }
+            break;
+          case 'weight':
+            if (!fontWeights.contains(kvp.value)) {
+              errors.add('Invalid value ${kvp.value} ((${kvp.value.runtimeType})) for font -> weight.');
+            }
+            break;
+          case 'style':
+            if (kvp.value != 'normal' && kvp.value != 'italic') {
+              errors.add('Invalid value ${kvp.value} ((${kvp.value.runtimeType})) for font -> style.');
+            }
+            break;
+          default:
+            errors.add('Unexpected key ${kvp.key} ((${kvp.value.runtimeType})) under font.');
+            break;
+        }
+      }
+    }
+  }
+}
\ No newline at end of file