Line data Source code
1 : /// A simple parser for pubspec.lock files. 2 : /// 3 : /// This is used by the `packages check license` command to check the type and 4 : /// source of the dependencies to analyze. Hence, it is not a complete parser, 5 : /// it only parses the information that is needed for the 6 : /// `packages check license` command. 7 : library pubspec_lock; 8 : 9 : import 'dart:collection'; 10 : 11 : import 'package:equatable/equatable.dart'; 12 : import 'package:yaml/yaml.dart'; 13 : 14 : /// {@template PubspecLockParseException} 15 : /// Thrown when a [PubspecLock] fails to parse. 16 : /// {@endtemplate} 17 : class PubspecLockParseException implements Exception { 18 : /// {@macro PubspecLockParseException} 19 1 : const PubspecLockParseException(); 20 : } 21 : 22 : /// {@template PubspecLock} 23 : /// A representation of a pubspec.lock file. 24 : /// {@endtemplate} 25 : class PubspecLock { 26 1 : const PubspecLock._({ 27 : required this.packages, 28 : }); 29 : 30 : /// Parses a [PubspecLock] from a string. 31 : /// 32 : /// If no packages are found, an empty [PubspecLock] is returned. Those 33 : /// packages entries that cannot be parsed are ignored. 34 : /// 35 : /// It throws a [PubspecLockParseException] if the string cannot be parsed 36 : /// as a [YamlMap]. 37 1 : factory PubspecLock.fromString(String content) { 38 : late final YamlMap yaml; 39 : try { 40 1 : yaml = loadYaml(content) as YamlMap; 41 : } catch (_) { 42 : throw const PubspecLockParseException(); 43 : } 44 : 45 1 : if (!yaml.containsKey('packages')) { 46 1 : return PubspecLock.empty; 47 : } 48 : 49 1 : final packages = yaml['packages'] as YamlMap; 50 : 51 1 : final parsedPackages = <PubspecLockPackage>[]; 52 2 : for (final entry in packages.entries) { 53 : try { 54 1 : final package = PubspecLockPackage.fromYamlMap( 55 1 : name: entry.key as String, 56 1 : data: entry.value as YamlMap, 57 : ); 58 1 : parsedPackages.add(package); 59 : } catch (_) { 60 : // Ignore those packages that for some reason cannot be parsed. 61 : } 62 : } 63 : 64 1 : return PubspecLock._( 65 1 : packages: UnmodifiableListView(parsedPackages), 66 : ); 67 : } 68 : 69 : /// An empty [PubspecLock]. 70 3 : static PubspecLock empty = PubspecLock._( 71 2 : packages: UnmodifiableListView([]), 72 : ); 73 : 74 : /// All the dependencies in the pubspec.lock file. 75 : final UnmodifiableListView<PubspecLockPackage> packages; 76 : } 77 : 78 : /// {@template PubspecLockDependency} 79 : /// A representation of a dependency in a pubspec.lock file. 80 : /// {@endtemplate} 81 : class PubspecLockPackage extends Equatable { 82 : /// {@macro PubspecLockDependency} 83 2 : const PubspecLockPackage({ 84 : required this.name, 85 : required this.type, 86 : required this.isPubHosted, 87 : }); 88 : 89 : /// Parses a [PubspecLockPackage] from a [YamlMap]. 90 1 : factory PubspecLockPackage.fromYamlMap({ 91 : required String name, 92 : required YamlMap data, 93 : }) { 94 1 : final dependency = data['dependency'] as String; 95 1 : final dependencyType = PubspecLockPackageDependencyType.parse(dependency); 96 : 97 1 : final source = data['source'] as String; 98 : late final bool isPubHosted; 99 1 : if (source == 'hosted') { 100 1 : final description = data['description'] as YamlMap; 101 1 : final url = description['url'] as String; 102 1 : isPubHosted = url == 'https://pub.dev'; 103 : } else { 104 : isPubHosted = false; 105 : } 106 : 107 1 : return PubspecLockPackage( 108 : name: name, 109 : type: dependencyType, 110 : isPubHosted: isPubHosted, 111 : ); 112 : } 113 : 114 : /// The name of the dependency. 115 : final String name; 116 : 117 : /// {@macro PubspecLockDependencyType} 118 : final PubspecLockPackageDependencyType type; 119 : 120 : /// Whether the dependency is hosted on pub.dev or not. 121 : final bool isPubHosted; 122 : 123 1 : @override 124 3 : List<Object?> get props => [type, isPubHosted]; 125 : } 126 : 127 : /// {@template PubspecLockDependencyType} 128 : /// The type of a [PubspecLockPackage]. 129 : /// {@endtemplate} 130 : enum PubspecLockPackageDependencyType { 131 : /// Another package that your package needs to work. 132 : /// 133 : /// See also: 134 : /// 135 : /// * [Dart's dependency documentation](https://dart.dev/tools/pub/dependencies) 136 : directMain._('direct main'), 137 : 138 : /// Another package that your package needs during development. 139 : /// 140 : /// See also: 141 : /// 142 : /// * [Dart's developer dependency documentation](https://dart.dev/tools/pub/dependencies#dev-dependencies) 143 : directDev._('direct dev'), 144 : 145 : /// A dependency that your package indirectly uses because one of its 146 : /// dependencies requires it. 147 : /// 148 : /// See also: 149 : /// 150 : /// * [Dart's transitive dependency documentation](https://dart.dev/tools/pub/glossary#transitive-) 151 : transitive._('transitive'), 152 : 153 : /// A dependency that your package overrides that is not already a 154 : /// `direct main` or `direct dev` dependency. 155 : /// 156 : /// See also: 157 : /// 158 : /// * [Dart's dependency override documentation](https://dart.dev/tools/pub/dependencies#dependency-overrides) 159 : directOverridden._('direct overridden'); 160 : 161 : const PubspecLockPackageDependencyType._(this.value); 162 : 163 : /// Parses a [PubspecLockPackageDependencyType] from a string. 164 : /// 165 : /// Throws an [ArgumentError] if the string is not a valid dependency type. 166 1 : factory PubspecLockPackageDependencyType.parse(String value) { 167 2 : if (_valueMap.containsKey(value)) { 168 2 : return _valueMap[value]!; 169 : } 170 : 171 1 : throw ArgumentError.value( 172 : value, 173 : 'value', 174 : 'Invalid PubspecLockPackageDependencyType value.', 175 : ); 176 : } 177 : 178 3 : static Map<String, PubspecLockPackageDependencyType> _valueMap = { 179 1 : for (final type in PubspecLockPackageDependencyType.values) 180 2 : type.value: type, 181 : }; 182 : 183 : /// The string representation of the [PubspecLockPackageDependencyType] 184 : /// as it appears in a pubspec.lock file. 185 : final String value; 186 : }