Chromium Code Reviews| Index: sdk/lib/_internal/pub/lib/src/pubspec.dart |
| diff --git a/sdk/lib/_internal/pub/lib/src/pubspec.dart b/sdk/lib/_internal/pub/lib/src/pubspec.dart |
| index e01cab1583142c5776895616e536a151fba62243..2100cc60171a05c8b26f29a25c3f68ed45d55f07 100644 |
| --- a/sdk/lib/_internal/pub/lib/src/pubspec.dart |
| +++ b/sdk/lib/_internal/pub/lib/src/pubspec.dart |
| @@ -74,6 +74,13 @@ class Pubspec { |
| bool get isEmpty => |
| name == null && version == Version.none && dependencies.isEmpty; |
| + /// Returns a Pubspec object for an already-parsed map representing its |
| + /// contents. |
| + /// |
| + /// This will validate that [contents] is a valid pubspec. |
| + factory Pubspec.fromMap(Map contents, SourceRegistry sources) => |
|
Bob Nystrom
2013/06/04 20:21:41
"map" seems weird to me here. Maybe "Json"?
nweiz
2013/06/04 21:04:17
That's confusing, because it's not parsing JSON. T
|
| + _parseMap(null, contents, sources); |
| + |
| // TODO(rnystrom): Instead of allowing a null argument here, split this up |
| // into load(), parse(), and _parse() like LockFile does. |
| /// Parses the pubspec stored at [filePath] whose text is [contents]. If the |
| @@ -82,9 +89,6 @@ class Pubspec { |
| /// file system. |
| factory Pubspec.parse(String filePath, String contents, |
| SourceRegistry sources) { |
| - var name = null; |
| - var version = Version.none; |
| - |
| if (contents.trim() == '') return new Pubspec.empty(); |
| var parsedPubspec = loadYaml(contents); |
| @@ -94,141 +98,147 @@ class Pubspec { |
| throw new FormatException('The pubspec must be a YAML mapping.'); |
| } |
| - if (parsedPubspec.containsKey('name')) { |
| - name = parsedPubspec['name']; |
| - if (name is! String) { |
| - throw new FormatException( |
| - 'The pubspec "name" field should be a string, but was "$name".'); |
| - } |
| - } |
| + return _parseMap(filePath, parsedPubspec, sources); |
| + } |
| +} |
| - if (parsedPubspec.containsKey('version')) { |
| - version = new Version.parse(parsedPubspec['version']); |
| - } |
| +/// Evaluates whether the given [url] for [field] is valid. |
| +/// |
| +/// Throws [FormatException] on an invalid url. |
| +void _validateFieldUrl(url, String field) { |
| + if (url is! String) { |
| + throw new FormatException( |
| + 'The "$field" field should be a string, but was "$url".'); |
| + } |
| - var dependencies = _parseDependencies(filePath, sources, |
| - parsedPubspec['dependencies']); |
| - |
| - var devDependencies = _parseDependencies(filePath, sources, |
| - parsedPubspec['dev_dependencies']); |
| - |
| - // Make sure the same package doesn't appear as both a regular and dev |
| - // dependency. |
| - var dependencyNames = dependencies.map((dep) => dep.name).toSet(); |
| - var collisions = dependencyNames.intersection( |
| - devDependencies.map((dep) => dep.name).toSet()); |
| - |
| - if (!collisions.isEmpty) { |
| - var packageNames; |
| - if (collisions.length == 1) { |
| - packageNames = 'Package "${collisions.first}"'; |
| - } else { |
| - var names = collisions.toList(); |
| - names.sort(); |
| - var buffer = new StringBuffer(); |
| - buffer.write("Packages "); |
| - for (var i = 0; i < names.length; i++) { |
| - buffer.write('"'); |
| - buffer.write(names[i]); |
| - buffer.write('"'); |
| - if (i == names.length - 2) { |
| - buffer.write(", "); |
| - } else if (i == names.length - 1) { |
| - buffer.write(", and "); |
| - } |
| - } |
| + var goodScheme = new RegExp(r'^https?:'); |
| + if (!goodScheme.hasMatch(url)) { |
| + throw new FormatException( |
| + 'The "$field" field should be an "http:" or "https:" URL, but ' |
| + 'was "$url".'); |
| + } |
| +} |
| - packageNames = buffer.toString(); |
| - } |
| +Pubspec _parseMap(String filePath, Map map, SourceRegistry sources) { |
| + var name = null; |
| + var version = Version.none; |
| + |
| + if (map.containsKey('name')) { |
| + name = map['name']; |
| + if (name is! String) { |
| throw new FormatException( |
| - '$packageNames cannot appear in both "dependencies" and ' |
| - '"dev_dependencies".'); |
| + 'The pubspec "name" field should be a string, but was "$name".'); |
| } |
| + } |
| - var environmentYaml = parsedPubspec['environment']; |
| - var sdkConstraint = VersionConstraint.any; |
| - if (environmentYaml != null) { |
| - if (environmentYaml is! Map) { |
| - throw new FormatException( |
| - 'The pubspec "environment" field should be a map, but was ' |
| - '"$environmentYaml".'); |
| - } |
| + if (map.containsKey('version')) { |
| + version = new Version.parse(map['version']); |
| + } |
| - var sdkYaml = environmentYaml['sdk']; |
| - if (sdkYaml is! String) { |
| - throw new FormatException( |
| - 'The "sdk" field of "environment" should be a string, but was ' |
| - '"$sdkYaml".'); |
| + var dependencies = _parseDependencies(filePath, sources, |
| + map['dependencies']); |
| + |
| + var devDependencies = _parseDependencies(filePath, sources, |
| + map['dev_dependencies']); |
| + |
| + // Make sure the same package doesn't appear as both a regular and dev |
| + // dependency. |
| + var dependencyNames = dependencies.map((dep) => dep.name).toSet(); |
| + var collisions = dependencyNames.intersection( |
| + devDependencies.map((dep) => dep.name).toSet()); |
| + |
| + if (!collisions.isEmpty) { |
| + var packageNames; |
| + if (collisions.length == 1) { |
| + packageNames = 'Package "${collisions.first}"'; |
| + } else { |
| + var names = collisions.toList(); |
| + names.sort(); |
| + var buffer = new StringBuffer(); |
| + buffer.write("Packages "); |
| + for (var i = 0; i < names.length; i++) { |
| + buffer.write('"'); |
| + buffer.write(names[i]); |
| + buffer.write('"'); |
| + if (i == names.length - 2) { |
| + buffer.write(", "); |
| + } else if (i == names.length - 1) { |
| + buffer.write(", and "); |
| + } |
| } |
| - sdkConstraint = new VersionConstraint.parse(sdkYaml); |
| - } |
| - var environment = new PubspecEnvironment(sdkConstraint); |
| - |
| - // Even though the pub app itself doesn't use these fields, we validate |
| - // them here so that users find errors early before they try to upload to |
| - // the server: |
| - // TODO(rnystrom): We should split this validation into separate layers: |
| - // 1. Stuff that is required in any pubspec to perform any command. Things |
| - // like "must have a name". That should go here. |
| - // 2. Stuff that is required to upload a package. Things like "homepage |
| - // must use a valid scheme". That should go elsewhere. pub upload should |
| - // call it, and we should provide a separate command to show the user, |
| - // and also expose it to the editor in some way. |
| - |
| - if (parsedPubspec.containsKey('homepage')) { |
| - _validateFieldUrl(parsedPubspec['homepage'], 'homepage'); |
| - } |
| - if (parsedPubspec.containsKey('documentation')) { |
| - _validateFieldUrl(parsedPubspec['documentation'], 'documentation'); |
| + packageNames = buffer.toString(); |
| } |
| + throw new FormatException( |
| + '$packageNames cannot appear in both "dependencies" and ' |
| + '"dev_dependencies".'); |
| + } |
| - if (parsedPubspec.containsKey('author') && |
| - parsedPubspec['author'] is! String) { |
| + var environmentYaml = map['environment']; |
| + var sdkConstraint = VersionConstraint.any; |
| + if (environmentYaml != null) { |
| + if (environmentYaml is! Map) { |
| throw new FormatException( |
| - 'The "author" field should be a string, but was ' |
| - '${parsedPubspec["author"]}.'); |
| + 'The pubspec "environment" field should be a map, but was ' |
| + '"$environmentYaml".'); |
| } |
| - if (parsedPubspec.containsKey('authors')) { |
| - var authors = parsedPubspec['authors']; |
| - if (authors is List) { |
| - // All of the elements must be strings. |
| - if (!authors.every((author) => author is String)) { |
| - throw new FormatException('The "authors" field should be a string ' |
| - 'or a list of strings, but was "$authors".'); |
| - } |
| - } else if (authors is! String) { |
| - throw new FormatException('The pubspec "authors" field should be a ' |
| - 'string or a list of strings, but was "$authors".'); |
| - } |
| - |
| - if (parsedPubspec.containsKey('author')) { |
| - throw new FormatException('A pubspec should not have both an "author" ' |
| - 'and an "authors" field.'); |
| - } |
| + var sdkYaml = environmentYaml['sdk']; |
| + if (sdkYaml is! String) { |
| + throw new FormatException( |
| + 'The "sdk" field of "environment" should be a string, but was ' |
| + '"$sdkYaml".'); |
| } |
| - return new Pubspec(name, version, dependencies, devDependencies, |
| - environment, parsedPubspec); |
| + sdkConstraint = new VersionConstraint.parse(sdkYaml); |
| + } |
| + var environment = new PubspecEnvironment(sdkConstraint); |
| + |
| + // Even though the pub app itself doesn't use these fields, we validate |
| + // them here so that users find errors early before they try to upload to |
| + // the server: |
| + // TODO(rnystrom): We should split this validation into separate layers: |
| + // 1. Stuff that is required in any pubspec to perform any command. Things |
| + // like "must have a name". That should go here. |
| + // 2. Stuff that is required to upload a package. Things like "homepage |
| + // must use a valid scheme". That should go elsewhere. pub upload should |
| + // call it, and we should provide a separate command to show the user, |
| + // and also expose it to the editor in some way. |
| + |
| + if (map.containsKey('homepage')) { |
| + _validateFieldUrl(map['homepage'], 'homepage'); |
| + } |
| + if (map.containsKey('documentation')) { |
| + _validateFieldUrl(map['documentation'], 'documentation'); |
| } |
| -} |
| -/// Evaluates whether the given [url] for [field] is valid. |
| -/// |
| -/// Throws [FormatException] on an invalid url. |
| -void _validateFieldUrl(url, String field) { |
| - if (url is! String) { |
| + if (map.containsKey('author') && map['author'] is! String) { |
| throw new FormatException( |
| - 'The "$field" field should be a string, but was "$url".'); |
| + 'The "author" field should be a string, but was ' |
| + '${map["author"]}.'); |
| } |
| - var goodScheme = new RegExp(r'^https?:'); |
| - if (!goodScheme.hasMatch(url)) { |
| - throw new FormatException( |
| - 'The "$field" field should be an "http:" or "https:" URL, but ' |
| - 'was "$url".'); |
| + if (map.containsKey('authors')) { |
| + var authors = map['authors']; |
| + if (authors is List) { |
| + // All of the elements must be strings. |
| + if (!authors.every((author) => author is String)) { |
| + throw new FormatException('The "authors" field should be a string ' |
| + 'or a list of strings, but was "$authors".'); |
| + } |
| + } else if (authors is! String) { |
| + throw new FormatException('The pubspec "authors" field should be a ' |
| + 'string or a list of strings, but was "$authors".'); |
| + } |
| + |
| + if (map.containsKey('author')) { |
| + throw new FormatException('A pubspec should not have both an "author" ' |
| + 'and an "authors" field.'); |
| + } |
| } |
| + |
| + return new Pubspec(name, version, dependencies, devDependencies, |
| + environment, map); |
| } |
| List<PackageDep> _parseDependencies(String pubspecPath, SourceRegistry sources, |