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) => |
+ _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, |