Index: sdk/lib/_internal/pub_generated/lib/src/pubspec.dart |
diff --git a/sdk/lib/_internal/pub_generated/lib/src/pubspec.dart b/sdk/lib/_internal/pub_generated/lib/src/pubspec.dart |
index d3dd3b82657ccdb0766c3c17c85521a460caa2eb..8fb27db00dbbb7295abc6f678224ee75cdacc425 100644 |
--- a/sdk/lib/_internal/pub_generated/lib/src/pubspec.dart |
+++ b/sdk/lib/_internal/pub_generated/lib/src/pubspec.dart |
@@ -1,20 +1,54 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
library pub.pubspec; |
+ |
import 'package:path/path.dart' as path; |
import 'package:pub_semver/pub_semver.dart'; |
import 'package:source_span/source_span.dart'; |
import 'package:yaml/yaml.dart'; |
+ |
import 'barback/transformer_config.dart'; |
import 'exceptions.dart'; |
import 'io.dart'; |
import 'package.dart'; |
import 'source_registry.dart'; |
import 'utils.dart'; |
+ |
+/// The parsed contents of a pubspec file. |
+/// |
+/// The fields of a pubspec are, for the most part, validated when they're first |
+/// accessed. This allows a partially-invalid pubspec to be used if only the |
+/// valid portions are relevant. To get a list of all errors in the pubspec, use |
+/// [allErrors]. |
class Pubspec { |
+ // If a new lazily-initialized field is added to this class and the |
+ // initialization can throw a [PubspecException], that error should also be |
+ // exposed through [allErrors]. |
+ |
+ /// The registry of sources to use when parsing [dependencies] and |
+ /// [devDependencies]. |
+ /// |
+ /// This will be null if this was created using [new Pubspec] or [new |
+ /// Pubspec.empty]. |
final SourceRegistry _sources; |
+ |
+ /// The location from which the pubspec was loaded. |
+ /// |
+ /// This can be null if the pubspec was created in-memory or if its location |
+ /// is unknown. |
Uri get _location => fields.span.sourceUrl; |
+ |
+ /// All pubspec fields. |
+ /// |
+ /// This includes the fields from which other properties are derived. |
final YamlMap fields; |
+ |
+ /// The package's name. |
String get name { |
if (_name != null) return _name; |
+ |
var name = fields['name']; |
if (name == null) { |
throw new PubspecException( |
@@ -25,34 +59,44 @@ class Pubspec { |
'"name" field must be a string.', |
fields.nodes['name'].span); |
} |
+ |
_name = name; |
return _name; |
} |
String _name; |
+ |
+ /// The package's version. |
Version get version { |
if (_version != null) return _version; |
+ |
var version = fields['version']; |
if (version == null) { |
_version = Version.none; |
return _version; |
} |
+ |
var span = fields.nodes['version'].span; |
if (version is num) { |
var fixed = '$version.0'; |
if (version is int) { |
fixed = '$fixed.0'; |
} |
- _error('"version" field must have three numeric components: major, ' |
- 'minor, and patch. Instead of "$version", consider "$fixed".', span); |
+ _error( |
+ '"version" field must have three numeric components: major, ' |
+ 'minor, and patch. Instead of "$version", consider "$fixed".', |
+ span); |
} |
if (version is! String) { |
_error('"version" field must be a string.', span); |
} |
+ |
_version = |
_wrapFormatException('version number', span, () => new Version.parse(version)); |
return _version; |
} |
Version _version; |
+ |
+ /// The additional packages this package depends on. |
List<PackageDep> get dependencies { |
if (_dependencies != null) return _dependencies; |
_dependencies = _parseDependencies('dependencies'); |
@@ -62,6 +106,8 @@ class Pubspec { |
return _dependencies; |
} |
List<PackageDep> _dependencies; |
+ |
+ /// The packages this package depends on when it is the root package. |
List<PackageDep> get devDependencies { |
if (_devDependencies != null) return _devDependencies; |
_devDependencies = _parseDependencies('dev_dependencies'); |
@@ -71,24 +117,35 @@ class Pubspec { |
return _devDependencies; |
} |
List<PackageDep> _devDependencies; |
+ |
+ /// The dependency constraints that this package overrides when it is the |
+ /// root package. |
+ /// |
+ /// Dependencies here will replace any dependency on a package with the same |
+ /// name anywhere in the dependency graph. |
List<PackageDep> get dependencyOverrides { |
if (_dependencyOverrides != null) return _dependencyOverrides; |
_dependencyOverrides = _parseDependencies('dependency_overrides'); |
return _dependencyOverrides; |
} |
List<PackageDep> _dependencyOverrides; |
+ |
+ /// The configurations of the transformers to use for this package. |
List<Set<TransformerConfig>> get transformers { |
if (_transformers != null) return _transformers; |
+ |
var transformers = fields['transformers']; |
if (transformers == null) { |
_transformers = []; |
return _transformers; |
} |
+ |
if (transformers is! List) { |
_error( |
'"transformers" field must be a list.', |
fields.nodes['transformers'].span); |
} |
+ |
var i = 0; |
_transformers = transformers.nodes.map((phase) { |
var phaseNodes = phase is YamlList ? phase.nodes : [phase]; |
@@ -99,6 +156,7 @@ class Pubspec { |
'A transformer must be a string or map.', |
transformerNode.span); |
} |
+ |
var libraryNode; |
var configurationNode; |
if (transformer is String) { |
@@ -113,6 +171,7 @@ class Pubspec { |
'A transformer identifier must be a string.', |
transformer.nodes.keys.single.span); |
} |
+ |
libraryNode = transformer.nodes.keys.single; |
configurationNode = transformer.nodes.values.single; |
if (configurationNode is! YamlMap) { |
@@ -121,12 +180,14 @@ class Pubspec { |
configurationNode.span); |
} |
} |
+ |
var config = _wrapSpanFormatException('transformer config', () { |
return new TransformerConfig.parse( |
libraryNode.value, |
libraryNode.span, |
configurationNode); |
}); |
+ |
var package = config.id.package; |
if (package != name && |
!config.id.isBuiltInTransformer && |
@@ -135,37 +196,54 @@ class Pubspec { |
!dependencyOverrides.any((ref) => ref.name == package)) { |
_error('"$package" is not a dependency.', libraryNode.span); |
} |
+ |
return config; |
}).toSet(); |
}).toList(); |
+ |
return _transformers; |
} |
List<Set<TransformerConfig>> _transformers; |
+ |
+ /// The environment-related metadata. |
PubspecEnvironment get environment { |
if (_environment != null) return _environment; |
+ |
var yaml = fields['environment']; |
if (yaml == null) { |
_environment = new PubspecEnvironment(VersionConstraint.any); |
return _environment; |
} |
+ |
if (yaml is! Map) { |
_error( |
'"environment" field must be a map.', |
fields.nodes['environment'].span); |
} |
+ |
_environment = |
new PubspecEnvironment(_parseVersionConstraint(yaml.nodes['sdk'])); |
return _environment; |
} |
PubspecEnvironment _environment; |
+ |
+ /// The URL of the server that the package should default to being published |
+ /// to, "none" if the package should not be published, or `null` if it should |
+ /// be published to the default server. |
+ /// |
+ /// If this does return a URL string, it will be a valid parseable URL. |
String get publishTo { |
if (_parsedPublishTo) return _publishTo; |
+ |
var publishTo = fields['publish_to']; |
if (publishTo != null) { |
var span = fields.nodes['publish_to'].span; |
+ |
if (publishTo is! String) { |
_error('"publish_to" field must be a string.', span); |
} |
+ |
+ // It must be "none" or a valid URL. |
if (publishTo != "none") { |
_wrapFormatException( |
'"publish_to" field', |
@@ -173,27 +251,45 @@ class Pubspec { |
() => Uri.parse(publishTo)); |
} |
} |
+ |
_parsedPublishTo = true; |
_publishTo = publishTo; |
return _publishTo; |
} |
bool _parsedPublishTo = false; |
String _publishTo; |
+ |
+ /// The executables that should be placed on the user's PATH when this |
+ /// package is globally activated. |
+ /// |
+ /// It is a map of strings to string. Each key is the name of the command |
+ /// that will be placed on the user's PATH. The value is the name of the |
+ /// .dart script (without extension) in the package's `bin` directory that |
+ /// should be run for that command. Both key and value must be "simple" |
+ /// strings: alphanumerics, underscores and hypens only. If a value is |
+ /// omitted, it is inferred to use the same name as the key. |
Map<String, String> get executables { |
if (_executables != null) return _executables; |
+ |
_executables = {}; |
var yaml = fields['executables']; |
if (yaml == null) return _executables; |
+ |
if (yaml is! Map) { |
_error( |
'"executables" field must be a map.', |
fields.nodes['executables'].span); |
} |
+ |
yaml.nodes.forEach((key, value) { |
- validateName(name, description) {} |
+ // Don't allow path separators or other stuff meaningful to the shell. |
+ validateName(name, description) { |
+ } |
+ |
if (key.value is! String) { |
_error('"executables" keys must be strings.', key.span); |
} |
+ |
final keyPattern = new RegExp(r"^[a-zA-Z0-9_-]+$"); |
if (!keyPattern.hasMatch(key.value)) { |
_error( |
@@ -201,25 +297,40 @@ class Pubspec { |
'numbers, hyphens and underscores.', |
key.span); |
} |
+ |
if (value.value == null) { |
value = key; |
} else if (value.value is! String) { |
_error('"executables" values must be strings or null.', value.span); |
} |
+ |
final valuePattern = new RegExp(r"[/\\]"); |
if (valuePattern.hasMatch(value.value)) { |
_error( |
'"executables" values may not contain path separators.', |
value.span); |
} |
+ |
_executables[key.value] = value.value; |
}); |
+ |
return _executables; |
} |
Map<String, String> _executables; |
+ |
+ /// Whether the package is private and cannot be published. |
+ /// |
+ /// This is specified in the pubspec by setting "publish_to" to "none". |
bool get isPrivate => publishTo == "none"; |
+ |
+ /// Whether or not the pubspec has no contents. |
bool get isEmpty => |
name == null && version == Version.none && dependencies.isEmpty; |
+ |
+ /// Loads the pubspec for a package located in [packageDir]. |
+ /// |
+ /// If [expectedName] is passed and the pubspec doesn't have a matching name |
+ /// field, this will throw a [PubspecError]. |
factory Pubspec.load(String packageDir, SourceRegistry sources, |
{String expectedName}) { |
var pubspecPath = path.join(packageDir, 'pubspec.yaml'); |
@@ -229,12 +340,14 @@ class Pubspec { |
'Could not find a file named "pubspec.yaml" in "$packageDir".', |
pubspecPath); |
} |
+ |
return new Pubspec.parse( |
readTextFile(pubspecPath), |
sources, |
expectedName: expectedName, |
location: pubspecUri); |
} |
+ |
Pubspec(this._name, {Version version, Iterable<PackageDep> dependencies, |
Iterable<PackageDep> devDependencies, Iterable<PackageDep> dependencyOverrides, |
VersionConstraint sdkConstraint, |
@@ -254,6 +367,7 @@ class Pubspec { |
transformers.map((phase) => phase.toSet()).toList(), |
fields = fields == null ? new YamlMap() : new YamlMap.wrap(fields), |
_sources = sources; |
+ |
Pubspec.empty() |
: _sources = null, |
_name = null, |
@@ -263,17 +377,33 @@ class Pubspec { |
_environment = new PubspecEnvironment(), |
_transformers = <Set<TransformerConfig>>[], |
fields = new YamlMap(); |
+ |
+ /// Returns a Pubspec object for an already-parsed map representing its |
+ /// contents. |
+ /// |
+ /// If [expectedName] is passed and the pubspec doesn't have a matching name |
+ /// field, this will throw a [PubspecError]. |
+ /// |
+ /// [location] is the location from which this pubspec was loaded. |
Pubspec.fromMap(Map fields, this._sources, {String expectedName, |
Uri location}) |
: fields = fields is YamlMap ? |
fields : |
new YamlMap.wrap(fields, sourceUrl: location) { |
+ // If [expectedName] is passed, ensure that the actual 'name' field exists |
+ // and matches the expectation. |
if (expectedName == null) return; |
if (name == expectedName) return; |
+ |
throw new PubspecException( |
'"name" field doesn\'t match expected name ' '"$expectedName".', |
this.fields.nodes["name"].span); |
} |
+ |
+ /// Parses the pubspec stored at [filePath] whose text is [contents]. |
+ /// |
+ /// If the pubspec doesn't define a version for itself, it defaults to |
+ /// [Version.none]. |
factory Pubspec.parse(String contents, SourceRegistry sources, |
{String expectedName, Uri location}) { |
var pubspecNode = loadYamlNode(contents, sourceUrl: location); |
@@ -284,12 +414,17 @@ class Pubspec { |
'The pubspec must be a YAML mapping.', |
pubspecNode.span); |
} |
+ |
return new Pubspec.fromMap( |
pubspecNode, |
sources, |
expectedName: expectedName, |
location: location); |
} |
+ |
+ /// Returns a list of most errors in this pubspec. |
+ /// |
+ /// This will return at most one error for each field. |
List<PubspecException> get allErrors { |
var errors = <PubspecException>[]; |
_getError(fn()) { |
@@ -299,6 +434,7 @@ class Pubspec { |
errors.add(e); |
} |
} |
+ |
_getError(() => this.name); |
_getError(() => this.version); |
_getError(() => this.dependencies); |
@@ -308,26 +444,36 @@ class Pubspec { |
_getError(() => this.publishTo); |
return errors; |
} |
+ |
+ /// Parses the dependency field named [field], and returns the corresponding |
+ /// list of dependencies. |
List<PackageDep> _parseDependencies(String field) { |
var dependencies = <PackageDep>[]; |
+ |
var yaml = fields[field]; |
+ // Allow an empty dependencies key. |
if (yaml == null) return dependencies; |
+ |
if (yaml is! Map) { |
_error('"$field" field must be a map.', fields.nodes[field].span); |
} |
+ |
var nonStringNode = |
yaml.nodes.keys.firstWhere((e) => e.value is! String, orElse: () => null); |
if (nonStringNode != null) { |
_error('A dependency name must be a string.', nonStringNode.span); |
} |
+ |
yaml.nodes.forEach((nameNode, specNode) { |
var name = nameNode.value; |
var spec = specNode.value; |
if (fields['name'] != null && name == this.name) { |
_error('A package may not list itself as a dependency.', nameNode.span); |
} |
+ |
var descriptionNode; |
var sourceName; |
+ |
var versionConstraint = new VersionRange(); |
if (spec == null) { |
descriptionNode = nameNode; |
@@ -337,68 +483,95 @@ class Pubspec { |
sourceName = _sources.defaultSource.name; |
versionConstraint = _parseVersionConstraint(specNode); |
} else if (spec is Map) { |
+ // Don't write to the immutable YAML map. |
spec = new Map.from(spec); |
+ |
if (spec.containsKey('version')) { |
spec.remove('version'); |
versionConstraint = |
_parseVersionConstraint(specNode.nodes['version']); |
} |
+ |
var sourceNames = spec.keys.toList(); |
if (sourceNames.length > 1) { |
_error('A dependency may only have one source.', specNode.span); |
} |
+ |
sourceName = sourceNames.single; |
if (sourceName is! String) { |
_error( |
'A source name must be a string.', |
specNode.nodes.keys.single.span); |
} |
+ |
descriptionNode = specNode.nodes[sourceName]; |
} else { |
_error( |
'A dependency specification must be a string or a mapping.', |
specNode.span); |
} |
+ |
+ // Let the source validate the description. |
var description = |
_wrapFormatException('description', descriptionNode.span, () { |
var pubspecPath; |
if (_location != null && _isFileUri(_location)) { |
pubspecPath = path.fromUri(_location); |
} |
+ |
return _sources[sourceName].parseDescription( |
pubspecPath, |
descriptionNode.value, |
fromLockFile: false); |
}); |
+ |
dependencies.add( |
new PackageDep(name, sourceName, versionConstraint, description)); |
}); |
+ |
return dependencies; |
} |
+ |
+ /// Parses [node] to a [VersionConstraint]. |
VersionConstraint _parseVersionConstraint(YamlNode node) { |
if (node.value == null) return VersionConstraint.any; |
if (node.value is! String) { |
_error('A version constraint must be a string.', node.span); |
} |
+ |
return _wrapFormatException( |
'version constraint', |
node.span, |
() => new VersionConstraint.parse(node.value)); |
} |
+ |
+ /// Makes sure the same package doesn't appear as both a regular and dev |
+ /// dependency. |
void _checkDependencyOverlap(List<PackageDep> dependencies, |
List<PackageDep> devDependencies) { |
var dependencyNames = dependencies.map((dep) => dep.name).toSet(); |
var collisions = |
dependencyNames.intersection(devDependencies.map((dep) => dep.name).toSet()); |
if (collisions.isEmpty) return; |
+ |
var span = fields["dependencies"].nodes.keys.firstWhere( |
(key) => collisions.contains(key.value)).span; |
+ |
+ // TODO(nweiz): associate source range info with PackageDeps and use it |
+ // here. |
_error( |
'${pluralize('Package', collisions.length)} ' |
'${toSentence(collisions.map((package) => '"$package"'))} cannot ' |
'appear in both "dependencies" and "dev_dependencies".', |
span); |
} |
+ |
+ /// Runs [fn] and wraps any [FormatException] it throws in a |
+ /// [PubspecException]. |
+ /// |
+ /// [description] should be a noun phrase that describes whatever's being |
+ /// parsed or processed by [fn]. [span] should be the location of whatever's |
+ /// being processed within the pubspec. |
_wrapFormatException(String description, SourceSpan span, fn()) { |
try { |
return fn(); |
@@ -406,6 +579,7 @@ class Pubspec { |
_error('Invalid $description: ${e.message}', span); |
} |
} |
+ |
_wrapSpanFormatException(String description, fn()) { |
try { |
return fn(); |
@@ -413,21 +587,43 @@ class Pubspec { |
_error('Invalid $description: ${e.message}', e.span); |
} |
} |
+ |
+ /// Throws a [PubspecException] with the given message. |
void _error(String message, SourceSpan span) { |
var name; |
try { |
name = this.name; |
- } on PubspecException catch (_) {} |
+ } on PubspecException catch (_) { |
+ // [name] is null. |
+ } |
+ |
throw new PubspecException(message, span); |
} |
} |
+ |
+/// The environment-related metadata in the pubspec. |
+/// |
+/// Corresponds to the data under the "environment:" key in the pubspec. |
class PubspecEnvironment { |
+ /// The version constraint specifying which SDK versions this package works |
+ /// with. |
final VersionConstraint sdkVersion; |
+ |
PubspecEnvironment([VersionConstraint sdk]) |
: sdkVersion = sdk != null ? sdk : VersionConstraint.any; |
} |
+ |
+/// An exception thrown when parsing a pubspec. |
+/// |
+/// These exceptions are often thrown lazily while accessing pubspec properties. |
class PubspecException extends SourceSpanFormatException implements |
ApplicationException { |
- PubspecException(String message, SourceSpan span) : super(message, span); |
+ PubspecException(String message, SourceSpan span) |
+ : super(message, span); |
} |
+ |
+/// Returns whether [uri] is a file URI. |
+/// |
+/// This is slightly more complicated than just checking if the scheme is |
+/// 'file', since relative URIs also refer to the filesystem on the VM. |
bool _isFileUri(Uri uri) => uri.scheme == 'file' || uri.scheme == ''; |