| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library pub.pubspec; | |
| 6 | |
| 7 import 'package:path/path.dart' as path; | |
| 8 import 'package:pub_semver/pub_semver.dart'; | |
| 9 import 'package:source_span/source_span.dart'; | |
| 10 import 'package:yaml/yaml.dart'; | |
| 11 | |
| 12 import 'barback/transformer_config.dart'; | |
| 13 import 'exceptions.dart'; | |
| 14 import 'io.dart'; | |
| 15 import 'package.dart'; | |
| 16 import 'source_registry.dart'; | |
| 17 import 'utils.dart'; | |
| 18 | |
| 19 /// The parsed contents of a pubspec file. | |
| 20 /// | |
| 21 /// The fields of a pubspec are, for the most part, validated when they're first | |
| 22 /// accessed. This allows a partially-invalid pubspec to be used if only the | |
| 23 /// valid portions are relevant. To get a list of all errors in the pubspec, use | |
| 24 /// [allErrors]. | |
| 25 class Pubspec { | |
| 26 // If a new lazily-initialized field is added to this class and the | |
| 27 // initialization can throw a [PubspecException], that error should also be | |
| 28 // exposed through [allErrors]. | |
| 29 | |
| 30 /// The registry of sources to use when parsing [dependencies] and | |
| 31 /// [devDependencies]. | |
| 32 /// | |
| 33 /// This will be null if this was created using [new Pubspec] or [new | |
| 34 /// Pubspec.empty]. | |
| 35 final SourceRegistry _sources; | |
| 36 | |
| 37 /// The location from which the pubspec was loaded. | |
| 38 /// | |
| 39 /// This can be null if the pubspec was created in-memory or if its location | |
| 40 /// is unknown. | |
| 41 Uri get _location => fields.span.sourceUrl; | |
| 42 | |
| 43 /// All pubspec fields. | |
| 44 /// | |
| 45 /// This includes the fields from which other properties are derived. | |
| 46 final YamlMap fields; | |
| 47 | |
| 48 /// The package's name. | |
| 49 String get name { | |
| 50 if (_name != null) return _name; | |
| 51 | |
| 52 var name = fields['name']; | |
| 53 if (name == null) { | |
| 54 throw new PubspecException( | |
| 55 'Missing the required "name" field.', | |
| 56 fields.span); | |
| 57 } else if (name is! String) { | |
| 58 throw new PubspecException( | |
| 59 '"name" field must be a string.', | |
| 60 fields.nodes['name'].span); | |
| 61 } | |
| 62 | |
| 63 _name = name; | |
| 64 return _name; | |
| 65 } | |
| 66 String _name; | |
| 67 | |
| 68 /// The package's version. | |
| 69 Version get version { | |
| 70 if (_version != null) return _version; | |
| 71 | |
| 72 var version = fields['version']; | |
| 73 if (version == null) { | |
| 74 _version = Version.none; | |
| 75 return _version; | |
| 76 } | |
| 77 | |
| 78 var span = fields.nodes['version'].span; | |
| 79 if (version is num) { | |
| 80 var fixed = '$version.0'; | |
| 81 if (version is int) { | |
| 82 fixed = '$fixed.0'; | |
| 83 } | |
| 84 _error( | |
| 85 '"version" field must have three numeric components: major, ' | |
| 86 'minor, and patch. Instead of "$version", consider "$fixed".', | |
| 87 span); | |
| 88 } | |
| 89 if (version is! String) { | |
| 90 _error('"version" field must be a string.', span); | |
| 91 } | |
| 92 | |
| 93 _version = | |
| 94 _wrapFormatException('version number', span, () => new Version.parse(ver
sion)); | |
| 95 return _version; | |
| 96 } | |
| 97 Version _version; | |
| 98 | |
| 99 /// The additional packages this package depends on. | |
| 100 List<PackageDep> get dependencies { | |
| 101 if (_dependencies != null) return _dependencies; | |
| 102 _dependencies = _parseDependencies('dependencies'); | |
| 103 _checkDependencyOverlap(_dependencies, _devDependencies); | |
| 104 return _dependencies; | |
| 105 } | |
| 106 List<PackageDep> _dependencies; | |
| 107 | |
| 108 /// The packages this package depends on when it is the root package. | |
| 109 List<PackageDep> get devDependencies { | |
| 110 if (_devDependencies != null) return _devDependencies; | |
| 111 _devDependencies = _parseDependencies('dev_dependencies'); | |
| 112 _checkDependencyOverlap(_dependencies, _devDependencies); | |
| 113 return _devDependencies; | |
| 114 } | |
| 115 List<PackageDep> _devDependencies; | |
| 116 | |
| 117 /// The dependency constraints that this package overrides when it is the | |
| 118 /// root package. | |
| 119 /// | |
| 120 /// Dependencies here will replace any dependency on a package with the same | |
| 121 /// name anywhere in the dependency graph. | |
| 122 List<PackageDep> get dependencyOverrides { | |
| 123 if (_dependencyOverrides != null) return _dependencyOverrides; | |
| 124 _dependencyOverrides = _parseDependencies('dependency_overrides'); | |
| 125 return _dependencyOverrides; | |
| 126 } | |
| 127 List<PackageDep> _dependencyOverrides; | |
| 128 | |
| 129 /// The configurations of the transformers to use for this package. | |
| 130 List<Set<TransformerConfig>> get transformers { | |
| 131 if (_transformers != null) return _transformers; | |
| 132 | |
| 133 var transformers = fields['transformers']; | |
| 134 if (transformers == null) { | |
| 135 _transformers = []; | |
| 136 return _transformers; | |
| 137 } | |
| 138 | |
| 139 if (transformers is! List) { | |
| 140 _error( | |
| 141 '"transformers" field must be a list.', | |
| 142 fields.nodes['transformers'].span); | |
| 143 } | |
| 144 | |
| 145 var i = 0; | |
| 146 _transformers = transformers.nodes.map((phase) { | |
| 147 var phaseNodes = phase is YamlList ? phase.nodes : [phase]; | |
| 148 return phaseNodes.map((transformerNode) { | |
| 149 var transformer = transformerNode.value; | |
| 150 if (transformer is! String && transformer is! Map) { | |
| 151 _error( | |
| 152 'A transformer must be a string or map.', | |
| 153 transformerNode.span); | |
| 154 } | |
| 155 | |
| 156 var libraryNode; | |
| 157 var configurationNode; | |
| 158 if (transformer is String) { | |
| 159 libraryNode = transformerNode; | |
| 160 } else { | |
| 161 if (transformer.length != 1) { | |
| 162 _error( | |
| 163 'A transformer map must have a single key: the transformer ' 'id
entifier.', | |
| 164 transformerNode.span); | |
| 165 } else if (transformer.keys.single is! String) { | |
| 166 _error( | |
| 167 'A transformer identifier must be a string.', | |
| 168 transformer.nodes.keys.single.span); | |
| 169 } | |
| 170 | |
| 171 libraryNode = transformer.nodes.keys.single; | |
| 172 configurationNode = transformer.nodes.values.single; | |
| 173 if (configurationNode is! YamlMap) { | |
| 174 _error( | |
| 175 "A transformer's configuration must be a map.", | |
| 176 configurationNode.span); | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 var config = _wrapSpanFormatException('transformer config', () { | |
| 181 return new TransformerConfig.parse( | |
| 182 libraryNode.value, | |
| 183 libraryNode.span, | |
| 184 configurationNode); | |
| 185 }); | |
| 186 | |
| 187 var package = config.id.package; | |
| 188 if (package != name && | |
| 189 !config.id.isBuiltInTransformer && | |
| 190 !dependencies.any((ref) => ref.name == package) && | |
| 191 !devDependencies.any((ref) => ref.name == package) && | |
| 192 !dependencyOverrides.any((ref) => ref.name == package)) { | |
| 193 _error('"$package" is not a dependency.', libraryNode.span); | |
| 194 } | |
| 195 | |
| 196 return config; | |
| 197 }).toSet(); | |
| 198 }).toList(); | |
| 199 | |
| 200 return _transformers; | |
| 201 } | |
| 202 List<Set<TransformerConfig>> _transformers; | |
| 203 | |
| 204 /// The environment-related metadata. | |
| 205 PubspecEnvironment get environment { | |
| 206 if (_environment != null) return _environment; | |
| 207 | |
| 208 var yaml = fields['environment']; | |
| 209 if (yaml == null) { | |
| 210 _environment = new PubspecEnvironment(VersionConstraint.any); | |
| 211 return _environment; | |
| 212 } | |
| 213 | |
| 214 if (yaml is! Map) { | |
| 215 _error( | |
| 216 '"environment" field must be a map.', | |
| 217 fields.nodes['environment'].span); | |
| 218 } | |
| 219 | |
| 220 _environment = | |
| 221 new PubspecEnvironment(_parseVersionConstraint(yaml.nodes['sdk'])); | |
| 222 return _environment; | |
| 223 } | |
| 224 PubspecEnvironment _environment; | |
| 225 | |
| 226 /// The URL of the server that the package should default to being published | |
| 227 /// to, "none" if the package should not be published, or `null` if it should | |
| 228 /// be published to the default server. | |
| 229 /// | |
| 230 /// If this does return a URL string, it will be a valid parseable URL. | |
| 231 String get publishTo { | |
| 232 if (_parsedPublishTo) return _publishTo; | |
| 233 | |
| 234 var publishTo = fields['publish_to']; | |
| 235 if (publishTo != null) { | |
| 236 var span = fields.nodes['publish_to'].span; | |
| 237 | |
| 238 if (publishTo is! String) { | |
| 239 _error('"publish_to" field must be a string.', span); | |
| 240 } | |
| 241 | |
| 242 // It must be "none" or a valid URL. | |
| 243 if (publishTo != "none") { | |
| 244 _wrapFormatException( | |
| 245 '"publish_to" field', | |
| 246 span, | |
| 247 () => Uri.parse(publishTo)); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 _parsedPublishTo = true; | |
| 252 _publishTo = publishTo; | |
| 253 return _publishTo; | |
| 254 } | |
| 255 bool _parsedPublishTo = false; | |
| 256 String _publishTo; | |
| 257 | |
| 258 /// The executables that should be placed on the user's PATH when this | |
| 259 /// package is globally activated. | |
| 260 /// | |
| 261 /// It is a map of strings to string. Each key is the name of the command | |
| 262 /// that will be placed on the user's PATH. The value is the name of the | |
| 263 /// .dart script (without extension) in the package's `bin` directory that | |
| 264 /// should be run for that command. Both key and value must be "simple" | |
| 265 /// strings: alphanumerics, underscores and hypens only. If a value is | |
| 266 /// omitted, it is inferred to use the same name as the key. | |
| 267 Map<String, String> get executables { | |
| 268 if (_executables != null) return _executables; | |
| 269 | |
| 270 _executables = {}; | |
| 271 var yaml = fields['executables']; | |
| 272 if (yaml == null) return _executables; | |
| 273 | |
| 274 if (yaml is! Map) { | |
| 275 _error( | |
| 276 '"executables" field must be a map.', | |
| 277 fields.nodes['executables'].span); | |
| 278 } | |
| 279 | |
| 280 yaml.nodes.forEach((key, value) { | |
| 281 // Don't allow path separators or other stuff meaningful to the shell. | |
| 282 validateName(name, description) { | |
| 283 } | |
| 284 | |
| 285 if (key.value is! String) { | |
| 286 _error('"executables" keys must be strings.', key.span); | |
| 287 } | |
| 288 | |
| 289 final keyPattern = new RegExp(r"^[a-zA-Z0-9_-]+$"); | |
| 290 if (!keyPattern.hasMatch(key.value)) { | |
| 291 _error( | |
| 292 '"executables" keys may only contain letters, ' | |
| 293 'numbers, hyphens and underscores.', | |
| 294 key.span); | |
| 295 } | |
| 296 | |
| 297 if (value.value == null) { | |
| 298 value = key; | |
| 299 } else if (value.value is! String) { | |
| 300 _error('"executables" values must be strings or null.', value.span); | |
| 301 } | |
| 302 | |
| 303 final valuePattern = new RegExp(r"[/\\]"); | |
| 304 if (valuePattern.hasMatch(value.value)) { | |
| 305 _error( | |
| 306 '"executables" values may not contain path separators.', | |
| 307 value.span); | |
| 308 } | |
| 309 | |
| 310 _executables[key.value] = value.value; | |
| 311 }); | |
| 312 | |
| 313 return _executables; | |
| 314 } | |
| 315 Map<String, String> _executables; | |
| 316 | |
| 317 /// Whether the package is private and cannot be published. | |
| 318 /// | |
| 319 /// This is specified in the pubspec by setting "publish_to" to "none". | |
| 320 bool get isPrivate => publishTo == "none"; | |
| 321 | |
| 322 /// Whether or not the pubspec has no contents. | |
| 323 bool get isEmpty => | |
| 324 name == null && version == Version.none && dependencies.isEmpty; | |
| 325 | |
| 326 /// Loads the pubspec for a package located in [packageDir]. | |
| 327 /// | |
| 328 /// If [expectedName] is passed and the pubspec doesn't have a matching name | |
| 329 /// field, this will throw a [PubspecError]. | |
| 330 factory Pubspec.load(String packageDir, SourceRegistry sources, | |
| 331 {String expectedName}) { | |
| 332 var pubspecPath = path.join(packageDir, 'pubspec.yaml'); | |
| 333 var pubspecUri = path.toUri(pubspecPath); | |
| 334 if (!fileExists(pubspecPath)) { | |
| 335 throw new FileException( | |
| 336 'Could not find a file named "pubspec.yaml" in "$packageDir".', | |
| 337 pubspecPath); | |
| 338 } | |
| 339 | |
| 340 return new Pubspec.parse( | |
| 341 readTextFile(pubspecPath), | |
| 342 sources, | |
| 343 expectedName: expectedName, | |
| 344 location: pubspecUri); | |
| 345 } | |
| 346 | |
| 347 Pubspec(this._name, {Version version, Iterable<PackageDep> dependencies, | |
| 348 Iterable<PackageDep> devDependencies, Iterable<PackageDep> dependencyOverr
ides, | |
| 349 VersionConstraint sdkConstraint, | |
| 350 Iterable<Iterable<TransformerConfig>> transformers, Map fields, | |
| 351 SourceRegistry sources}) | |
| 352 : _version = version, | |
| 353 _dependencies = dependencies == null ? null : dependencies.toList(), | |
| 354 _devDependencies = devDependencies == null ? | |
| 355 null : | |
| 356 devDependencies.toList(), | |
| 357 _dependencyOverrides = dependencyOverrides == null ? | |
| 358 null : | |
| 359 dependencyOverrides.toList(), | |
| 360 _environment = new PubspecEnvironment(sdkConstraint), | |
| 361 _transformers = transformers == null ? | |
| 362 [] : | |
| 363 transformers.map((phase) => phase.toSet()).toList(), | |
| 364 fields = fields == null ? new YamlMap() : new YamlMap.wrap(fields), | |
| 365 _sources = sources; | |
| 366 | |
| 367 Pubspec.empty() | |
| 368 : _sources = null, | |
| 369 _name = null, | |
| 370 _version = Version.none, | |
| 371 _dependencies = <PackageDep>[], | |
| 372 _devDependencies = <PackageDep>[], | |
| 373 _environment = new PubspecEnvironment(), | |
| 374 _transformers = <Set<TransformerConfig>>[], | |
| 375 fields = new YamlMap(); | |
| 376 | |
| 377 /// Returns a Pubspec object for an already-parsed map representing its | |
| 378 /// contents. | |
| 379 /// | |
| 380 /// If [expectedName] is passed and the pubspec doesn't have a matching name | |
| 381 /// field, this will throw a [PubspecError]. | |
| 382 /// | |
| 383 /// [location] is the location from which this pubspec was loaded. | |
| 384 Pubspec.fromMap(Map fields, this._sources, {String expectedName, | |
| 385 Uri location}) | |
| 386 : fields = fields is YamlMap ? | |
| 387 fields : | |
| 388 new YamlMap.wrap(fields, sourceUrl: location) { | |
| 389 // If [expectedName] is passed, ensure that the actual 'name' field exists | |
| 390 // and matches the expectation. | |
| 391 if (expectedName == null) return; | |
| 392 if (name == expectedName) return; | |
| 393 | |
| 394 throw new PubspecException( | |
| 395 '"name" field doesn\'t match expected name ' '"$expectedName".', | |
| 396 this.fields.nodes["name"].span); | |
| 397 } | |
| 398 | |
| 399 /// Parses the pubspec stored at [filePath] whose text is [contents]. | |
| 400 /// | |
| 401 /// If the pubspec doesn't define a version for itself, it defaults to | |
| 402 /// [Version.none]. | |
| 403 factory Pubspec.parse(String contents, SourceRegistry sources, | |
| 404 {String expectedName, Uri location}) { | |
| 405 var pubspecNode = loadYamlNode(contents, sourceUrl: location); | |
| 406 if (pubspecNode is YamlScalar && pubspecNode.value == null) { | |
| 407 pubspecNode = new YamlMap(sourceUrl: location); | |
| 408 } else if (pubspecNode is! YamlMap) { | |
| 409 throw new PubspecException( | |
| 410 'The pubspec must be a YAML mapping.', | |
| 411 pubspecNode.span); | |
| 412 } | |
| 413 | |
| 414 return new Pubspec.fromMap( | |
| 415 pubspecNode, | |
| 416 sources, | |
| 417 expectedName: expectedName, | |
| 418 location: location); | |
| 419 } | |
| 420 | |
| 421 /// Returns a list of most errors in this pubspec. | |
| 422 /// | |
| 423 /// This will return at most one error for each field. | |
| 424 List<PubspecException> get allErrors { | |
| 425 var errors = <PubspecException>[]; | |
| 426 _getError(fn()) { | |
| 427 try { | |
| 428 fn(); | |
| 429 } on PubspecException catch (e) { | |
| 430 errors.add(e); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 _getError(() => this.name); | |
| 435 _getError(() => this.version); | |
| 436 _getError(() => this.dependencies); | |
| 437 _getError(() => this.devDependencies); | |
| 438 _getError(() => this.transformers); | |
| 439 _getError(() => this.environment); | |
| 440 _getError(() => this.publishTo); | |
| 441 return errors; | |
| 442 } | |
| 443 | |
| 444 /// Parses the dependency field named [field], and returns the corresponding | |
| 445 /// list of dependencies. | |
| 446 List<PackageDep> _parseDependencies(String field) { | |
| 447 var dependencies = <PackageDep>[]; | |
| 448 | |
| 449 var yaml = fields[field]; | |
| 450 // Allow an empty dependencies key. | |
| 451 if (yaml == null) return dependencies; | |
| 452 | |
| 453 if (yaml is! Map) { | |
| 454 _error('"$field" field must be a map.', fields.nodes[field].span); | |
| 455 } | |
| 456 | |
| 457 var nonStringNode = | |
| 458 yaml.nodes.keys.firstWhere((e) => e.value is! String, orElse: () => null
); | |
| 459 if (nonStringNode != null) { | |
| 460 _error('A dependency name must be a string.', nonStringNode.span); | |
| 461 } | |
| 462 | |
| 463 yaml.nodes.forEach((nameNode, specNode) { | |
| 464 var name = nameNode.value; | |
| 465 var spec = specNode.value; | |
| 466 if (fields['name'] != null && name == this.name) { | |
| 467 _error('A package may not list itself as a dependency.', nameNode.span); | |
| 468 } | |
| 469 | |
| 470 var descriptionNode; | |
| 471 var sourceName; | |
| 472 | |
| 473 var versionConstraint = new VersionRange(); | |
| 474 if (spec == null) { | |
| 475 descriptionNode = nameNode; | |
| 476 sourceName = _sources.defaultSource.name; | |
| 477 } else if (spec is String) { | |
| 478 descriptionNode = nameNode; | |
| 479 sourceName = _sources.defaultSource.name; | |
| 480 versionConstraint = _parseVersionConstraint(specNode); | |
| 481 } else if (spec is Map) { | |
| 482 // Don't write to the immutable YAML map. | |
| 483 spec = new Map.from(spec); | |
| 484 | |
| 485 if (spec.containsKey('version')) { | |
| 486 spec.remove('version'); | |
| 487 versionConstraint = | |
| 488 _parseVersionConstraint(specNode.nodes['version']); | |
| 489 } | |
| 490 | |
| 491 var sourceNames = spec.keys.toList(); | |
| 492 if (sourceNames.length > 1) { | |
| 493 _error('A dependency may only have one source.', specNode.span); | |
| 494 } | |
| 495 | |
| 496 sourceName = sourceNames.single; | |
| 497 if (sourceName is! String) { | |
| 498 _error( | |
| 499 'A source name must be a string.', | |
| 500 specNode.nodes.keys.single.span); | |
| 501 } | |
| 502 | |
| 503 descriptionNode = specNode.nodes[sourceName]; | |
| 504 } else { | |
| 505 _error( | |
| 506 'A dependency specification must be a string or a mapping.', | |
| 507 specNode.span); | |
| 508 } | |
| 509 | |
| 510 // Let the source validate the description. | |
| 511 var description = | |
| 512 _wrapFormatException('description', descriptionNode.span, () { | |
| 513 var pubspecPath; | |
| 514 if (_location != null && _isFileUri(_location)) { | |
| 515 pubspecPath = path.fromUri(_location); | |
| 516 } | |
| 517 | |
| 518 return _sources[sourceName].parseDescription( | |
| 519 pubspecPath, | |
| 520 descriptionNode.value, | |
| 521 fromLockFile: false); | |
| 522 }); | |
| 523 | |
| 524 dependencies.add( | |
| 525 new PackageDep(name, sourceName, versionConstraint, description)); | |
| 526 }); | |
| 527 | |
| 528 return dependencies; | |
| 529 } | |
| 530 | |
| 531 /// Parses [node] to a [VersionConstraint]. | |
| 532 VersionConstraint _parseVersionConstraint(YamlNode node) { | |
| 533 if (node.value == null) return VersionConstraint.any; | |
| 534 if (node.value is! String) { | |
| 535 _error('A version constraint must be a string.', node.span); | |
| 536 } | |
| 537 | |
| 538 return _wrapFormatException( | |
| 539 'version constraint', | |
| 540 node.span, | |
| 541 () => new VersionConstraint.parse(node.value)); | |
| 542 } | |
| 543 | |
| 544 /// Makes sure the same package doesn't appear as both a regular and dev | |
| 545 /// dependency. | |
| 546 void _checkDependencyOverlap(List<PackageDep> dependencies, | |
| 547 List<PackageDep> devDependencies) { | |
| 548 if (dependencies == null) return; | |
| 549 if (devDependencies == null) return; | |
| 550 | |
| 551 var dependencyNames = dependencies.map((dep) => dep.name).toSet(); | |
| 552 var collisions = | |
| 553 dependencyNames.intersection(devDependencies.map((dep) => dep.name).toSe
t()); | |
| 554 if (collisions.isEmpty) return; | |
| 555 | |
| 556 var span = fields["dependencies"].nodes.keys.firstWhere( | |
| 557 (key) => collisions.contains(key.value)).span; | |
| 558 | |
| 559 // TODO(nweiz): associate source range info with PackageDeps and use it | |
| 560 // here. | |
| 561 _error( | |
| 562 '${pluralize('Package', collisions.length)} ' | |
| 563 '${toSentence(collisions.map((package) => '"$package"'))} cannot ' | |
| 564 'appear in both "dependencies" and "dev_dependencies".', | |
| 565 span); | |
| 566 } | |
| 567 | |
| 568 /// Runs [fn] and wraps any [FormatException] it throws in a | |
| 569 /// [PubspecException]. | |
| 570 /// | |
| 571 /// [description] should be a noun phrase that describes whatever's being | |
| 572 /// parsed or processed by [fn]. [span] should be the location of whatever's | |
| 573 /// being processed within the pubspec. | |
| 574 _wrapFormatException(String description, SourceSpan span, fn()) { | |
| 575 try { | |
| 576 return fn(); | |
| 577 } on FormatException catch (e) { | |
| 578 _error('Invalid $description: ${e.message}', span); | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 _wrapSpanFormatException(String description, fn()) { | |
| 583 try { | |
| 584 return fn(); | |
| 585 } on SourceSpanFormatException catch (e) { | |
| 586 _error('Invalid $description: ${e.message}', e.span); | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 /// Throws a [PubspecException] with the given message. | |
| 591 void _error(String message, SourceSpan span) { | |
| 592 var name; | |
| 593 try { | |
| 594 name = this.name; | |
| 595 } on PubspecException catch (_) { | |
| 596 // [name] is null. | |
| 597 } | |
| 598 | |
| 599 throw new PubspecException(message, span); | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 /// The environment-related metadata in the pubspec. | |
| 604 /// | |
| 605 /// Corresponds to the data under the "environment:" key in the pubspec. | |
| 606 class PubspecEnvironment { | |
| 607 /// The version constraint specifying which SDK versions this package works | |
| 608 /// with. | |
| 609 final VersionConstraint sdkVersion; | |
| 610 | |
| 611 PubspecEnvironment([VersionConstraint sdk]) | |
| 612 : sdkVersion = sdk != null ? sdk : VersionConstraint.any; | |
| 613 } | |
| 614 | |
| 615 /// An exception thrown when parsing a pubspec. | |
| 616 /// | |
| 617 /// These exceptions are often thrown lazily while accessing pubspec properties. | |
| 618 class PubspecException extends SourceSpanFormatException implements | |
| 619 ApplicationException { | |
| 620 PubspecException(String message, SourceSpan span) | |
| 621 : super(message, span); | |
| 622 } | |
| 623 | |
| 624 /// Returns whether [uri] is a file URI. | |
| 625 /// | |
| 626 /// This is slightly more complicated than just checking if the scheme is | |
| 627 /// 'file', since relative URIs also refer to the filesystem on the VM. | |
| 628 bool _isFileUri(Uri uri) => uri.scheme == 'file' || uri.scheme == ''; | |
| OLD | NEW |