| // Copyright (c) 2018, 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. |
| |
| import 'package:checked_yaml/checked_yaml.dart'; |
| import 'package:json_annotation/json_annotation.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| |
| import 'dependency.dart'; |
| |
| part 'pubspec.g.dart'; |
| |
| @JsonSerializable() |
| class Pubspec { |
| // TODO: executables |
| |
| final String name; |
| |
| @JsonKey(fromJson: _versionFromString) |
| final Version version; |
| |
| final String description; |
| |
| /// This should be a URL pointing to the website for the package. |
| final String homepage; |
| |
| /// Specifies where to publish this package. |
| /// |
| /// Accepted values: `null`, `'none'` or an `http` or `https` URL. |
| /// |
| /// If not specified, the pub client defaults to `https://pub.dartlang.org`. |
| /// |
| /// [More information](https://www.dartlang.org/tools/pub/pubspec#publish_to). |
| final String publishTo; |
| |
| /// Optional field to specify the source code repository of the package. |
| /// Useful when a package has both a home page and a repository. |
| final Uri repository; |
| |
| /// Optional field to a web page where developers can report new issues or |
| /// view existing ones. |
| final Uri issueTracker; |
| |
| /// If there is exactly 1 value in [authors], returns it. |
| /// |
| /// If there are 0 or more than 1, returns `null`. |
| @Deprecated( |
| 'Here for completeness, but not recommended. Use `authors` instead.') |
| String get author { |
| if (authors.length == 1) { |
| return authors.single; |
| } |
| return null; |
| } |
| |
| final List<String> authors; |
| final String documentation; |
| |
| @JsonKey(fromJson: _environmentMap) |
| final Map<String, VersionConstraint> environment; |
| |
| @JsonKey(fromJson: parseDeps, nullable: false) |
| final Map<String, Dependency> dependencies; |
| |
| @JsonKey(fromJson: parseDeps, nullable: false) |
| final Map<String, Dependency> devDependencies; |
| |
| @JsonKey(fromJson: parseDeps, nullable: false) |
| final Map<String, Dependency> dependencyOverrides; |
| |
| /// Optional configuration specific to [Flutter](https://flutter.io/) |
| /// packages. |
| /// |
| /// May include |
| /// [assets](https://flutter.io/docs/development/ui/assets-and-images) |
| /// and other settings. |
| final Map<String, dynamic> flutter; |
| |
| /// If [author] and [authors] are both provided, their values are combined |
| /// with duplicates eliminated. |
| Pubspec( |
| this.name, { |
| this.version, |
| this.publishTo, |
| String author, |
| List<String> authors, |
| Map<String, VersionConstraint> environment, |
| this.homepage, |
| this.repository, |
| this.issueTracker, |
| this.documentation, |
| this.description, |
| Map<String, Dependency> dependencies, |
| Map<String, Dependency> devDependencies, |
| Map<String, Dependency> dependencyOverrides, |
| this.flutter, |
| }) : authors = _normalizeAuthors(author, authors), |
| environment = environment ?? const {}, |
| dependencies = dependencies ?? const {}, |
| devDependencies = devDependencies ?? const {}, |
| dependencyOverrides = dependencyOverrides ?? const {} { |
| if (name == null || name.isEmpty) { |
| throw ArgumentError.value(name, 'name', '"name" cannot be empty.'); |
| } |
| |
| if (publishTo != null && publishTo != 'none') { |
| try { |
| final targetUri = Uri.parse(publishTo); |
| if (!(targetUri.isScheme('http') || targetUri.isScheme('https'))) { |
| throw const FormatException('Must be an http or https URL.'); |
| } |
| } on FormatException catch (e) { |
| throw ArgumentError.value(publishTo, 'publishTo', e.message); |
| } |
| } |
| } |
| |
| factory Pubspec.fromJson(Map json, {bool lenient = false}) { |
| lenient ??= false; |
| |
| if (lenient) { |
| while (json.isNotEmpty) { |
| // Attempting to remove top-level properties that cause parsing errors. |
| try { |
| return _$PubspecFromJson(json); |
| } on CheckedFromJsonException catch (e) { |
| if (e.map == json && json.containsKey(e.key)) { |
| json = Map.from(json)..remove(e.key); |
| continue; |
| } |
| rethrow; |
| } |
| } |
| } |
| |
| return _$PubspecFromJson(json); |
| } |
| |
| /// Parses source [yaml] into [Pubspec]. |
| /// |
| /// When [lenient] is set, top-level property-parsing or type cast errors are |
| /// ignored and `null` values are returned. |
| factory Pubspec.parse(String yaml, {sourceUrl, bool lenient = false}) { |
| lenient ??= false; |
| |
| return checkedYamlDecode( |
| yaml, (map) => Pubspec.fromJson(map, lenient: lenient), |
| sourceUrl: sourceUrl); |
| } |
| |
| static List<String> _normalizeAuthors(String author, List<String> authors) { |
| final value = <String>{}; |
| if (author != null) { |
| value.add(author); |
| } |
| if (authors != null) { |
| value.addAll(authors); |
| } |
| return value.toList(); |
| } |
| } |
| |
| Version _versionFromString(String input) => |
| input == null ? null : Version.parse(input); |
| |
| Map<String, VersionConstraint> _environmentMap(Map source) => |
| source?.map((k, value) { |
| final key = k as String; |
| if (key == 'dart') { |
| // github.com/dart-lang/pub/blob/d84173eeb03c3/lib/src/pubspec.dart#L342 |
| // 'dart' is not allowed as a key! |
| throw CheckedFromJsonException( |
| source, |
| 'dart', |
| 'VersionConstraint', |
| 'Use "sdk" to for Dart SDK constraints.', |
| badKey: true, |
| ); |
| } |
| |
| VersionConstraint constraint; |
| if (value == null) { |
| constraint = null; |
| } else if (value is String) { |
| try { |
| constraint = VersionConstraint.parse(value); |
| } on FormatException catch (e) { |
| throw CheckedFromJsonException(source, key, 'Pubspec', e.message); |
| } |
| |
| return MapEntry(key, constraint); |
| } else { |
| throw CheckedFromJsonException( |
| source, key, 'VersionConstraint', '`$value` is not a String.'); |
| } |
| |
| return MapEntry(key, constraint); |
| }); |