diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index 6eb5e43e1ad..5f7c5d87384 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 @@ import '../runner/flutter_command.dart'; const Map _kManuallyPinnedDependencies = { // 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 d9b878a1dc6..ac0bef1dd0c 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/file_system.dart'; 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 @@ String buildSchemaPath(FileSystem fs) { ); } -Future _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 _validate(YamlMap manifest) async { + final List errors = []; + for (final MapEntry 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 errors) { + if (yaml == null || yaml.entries == null) { + return; + } + for (final MapEntry 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 errors) { + if (fonts == null) { + return; + } + const Set fontWeights = const { + 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 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 diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 7fff92e8e47..62a11563c1d 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,7 +12,6 @@ dependencies: analyzer: 0.35.1 archive: 2.0.8 args: 1.5.1 - cli_util: 0.1.3+2 completion: 0.2.1+1 coverage: 0.12.4 crypto: 2.0.6 @@ -20,7 +19,6 @@ dependencies: http: 0.12.0+1 intl: 0.15.7 json_rpc_2: 2.0.9 - json_schema: 1.0.10 linter: 0.1.82 meta: 1.1.6 multicast_dns: 0.1.0+1 @@ -64,7 +62,6 @@ dependencies: charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" csslib: 0.14.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - dart2_constant: 1.0.2+dart2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fixnum: 0.10.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" front_end: 0.1.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 1.1.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -117,4 +114,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 6362 +# PUBSPEC CHECKSUM: 9759 diff --git a/packages/flutter_tools/schema/pubspec_yaml.json b/packages/flutter_tools/schema/pubspec_yaml.json index 0a4b56e97db..632becee53b 100644 --- a/packages/flutter_tools/schema/pubspec_yaml.json +++ b/packages/flutter_tools/schema/pubspec_yaml.json @@ -1,5 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", + "$comment": "This should be kept in sync with the validator in packages/flutter_tools/lib/src/flutter_manifest.dart", "title": "pubspec.yaml", "type": "object", "additionalProperties": true,