[email protected] | bdd5c00 | 2012-05-18 23:14:18 +0000 | [diff] [blame] | 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 | |
Jacob MacDonald | 9e216e0 | 2017-09-21 16:51:09 -0700 | [diff] [blame] | 5 | import 'dart:io'; |
| 6 | |
Kevin Moore | 00b3639 | 2020-07-01 00:23:23 -0700 | [diff] [blame] | 7 | import 'package:collection/collection.dart' hide mapMap; |
[email protected] | 6192b3d | 2013-07-12 17:54:48 +0000 | [diff] [blame] | 8 | import 'package:path/path.dart' as path; |
[email protected] | 3d164a9 | 2014-10-01 17:57:05 +0000 | [diff] [blame] | 9 | import 'package:pub_semver/pub_semver.dart'; |
[email protected] | f6065d9 | 2014-07-23 23:44:09 +0000 | [diff] [blame] | 10 | import 'package:source_span/source_span.dart'; |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 11 | import 'package:yaml/yaml.dart'; |
[email protected] | 0e3af51 | 2013-02-01 23:45:26 +0000 | [diff] [blame] | 12 | |
[email protected] | 1d0ac9a | 2014-06-25 23:28:48 +0000 | [diff] [blame] | 13 | import 'exceptions.dart'; |
[email protected] | f0ff41d | 2013-02-02 00:47:26 +0000 | [diff] [blame] | 14 | import 'io.dart'; |
Sigurd Meldgaard | 152e474 | 2020-07-24 17:06:05 +0200 | [diff] [blame] | 15 | import 'language_version.dart'; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 16 | import 'log.dart'; |
Natalie Weizenbaum | bc2e501 | 2017-06-20 16:23:18 -0700 | [diff] [blame] | 17 | import 'package_name.dart'; |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 18 | import 'pubspec_parse.dart'; |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 19 | import 'sdk.dart'; |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 20 | import 'system_cache.dart'; |
[email protected] | 05c9cc0 | 2012-10-09 01:25:43 +0000 | [diff] [blame] | 21 | import 'utils.dart'; |
[email protected] | bdd5c00 | 2012-05-18 23:14:18 +0000 | [diff] [blame] | 22 | |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 23 | export 'pubspec_parse.dart' hide PubspecBase; |
Natalie Weizenbaum | 8c091bf | 2015-12-01 16:30:13 -0800 | [diff] [blame] | 24 | |
Jacob MacDonald | 6c8357c | 2017-09-07 12:30:43 -0700 | [diff] [blame] | 25 | /// The default SDK upper bound constraint for packages that don't declare one. |
Natalie Weizenbaum | 62f917a | 2017-07-14 13:23:47 -0700 | [diff] [blame] | 26 | /// |
Jacob MacDonald | 6c8357c | 2017-09-07 12:30:43 -0700 | [diff] [blame] | 27 | /// This provides a sane default for packages that don't have an upper bound. |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 28 | final VersionRange _defaultUpperBoundSdkConstraint = |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 29 | VersionConstraint.parse('<2.0.0') as VersionRange; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 30 | |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 31 | /// Whether or not to allow the pre-release SDK for packages that have an |
| 32 | /// upper bound Dart SDK constraint of <2.0.0. |
| 33 | /// |
| 34 | /// If enabled then a Dart SDK upper bound of <2.0.0 is always converted to |
| 35 | /// <2.0.0-dev.infinity. |
| 36 | /// |
| 37 | /// This has a default value of `true` but can be overridden with the |
| 38 | /// PUB_ALLOW_PRERELEASE_SDK system environment variable. |
Kevin Moore | 2e821bf | 2018-04-25 09:40:39 -0700 | [diff] [blame] | 39 | bool get _allowPreReleaseSdk => _allowPreReleaseSdkValue != 'false'; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 40 | |
| 41 | /// The value of the PUB_ALLOW_PRERELEASE_SDK environment variable, defaulted |
| 42 | /// to `true`. |
Kevin Moore | 2e821bf | 2018-04-25 09:40:39 -0700 | [diff] [blame] | 43 | final String _allowPreReleaseSdkValue = () { |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 44 | var value = |
Nate Bosch | ceaa86f | 2020-01-06 14:12:36 -0800 | [diff] [blame] | 45 | Platform.environment['PUB_ALLOW_PRERELEASE_SDK']?.toLowerCase() ?? 'true'; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 46 | if (!['true', 'quiet', 'false'].contains(value)) { |
| 47 | warning(yellow(''' |
| 48 | The environment variable PUB_ALLOW_PRERELEASE_SDK is set as `$value`. |
| 49 | The expected value is either `true`, `quiet` (true but no logging), or `false`. |
| 50 | Using a default value of `true`. |
| 51 | ''')); |
| 52 | value = 'true'; |
| 53 | } |
| 54 | return value; |
| 55 | }(); |
| 56 | |
| 57 | /// Whether or not to warn about pre-release SDK overrides. |
Kevin Moore | 2e821bf | 2018-04-25 09:40:39 -0700 | [diff] [blame] | 58 | bool get warnAboutPreReleaseSdkOverrides => _allowPreReleaseSdkValue != 'quiet'; |
Natalie Weizenbaum | 62f917a | 2017-07-14 13:23:47 -0700 | [diff] [blame] | 59 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 60 | /// The parsed contents of a pubspec file. |
| 61 | /// |
| 62 | /// The fields of a pubspec are, for the most part, validated when they're first |
| 63 | /// accessed. This allows a partially-invalid pubspec to be used if only the |
| 64 | /// valid portions are relevant. To get a list of all errors in the pubspec, use |
| 65 | /// [allErrors]. |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 66 | class Pubspec extends PubspecBase { |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 67 | // If a new lazily-initialized field is added to this class and the |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 68 | // initialization can throw a [PubspecException], that error should also be |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 69 | // exposed through [allErrors]. |
[email protected] | cfd2148 | 2012-08-24 18:48:00 +0000 | [diff] [blame] | 70 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 71 | /// The fields of [pubspecOverridesFilename]. `null` if no such file exists or has |
| 72 | /// to be considered. |
| 73 | final YamlMap? _overridesFileFields; |
| 74 | |
| 75 | String? get _packageName => fields['name'] != null ? name : null; |
| 76 | |
| 77 | /// The name of the manifest file. |
| 78 | static const pubspecYamlFilename = 'pubspec.yaml'; |
| 79 | |
| 80 | /// The filename of the pubspec overrides file. |
| 81 | /// |
| 82 | /// This file can contain dependency_overrides that override those in |
| 83 | /// pubspec.yaml. |
| 84 | static const pubspecOverridesFilename = 'pubspec_overrides.yaml'; |
| 85 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 86 | /// The registry of sources to use when parsing [dependencies] and |
| 87 | /// [devDependencies]. |
| 88 | /// |
Sigurd Meldgaard | 6aeb179 | 2022-06-07 14:27:16 +0200 | [diff] [blame] | 89 | /// This will be null if this was created using [Pubspec] or [Pubspec.empty]. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 90 | final SourceRegistry _sources; |
[email protected] | bdd5c00 | 2012-05-18 23:14:18 +0000 | [diff] [blame] | 91 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 92 | /// The location from which the pubspec was loaded. |
| 93 | /// |
| 94 | /// This can be null if the pubspec was created in-memory or if its location |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 95 | /// is unknown. |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 96 | Uri? get _location => fields.span.sourceUrl; |
[email protected] | f8b08f7 | 2013-01-23 17:41:40 +0000 | [diff] [blame] | 97 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 98 | /// The additional packages this package depends on. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 99 | Map<String, PackageRange> get dependencies => |
| 100 | _dependencies ??= _parseDependencies( |
| 101 | 'dependencies', |
| 102 | fields.nodes['dependencies'], |
| 103 | _sources, |
| 104 | languageVersion, |
| 105 | _packageName, |
| 106 | _location); |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 107 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 108 | Map<String, PackageRange>? _dependencies; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 109 | |
| 110 | /// The packages this package depends on when it is the root package. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 111 | Map<String, PackageRange> get devDependencies => |
| 112 | _devDependencies ??= _parseDependencies( |
| 113 | 'dev_dependencies', |
| 114 | fields.nodes['dev_dependencies'], |
| 115 | _sources, |
| 116 | languageVersion, |
| 117 | _packageName, |
| 118 | _location, |
| 119 | ); |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 120 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 121 | Map<String, PackageRange>? _devDependencies; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 122 | |
[email protected] | 8bf7581 | 2013-11-18 21:51:24 +0000 | [diff] [blame] | 123 | /// The dependency constraints that this package overrides when it is the |
| 124 | /// root package. |
| 125 | /// |
| 126 | /// Dependencies here will replace any dependency on a package with the same |
| 127 | /// name anywhere in the dependency graph. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 128 | /// |
| 129 | /// These can occur both in the pubspec.yaml file and the [pubspecOverridesFilename]. |
| 130 | Map<String, PackageRange> get dependencyOverrides { |
| 131 | if (_dependencyOverrides != null) return _dependencyOverrides!; |
| 132 | final pubspecOverridesFields = _overridesFileFields; |
| 133 | if (pubspecOverridesFields != null) { |
| 134 | pubspecOverridesFields.nodes.forEach((key, _) { |
| 135 | if (!const {'dependency_overrides'}.contains(key.value)) { |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 136 | throw SourceSpanApplicationException( |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 137 | 'pubspec_overrides.yaml only supports the `dependency_overrides` field.', |
| 138 | key.span, |
| 139 | ); |
| 140 | } |
| 141 | }); |
| 142 | if (pubspecOverridesFields.containsKey('dependency_overrides')) { |
| 143 | _dependencyOverrides = _parseDependencies( |
| 144 | 'dependency_overrides', |
| 145 | pubspecOverridesFields.nodes['dependency_overrides'], |
| 146 | _sources, |
| 147 | languageVersion, |
| 148 | _packageName, |
| 149 | _location, |
| 150 | fileType: _FileType.pubspecOverrides, |
| 151 | ); |
| 152 | } |
| 153 | } |
| 154 | return _dependencyOverrides ??= _parseDependencies( |
| 155 | 'dependency_overrides', |
| 156 | fields.nodes['dependency_overrides'], |
| 157 | _sources, |
| 158 | languageVersion, |
| 159 | _packageName, |
| 160 | _location, |
| 161 | ); |
| 162 | } |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 163 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 164 | Map<String, PackageRange>? _dependencyOverrides; |
[email protected] | 8bf7581 | 2013-11-18 21:51:24 +0000 | [diff] [blame] | 165 | |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 166 | /// A map from SDK identifiers to constraints on those SDK versions. |
| 167 | Map<String, VersionConstraint> get sdkConstraints { |
Natalie Weizenbaum | 8b03f3f | 2017-07-07 16:27:48 -0700 | [diff] [blame] | 168 | _ensureEnvironment(); |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 169 | return _sdkConstraints!; |
Natalie Weizenbaum | 9a178f3 | 2016-07-21 17:45:13 -0700 | [diff] [blame] | 170 | } |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 171 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 172 | Map<String, VersionConstraint>? _sdkConstraints; |
Natalie Weizenbaum | 9a178f3 | 2016-07-21 17:45:13 -0700 | [diff] [blame] | 173 | |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 174 | /// Whether or not to apply the [_defaultUpperBoundsSdkConstraint] to this |
| 175 | /// pubspec. |
| 176 | final bool _includeDefaultSdkConstraint; |
| 177 | |
| 178 | /// Whether or not the SDK version was overridden from <2.0.0 to |
| 179 | /// <2.0.0-dev.infinity. |
| 180 | bool get dartSdkWasOverridden => _dartSdkWasOverridden; |
| 181 | bool _dartSdkWasOverridden = false; |
| 182 | |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 183 | /// The original Dart SDK constraint as written in the pubspec. |
| 184 | /// |
| 185 | /// If [dartSdkWasOverridden] is `false`, this will be identical to |
| 186 | /// `sdkConstraints["dart"]`. |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 187 | VersionConstraint get originalDartSdkConstraint { |
| 188 | _ensureEnvironment(); |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 189 | return _originalDartSdkConstraint ?? sdkConstraints['dart']!; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 190 | } |
| 191 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 192 | VersionConstraint? _originalDartSdkConstraint; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 193 | |
Natalie Weizenbaum | 8b03f3f | 2017-07-07 16:27:48 -0700 | [diff] [blame] | 194 | /// Ensures that the top-level "environment" field has been parsed and |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 195 | /// [_sdkConstraints] is set accordingly. |
Natalie Weizenbaum | 8b03f3f | 2017-07-07 16:27:48 -0700 | [diff] [blame] | 196 | void _ensureEnvironment() { |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 197 | if (_sdkConstraints != null) return; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 198 | |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 199 | var sdkConstraints = _parseEnvironment(fields); |
Nate Bosch | ceaa86f | 2020-01-06 14:12:36 -0800 | [diff] [blame] | 200 | var parsedDartSdkConstraint = sdkConstraints['dart']; |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 201 | |
Jacob MacDonald | 04c7827 | 2017-09-15 13:58:38 -0700 | [diff] [blame] | 202 | if (parsedDartSdkConstraint is VersionRange && |
| 203 | _shouldEnableCurrentSdk(parsedDartSdkConstraint)) { |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 204 | _originalDartSdkConstraint = parsedDartSdkConstraint; |
| 205 | _dartSdkWasOverridden = true; |
Nate Bosch | ceaa86f | 2020-01-06 14:12:36 -0800 | [diff] [blame] | 206 | sdkConstraints['dart'] = VersionRange( |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 207 | min: parsedDartSdkConstraint.min, |
| 208 | includeMin: parsedDartSdkConstraint.includeMin, |
Jacob MacDonald | 04c7827 | 2017-09-15 13:58:38 -0700 | [diff] [blame] | 209 | max: sdk.version, |
| 210 | includeMax: true); |
Jacob MacDonald | aad65fe | 2017-09-14 14:48:14 -0700 | [diff] [blame] | 211 | } |
| 212 | |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 213 | _sdkConstraints = UnmodifiableMapView(sdkConstraints); |
Natalie Weizenbaum | 8b03f3f | 2017-07-07 16:27:48 -0700 | [diff] [blame] | 214 | } |
| 215 | |
Jacob MacDonald | 04c7827 | 2017-09-15 13:58:38 -0700 | [diff] [blame] | 216 | /// Whether or not we should override [sdkConstraint] to be <= the user's |
| 217 | /// current SDK version. |
| 218 | /// |
| 219 | /// This is true if the following conditions are met: |
| 220 | /// |
Kevin Moore | 2e821bf | 2018-04-25 09:40:39 -0700 | [diff] [blame] | 221 | /// - [_allowPreReleaseSdk] is `true` |
Jacob MacDonald | 04c7827 | 2017-09-15 13:58:38 -0700 | [diff] [blame] | 222 | /// - The user's current SDK is a pre-release version. |
| 223 | /// - The original [sdkConstraint] max version is exclusive (`includeMax` |
| 224 | /// is `false`). |
| 225 | /// - The original [sdkConstraint] is not a pre-release version. |
| 226 | /// - The original [sdkConstraint] matches the exact same major, minor, and |
| 227 | /// patch versions as the user's current SDK. |
| 228 | bool _shouldEnableCurrentSdk(VersionRange sdkConstraint) { |
Kevin Moore | 2e821bf | 2018-04-25 09:40:39 -0700 | [diff] [blame] | 229 | if (!_allowPreReleaseSdk) return false; |
Jacob MacDonald | 04c7827 | 2017-09-15 13:58:38 -0700 | [diff] [blame] | 230 | if (!sdk.version.isPreRelease) return false; |
| 231 | if (sdkConstraint.includeMax) return false; |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 232 | var minSdkConstraint = sdkConstraint.min; |
| 233 | if (minSdkConstraint != null && |
| 234 | minSdkConstraint.isPreRelease && |
| 235 | equalsIgnoringPreRelease(sdkConstraint.min!, sdk.version)) { |
Natalie Weizenbaum | f1e09a3 | 2018-01-03 14:15:02 -0800 | [diff] [blame] | 236 | return false; |
| 237 | } |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 238 | var maxSdkConstraint = sdkConstraint.max; |
| 239 | if (maxSdkConstraint == null) return false; |
| 240 | if (maxSdkConstraint.max.isPreRelease && |
| 241 | !maxSdkConstraint.isFirstPreRelease) { |
Natalie Weizenbaum | 2164713 | 2018-05-02 16:18:57 -0700 | [diff] [blame] | 242 | return false; |
| 243 | } |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 244 | return equalsIgnoringPreRelease(maxSdkConstraint, sdk.version); |
Jacob MacDonald | 04c7827 | 2017-09-15 13:58:38 -0700 | [diff] [blame] | 245 | } |
| 246 | |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 247 | /// Parses the "environment" field in [parent] and returns a map from SDK |
| 248 | /// identifiers to constraints on those SDKs. |
| 249 | Map<String, VersionConstraint> _parseEnvironment(YamlMap parent) { |
Natalie Weizenbaum | 8b03f3f | 2017-07-07 16:27:48 -0700 | [diff] [blame] | 250 | var yaml = parent['environment']; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 251 | if (yaml == null) { |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 252 | return { |
Nate Bosch | ceaa86f | 2020-01-06 14:12:36 -0800 | [diff] [blame] | 253 | 'dart': _includeDefaultSdkConstraint |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 254 | ? _defaultUpperBoundSdkConstraint |
| 255 | : VersionConstraint.any |
| 256 | }; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 257 | } |
| 258 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 259 | if (yaml is! YamlMap) { |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 260 | _error('"environment" field must be a map.', |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 261 | parent.nodes['environment']!.span); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 262 | } |
| 263 | |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 264 | var constraints = { |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 265 | 'dart': _parseVersionConstraint( |
| 266 | yaml.nodes['sdk'], _packageName, _FileType.pubspec, |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 267 | defaultUpperBoundConstraint: _includeDefaultSdkConstraint |
| 268 | ? _defaultUpperBoundSdkConstraint |
| 269 | : null) |
| 270 | }; |
| 271 | yaml.nodes.forEach((name, constraint) { |
| 272 | if (name.value is! String) { |
| 273 | _error('SDK names must be strings.', name.span); |
Nate Bosch | ceaa86f | 2020-01-06 14:12:36 -0800 | [diff] [blame] | 274 | } else if (name.value == 'dart') { |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 275 | _error('Use "sdk" to for Dart SDK constraints.', name.span); |
| 276 | } |
Nate Bosch | ceaa86f | 2020-01-06 14:12:36 -0800 | [diff] [blame] | 277 | if (name.value == 'sdk') return; |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 278 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 279 | constraints[name.value as String] = |
| 280 | _parseVersionConstraint(constraint, _packageName, _FileType.pubspec, |
| 281 | // Flutter constraints get special treatment, as Flutter won't be |
| 282 | // using semantic versioning to mark breaking releases. |
| 283 | ignoreUpperBound: name.value == 'flutter'); |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 284 | }); |
| 285 | |
| 286 | return constraints; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 287 | } |
[email protected] | bdd5c00 | 2012-05-18 23:14:18 +0000 | [diff] [blame] | 288 | |
Sigurd Meldgaard | 152e474 | 2020-07-24 17:06:05 +0200 | [diff] [blame] | 289 | /// The language version implied by the sdk constraint. |
Jonas Finnemann Jensen | 3ea2b83 | 2020-11-12 13:50:27 +0100 | [diff] [blame] | 290 | LanguageVersion get languageVersion => |
| 291 | LanguageVersion.fromSdkConstraint(originalDartSdkConstraint); |
Sigurd Meldgaard | 152e474 | 2020-07-24 17:06:05 +0200 | [diff] [blame] | 292 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 293 | /// Loads the pubspec for a package located in [packageDir]. |
| 294 | /// |
| 295 | /// If [expectedName] is passed and the pubspec doesn't have a matching name |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 296 | /// field, this will throw a [SourceSpanApplicationException]. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 297 | /// |
| 298 | /// If [allowOverridesFile] is `true` [pubspecOverridesFilename] is loaded and |
| 299 | /// is allowed to override dependency_overrides from `pubspec.yaml`. |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 300 | factory Pubspec.load(String packageDir, SourceRegistry sources, |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 301 | {String? expectedName, bool allowOverridesFile = false}) { |
| 302 | var pubspecPath = path.join(packageDir, pubspecYamlFilename); |
| 303 | var overridesPath = path.join(packageDir, pubspecOverridesFilename); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 304 | if (!fileExists(pubspecPath)) { |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 305 | throw FileException( |
Natalie Weizenbaum | 072c5d8 | 2015-07-09 16:42:47 -0700 | [diff] [blame] | 306 | // Make the package dir absolute because for the entrypoint it'll just |
| 307 | // be ".", which may be confusing. |
| 308 | 'Could not find a file named "pubspec.yaml" in ' |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 309 | '"${canonicalize(packageDir)}".', |
[email protected] | 7fae9a6 | 2014-07-17 22:14:21 +0000 | [diff] [blame] | 310 | pubspecPath); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 311 | } |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 312 | String? overridesFileContents = |
| 313 | allowOverridesFile && fileExists(overridesPath) |
| 314 | ? readTextFile(overridesPath) |
| 315 | : null; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 316 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 317 | return Pubspec.parse( |
| 318 | readTextFile(pubspecPath), |
| 319 | sources, |
| 320 | expectedName: expectedName, |
| 321 | location: path.toUri(pubspecPath), |
| 322 | overridesFileContents: overridesFileContents, |
| 323 | overridesLocation: path.toUri(overridesPath), |
| 324 | ); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 325 | } |
| 326 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 327 | Pubspec( |
| 328 | String name, { |
| 329 | Version? version, |
| 330 | Iterable<PackageRange>? dependencies, |
| 331 | Iterable<PackageRange>? devDependencies, |
| 332 | Iterable<PackageRange>? dependencyOverrides, |
| 333 | Map? fields, |
| 334 | SourceRegistry? sources, |
| 335 | Map<String, VersionConstraint>? sdkConstraints, |
| 336 | }) : _dependencies = dependencies == null |
Natalie Weizenbaum | 73433d1 | 2017-11-27 14:18:38 -0800 | [diff] [blame] | 337 | ? null |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 338 | : Map.fromIterable(dependencies, key: (range) => range.name), |
Natalie Weizenbaum | 73433d1 | 2017-11-27 14:18:38 -0800 | [diff] [blame] | 339 | _devDependencies = devDependencies == null |
| 340 | ? null |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 341 | : Map.fromIterable(devDependencies, key: (range) => range.name), |
Natalie Weizenbaum | 73433d1 | 2017-11-27 14:18:38 -0800 | [diff] [blame] | 342 | _dependencyOverrides = dependencyOverrides == null |
| 343 | ? null |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 344 | : Map.fromIterable(dependencyOverrides, key: (range) => range.name), |
Sigurd Meldgaard | e02b7c6 | 2020-03-12 15:26:33 +0100 | [diff] [blame] | 345 | _sdkConstraints = sdkConstraints ?? |
| 346 | UnmodifiableMapView({'dart': VersionConstraint.any}), |
Natalie Weizenbaum | 711f515 | 2018-04-11 13:04:47 -0700 | [diff] [blame] | 347 | _includeDefaultSdkConstraint = false, |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 348 | _sources = sources ?? |
| 349 | ((String? name) => throw StateError('No source registry given')), |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 350 | _overridesFileFields = null, |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 351 | super( |
| 352 | fields == null ? YamlMap() : YamlMap.wrap(fields), |
| 353 | name: name, |
| 354 | version: version, |
| 355 | ); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 356 | |
[email protected] | 12fd104 | 2013-06-04 21:05:00 +0000 | [diff] [blame] | 357 | /// Returns a Pubspec object for an already-parsed map representing its |
| 358 | /// contents. |
| 359 | /// |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 360 | /// If [expectedName] is passed and the pubspec doesn't have a matching name |
| 361 | /// field, this will throw a [PubspecError]. |
| 362 | /// |
| 363 | /// [location] is the location from which this pubspec was loaded. |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 364 | Pubspec.fromMap(Map fields, this._sources, |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 365 | {YamlMap? overridesFields, String? expectedName, Uri? location}) |
| 366 | : _overridesFileFields = overridesFields, |
| 367 | _includeDefaultSdkConstraint = true, |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 368 | super(fields is YamlMap |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 369 | ? fields |
István Soós | df5db1f | 2021-10-01 12:32:50 +0200 | [diff] [blame] | 370 | : YamlMap.wrap(fields, sourceUrl: location)) { |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 371 | // If [expectedName] is passed, ensure that the actual 'name' field exists |
| 372 | // and matches the expectation. |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 373 | if (expectedName == null) return; |
| 374 | if (name == expectedName) return; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 375 | |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 376 | throw SourceSpanApplicationException( |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 377 | '"name" field doesn\'t match expected name ' |
| 378 | '"$expectedName".', |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 379 | this.fields.nodes['name']!.span); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 380 | } |
| 381 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 382 | /// Parses the pubspec stored at [location] whose text is [contents]. |
[email protected] | 7ce9564 | 2014-06-18 20:46:51 +0000 | [diff] [blame] | 383 | /// |
| 384 | /// If the pubspec doesn't define a version for itself, it defaults to |
| 385 | /// [Version.none]. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 386 | factory Pubspec.parse( |
| 387 | String contents, |
| 388 | SourceRegistry sources, { |
| 389 | String? expectedName, |
| 390 | Uri? location, |
| 391 | String? overridesFileContents, |
| 392 | Uri? overridesLocation, |
| 393 | }) { |
| 394 | late final YamlMap pubspecMap; |
| 395 | YamlMap? overridesFileMap; |
Natalie Weizenbaum | 3dc6777 | 2016-12-07 14:17:25 -0800 | [diff] [blame] | 396 | try { |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 397 | pubspecMap = _ensureMap(loadYamlNode(contents, sourceUrl: location)); |
| 398 | if (overridesFileContents != null) { |
| 399 | overridesFileMap = _ensureMap( |
| 400 | loadYamlNode(overridesFileContents, sourceUrl: overridesLocation)); |
| 401 | } |
Natalie Weizenbaum | 3dc6777 | 2016-12-07 14:17:25 -0800 | [diff] [blame] | 402 | } on YamlException catch (error) { |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 403 | throw SourceSpanApplicationException(error.message, error.span); |
Natalie Weizenbaum | 3dc6777 | 2016-12-07 14:17:25 -0800 | [diff] [blame] | 404 | } |
| 405 | |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 406 | return Pubspec.fromMap(pubspecMap, sources, |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 407 | overridesFields: overridesFileMap, |
| 408 | expectedName: expectedName, |
| 409 | location: location); |
| 410 | } |
| 411 | |
| 412 | /// Ensures that [node] is a mapping. |
| 413 | /// |
| 414 | /// If [node] is already a map it is returned. |
| 415 | /// If [node] is yaml-null an empty map is returned. |
| 416 | /// Otherwise an exception is thrown. |
| 417 | static YamlMap _ensureMap(YamlNode node) { |
| 418 | if (node is YamlScalar && node.value == null) { |
| 419 | return YamlMap(sourceUrl: node.span.sourceUrl); |
| 420 | } else if (node is YamlMap) { |
| 421 | return node; |
| 422 | } else { |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 423 | throw SourceSpanApplicationException( |
| 424 | 'The pubspec must be a YAML mapping.', node.span); |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 425 | } |
[email protected] | 0d9a8cc | 2013-02-09 00:16:15 +0000 | [diff] [blame] | 426 | } |
| 427 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 428 | /// Returns a list of most errors in this pubspec. |
| 429 | /// |
| 430 | /// This will return at most one error for each field. |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 431 | List<SourceSpanApplicationException> get allErrors { |
| 432 | var errors = <SourceSpanApplicationException>[]; |
Sigurd Meldgaard | 6aeb179 | 2022-06-07 14:27:16 +0200 | [diff] [blame] | 433 | void collectError(void Function() fn) { |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 434 | try { |
| 435 | fn(); |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 436 | } on SourceSpanApplicationException catch (e) { |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 437 | errors.add(e); |
| 438 | } |
[email protected] | 12fd104 | 2013-06-04 21:05:00 +0000 | [diff] [blame] | 439 | } |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 440 | |
Sigurd Meldgaard | 6aeb179 | 2022-06-07 14:27:16 +0200 | [diff] [blame] | 441 | collectError(() => name); |
| 442 | collectError(() => version); |
| 443 | collectError(() => dependencies); |
| 444 | collectError(() => devDependencies); |
| 445 | collectError(() => publishTo); |
| 446 | collectError(() => executables); |
| 447 | collectError(() => falseSecrets); |
| 448 | collectError(_ensureEnvironment); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 449 | return errors; |
[email protected] | 12fd104 | 2013-06-04 21:05:00 +0000 | [diff] [blame] | 450 | } |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 451 | } |
[email protected] | 12fd104 | 2013-06-04 21:05:00 +0000 | [diff] [blame] | 452 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 453 | /// Parses the dependency field named [field], and returns the corresponding |
| 454 | /// map of dependency names to dependencies. |
| 455 | Map<String, PackageRange> _parseDependencies( |
| 456 | String field, |
| 457 | YamlNode? node, |
| 458 | SourceRegistry sources, |
| 459 | LanguageVersion languageVersion, |
| 460 | String? packageName, |
| 461 | Uri? location, { |
| 462 | _FileType fileType = _FileType.pubspec, |
| 463 | }) { |
| 464 | var dependencies = <String, PackageRange>{}; |
[email protected] | 12fd104 | 2013-06-04 21:05:00 +0000 | [diff] [blame] | 465 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 466 | // Allow an empty dependencies key. |
| 467 | if (node == null || node.value == null) return dependencies; |
[email protected] | 12fd104 | 2013-06-04 21:05:00 +0000 | [diff] [blame] | 468 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 469 | if (node is! YamlMap) { |
| 470 | _error('"$field" field must be a map.', node.span); |
| 471 | } |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 472 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 473 | var nonStringNode = |
| 474 | node.nodes.keys.firstWhere((e) => e.value is! String, orElse: () => null); |
| 475 | if (nonStringNode != null) { |
| 476 | _error('A dependency name must be a string.', nonStringNode.span); |
| 477 | } |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 478 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 479 | node.nodes.forEach( |
| 480 | (nameNode, specNode) { |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 481 | var name = nameNode.value; |
| 482 | var spec = specNode.value; |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 483 | if (packageName != null && name == packageName) { |
Jacob MacDonald | edd3295 | 2017-03-15 11:12:46 -0700 | [diff] [blame] | 484 | _error('A package may not list itself as a dependency.', nameNode.span); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 485 | } |
| 486 | |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 487 | YamlNode? descriptionNode; |
| 488 | String? sourceName; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 489 | |
Sigurd Meldgaard | fdf7b05 | 2020-12-22 12:13:26 +0100 | [diff] [blame] | 490 | VersionConstraint versionConstraint = VersionRange(); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 491 | if (spec == null) { |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 492 | sourceName = null; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 493 | } else if (spec is String) { |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 494 | sourceName = null; |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 495 | versionConstraint = |
| 496 | _parseVersionConstraint(specNode, packageName, fileType); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 497 | } else if (spec is Map) { |
[email protected] | bc2bb14 | 2014-06-03 20:33:29 +0000 | [diff] [blame] | 498 | // Don't write to the immutable YAML map. |
Nate Bosch | 5962908 | 2018-08-07 12:47:19 -0700 | [diff] [blame] | 499 | spec = Map.from(spec); |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 500 | var specMap = specNode as YamlMap; |
[email protected] | bc2bb14 | 2014-06-03 20:33:29 +0000 | [diff] [blame] | 501 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 502 | if (spec.containsKey('version')) { |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 503 | spec.remove('version'); |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 504 | versionConstraint = _parseVersionConstraint( |
| 505 | specMap.nodes['version'], |
| 506 | packageName, |
| 507 | fileType, |
| 508 | ); |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 509 | } |
| 510 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 511 | var sourceNames = spec.keys.toList(); |
| 512 | if (sourceNames.length > 1) { |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 513 | _error('A dependency may only have one source.', specNode.span); |
Natalie Weizenbaum | 597233a | 2015-10-21 14:06:21 -0700 | [diff] [blame] | 514 | } else if (sourceNames.isEmpty) { |
Kevin Moore | 23a59a3 | 2020-02-19 11:40:17 -0800 | [diff] [blame] | 515 | // Default to a hosted dependency if no source is specified. |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 516 | sourceName = 'hosted'; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 517 | } |
| 518 | |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 519 | sourceName ??= sourceNames.single; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 520 | if (sourceName is! String) { |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 521 | _error('A source name must be a string.', |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 522 | specMap.nodes.keys.single.span); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 523 | } |
| 524 | |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 525 | descriptionNode ??= specMap.nodes[sourceName]; |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 526 | } else { |
[email protected] | 8d00035 | 2014-06-24 23:54:03 +0000 | [diff] [blame] | 527 | _error('A dependency specification must be a string or a mapping.', |
| 528 | specNode.span); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 529 | } |
| 530 | |
[email protected] | b2b68e9 | 2014-04-23 23:47:49 +0000 | [diff] [blame] | 531 | // Let the source validate the description. |
Natalie Weizenbaum | 3137e31 | 2017-07-05 15:00:25 -0700 | [diff] [blame] | 532 | var ref = _wrapFormatException('description', descriptionNode?.span, () { |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 533 | String? pubspecDir; |
Sarah Zakarias | 400f21e | 2021-10-16 21:23:33 +0200 | [diff] [blame] | 534 | if (location != null && _isFileUri(location)) { |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 535 | pubspecDir = path.dirname(path.fromUri(location)); |
[email protected] | b2b68e9 | 2014-04-23 23:47:49 +0000 | [diff] [blame] | 536 | } |
| 537 | |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 538 | return sources(sourceName).parseRef( |
Simon Binder | a2dbcff | 2021-10-26 15:12:36 +0200 | [diff] [blame] | 539 | name, |
| 540 | descriptionNode?.value, |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 541 | containingDir: pubspecDir, |
Simon Binder | a2dbcff | 2021-10-26 15:12:36 +0200 | [diff] [blame] | 542 | languageVersion: languageVersion, |
| 543 | ); |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 544 | }, packageName, fileType, targetPackage: name); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 545 | |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 546 | dependencies[name] = ref.withConstraint(versionConstraint); |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 547 | }, |
| 548 | ); |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 549 | |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 550 | return dependencies; |
| 551 | } |
| 552 | |
[email protected] | 84ee8ed | 2013-09-23 22:21:44 +0000 | [diff] [blame] | 553 | /// Returns whether [uri] is a file URI. |
| 554 | /// |
| 555 | /// This is slightly more complicated than just checking if the scheme is |
| 556 | /// 'file', since relative URIs also refer to the filesystem on the VM. |
| 557 | bool _isFileUri(Uri uri) => uri.scheme == 'file' || uri.scheme == ''; |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 558 | |
| 559 | /// Parses [node] to a [VersionConstraint]. |
| 560 | /// |
| 561 | /// If or [defaultUpperBoundConstraint] is specified then it will be set as |
| 562 | /// the max constraint if the original constraint doesn't have an upper |
| 563 | /// bound and it is compatible with [defaultUpperBoundConstraint]. |
| 564 | /// |
| 565 | /// If [ignoreUpperBound] the max constraint is ignored. |
| 566 | VersionConstraint _parseVersionConstraint( |
| 567 | YamlNode? node, String? packageName, _FileType fileType, |
| 568 | {VersionConstraint? defaultUpperBoundConstraint, |
| 569 | bool ignoreUpperBound = false}) { |
| 570 | if (node?.value == null) { |
| 571 | return defaultUpperBoundConstraint ?? VersionConstraint.any; |
| 572 | } |
| 573 | if (node!.value is! String) { |
| 574 | _error('A version constraint must be a string.', node.span); |
| 575 | } |
| 576 | |
| 577 | return _wrapFormatException('version constraint', node.span, () { |
| 578 | var constraint = VersionConstraint.parse(node.value); |
| 579 | if (defaultUpperBoundConstraint != null && |
| 580 | constraint is VersionRange && |
| 581 | constraint.max == null && |
| 582 | defaultUpperBoundConstraint.allowsAny(constraint)) { |
| 583 | constraint = VersionConstraint.intersection( |
| 584 | [constraint, defaultUpperBoundConstraint]); |
| 585 | } |
| 586 | if (ignoreUpperBound && constraint is VersionRange) { |
| 587 | return VersionRange( |
| 588 | min: constraint.min, includeMin: constraint.includeMin); |
| 589 | } |
| 590 | return constraint; |
| 591 | }, packageName, fileType); |
| 592 | } |
| 593 | |
| 594 | /// Runs [fn] and wraps any [FormatException] it throws in a |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 595 | /// [SourceSpanApplicationException]. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 596 | /// |
| 597 | /// [description] should be a noun phrase that describes whatever's being |
| 598 | /// parsed or processed by [fn]. [span] should be the location of whatever's |
| 599 | /// being processed within the pubspec. |
| 600 | /// |
| 601 | /// If [targetPackage] is provided, the value is used to describe the |
| 602 | /// dependency that caused the problem. |
Sigurd Meldgaard | 610ce7f | 2022-03-24 16:46:23 +0100 | [diff] [blame] | 603 | T _wrapFormatException<T>( |
| 604 | String description, |
| 605 | SourceSpan? span, |
| 606 | T Function() fn, |
| 607 | String? packageName, |
| 608 | _FileType fileType, { |
| 609 | String? targetPackage, |
| 610 | }) { |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 611 | try { |
| 612 | return fn(); |
| 613 | } on FormatException catch (e) { |
| 614 | // If we already have a pub exception with a span, re-use that |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 615 | if (e is SourceSpanApplicationException) rethrow; |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 616 | |
| 617 | var msg = 'Invalid $description'; |
| 618 | final typeName = _fileTypeName(fileType); |
| 619 | if (targetPackage != null) { |
| 620 | msg = '$msg in the "$packageName" $typeName on the "$targetPackage" ' |
| 621 | 'dependency'; |
| 622 | } |
| 623 | msg = '$msg: ${e.message}'; |
| 624 | _error(msg, span); |
| 625 | } |
| 626 | } |
| 627 | |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 628 | /// Throws a [SourceSpanApplicationException] with the given message. |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 629 | Never _error(String message, SourceSpan? span) { |
Sigurd Meldgaard | 29d7d86 | 2022-10-04 11:41:32 +0200 | [diff] [blame] | 630 | throw SourceSpanApplicationException(message, span); |
Gabriel Terwesten | 7a6ea39 | 2022-03-24 14:47:41 +0100 | [diff] [blame] | 631 | } |
| 632 | |
| 633 | enum _FileType { |
| 634 | pubspec, |
| 635 | pubspecOverrides, |
| 636 | } |
| 637 | |
| 638 | String _fileTypeName(_FileType type) { |
| 639 | switch (type) { |
| 640 | case _FileType.pubspec: |
| 641 | return 'pubspec'; |
| 642 | case _FileType.pubspecOverrides: |
| 643 | return 'pubspec override'; |
| 644 | } |
| 645 | } |