Skip to content

Commit d00f20f

Browse files
authored
refactor(dart_frog_prod_server): define internal PubspecLock (#1273)
1 parent 44f57a6 commit d00f20f

File tree

3 files changed

+554
-0
lines changed

3 files changed

+554
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/// A simple parser for pubspec.lock files.
2+
///
3+
/// This is used by the bundling process to check for those dependencies that
4+
/// are external path dependencies. Hence, it is not a complete parser, it only
5+
/// parses the information that is needed for the bundling process.
6+
library pubspec_lock;
7+
8+
import 'dart:collection';
9+
10+
import 'package:equatable/equatable.dart';
11+
import 'package:yaml/yaml.dart';
12+
13+
/// {@template pubspec_lock_parse_exception}
14+
/// Thrown when a [PubspecLock] fails to parse.
15+
/// {@endtemplate}
16+
class PubspecLockParseException implements Exception {
17+
/// {@macro pubspec_lock_parse_exception}
18+
const PubspecLockParseException();
19+
}
20+
21+
/// A representation of a pubspec.lock file.
22+
class PubspecLock {
23+
const PubspecLock._({
24+
required this.packages,
25+
});
26+
27+
/// Parses a [PubspecLock] from a string.
28+
///
29+
/// If no packages are found, an empty [PubspecLock] is returned. Those
30+
/// packages entries that cannot be parsed are ignored.
31+
///
32+
/// It throws a [PubspecLockParseException] if the string cannot be parsed
33+
/// as a [YamlMap].
34+
factory PubspecLock.fromString(String content) {
35+
late final YamlMap yaml;
36+
try {
37+
yaml = loadYaml(content) as YamlMap;
38+
} catch (_) {
39+
throw const PubspecLockParseException();
40+
}
41+
42+
if (!yaml.containsKey('packages')) {
43+
return PubspecLock.empty;
44+
}
45+
46+
final packages = yaml['packages'] as YamlMap;
47+
48+
final parsedPackages = <PubspecLockPackage>[];
49+
for (final entry in packages.entries) {
50+
try {
51+
final package = PubspecLockPackage.fromYamlMap(
52+
name: entry.key as String,
53+
data: entry.value as YamlMap,
54+
);
55+
parsedPackages.add(package);
56+
} catch (_) {
57+
// Ignore those packages that for some reason cannot be parsed.
58+
}
59+
}
60+
61+
return PubspecLock._(
62+
packages: UnmodifiableListView(parsedPackages),
63+
);
64+
}
65+
66+
/// An empty [PubspecLock].
67+
static PubspecLock empty = PubspecLock._(
68+
packages: UnmodifiableListView([]),
69+
);
70+
71+
/// All the dependencies in the pubspec.lock file.
72+
final UnmodifiableListView<PubspecLockPackage> packages;
73+
}
74+
75+
/// {@template pubspec_lock_dependency}
76+
/// A representation of a dependency in a pubspec.lock file.
77+
/// {@endtemplate}
78+
class PubspecLockPackage extends Equatable {
79+
/// {@macro pubspec_lock_dependency}
80+
const PubspecLockPackage({
81+
required this.name,
82+
required this.type,
83+
this.pathDescription,
84+
});
85+
86+
/// Parses a [PubspecLockPackage] from a [YamlMap].
87+
factory PubspecLockPackage.fromYamlMap({
88+
required String name,
89+
required YamlMap data,
90+
}) {
91+
final dependency = data['dependency'] as String;
92+
final dependencyType = PubspecLockPackageDependencyType.parse(dependency);
93+
94+
final description = data['description'] as YamlMap?;
95+
final pathDescription = description != null
96+
? PubspecPackagePathDescription.tryParse(description)
97+
: null;
98+
99+
return PubspecLockPackage(
100+
name: name,
101+
type: dependencyType,
102+
pathDescription: pathDescription,
103+
);
104+
}
105+
106+
/// The name of the dependency.
107+
final String name;
108+
109+
/// {@macro pubspec_lock_dependency_type}
110+
final PubspecLockPackageDependencyType type;
111+
112+
/// {@macro pubspec_package_path_description}
113+
final PubspecPackagePathDescription? pathDescription;
114+
115+
@override
116+
List<Object?> get props => [name, type, pathDescription];
117+
}
118+
119+
/// {@template pubspec_lock_dependency_type}
120+
/// The type of a [PubspecLockPackage].
121+
/// {@endtemplate}
122+
enum PubspecLockPackageDependencyType {
123+
/// Another package that your package needs to work.
124+
///
125+
/// See also:
126+
///
127+
/// * [Dart's dependency documentation](https://dart.dev/tools/pub/dependencies)
128+
directMain._('direct main'),
129+
130+
/// Another package that your package needs during development.
131+
///
132+
/// See also:
133+
///
134+
/// * [Dart's developer dependency documentation](https://dart.dev/tools/pub/dependencies#dev-dependencies)
135+
directDev._('direct dev'),
136+
137+
/// A dependency that your package indirectly uses because one of its
138+
/// dependencies requires it.
139+
///
140+
/// See also:
141+
///
142+
/// * [Dart's transitive dependency documentation](https://dart.dev/tools/pub/glossary#transitive-)
143+
transitive._('transitive'),
144+
145+
/// A dependency that your package overrides that is not already a
146+
/// `direct main` or `direct dev` dependency.
147+
///
148+
/// See also:
149+
///
150+
/// * [Dart's dependency override documentation](https://dart.dev/tools/pub/dependencies#dependency-overrides)
151+
directOverridden._('direct overridden');
152+
153+
const PubspecLockPackageDependencyType._(this.value);
154+
155+
/// Parses a [PubspecLockPackageDependencyType] from a string.
156+
///
157+
/// Throws an [ArgumentError] if the string is not a valid dependency type.
158+
factory PubspecLockPackageDependencyType.parse(String value) {
159+
if (_valueMap.containsKey(value)) {
160+
return _valueMap[value]!;
161+
}
162+
163+
throw ArgumentError.value(
164+
value,
165+
'value',
166+
'Invalid PubspecLockPackageDependencyType value.',
167+
);
168+
}
169+
170+
static Map<String, PubspecLockPackageDependencyType> _valueMap = {
171+
for (final type in PubspecLockPackageDependencyType.values)
172+
type.value: type,
173+
};
174+
175+
/// The string representation of the [PubspecLockPackageDependencyType]
176+
/// as it appears in a pubspec.lock file.
177+
final String value;
178+
}
179+
180+
/// {@template pubspec_package_path_description}
181+
/// The description of a path dependency in a pubspec.lock file.
182+
///
183+
/// For example, in:
184+
/// ```yaml
185+
/// my_package:
186+
/// dependency: "direct main"
187+
/// description:
188+
/// path: "packages/my_package"
189+
/// relative: true
190+
/// source: path
191+
/// version: "1.0.0+1"
192+
/// ```
193+
///
194+
/// The description is:
195+
/// ```yaml
196+
/// path: "packages/my_package"
197+
/// relative: true
198+
/// ```
199+
///
200+
/// See also:
201+
///
202+
/// * [PubspecPackagePathDescription.tryParse], which attempts to parses a
203+
/// [YamlMap] into a [PubspecPackagePathDescription].
204+
/// {@endtemplate}
205+
class PubspecPackagePathDescription extends Equatable {
206+
const PubspecPackagePathDescription({
207+
required this.path,
208+
required this.relative,
209+
});
210+
211+
/// Attempts to parse a [YamlMap] into a [PubspecPackagePathDescription].
212+
///
213+
/// Returns `null` if the [YamlMap] does not contain the required data
214+
/// to create a [PubspecPackagePathDescription].
215+
static PubspecPackagePathDescription? tryParse(YamlMap data) {
216+
if ((!data.containsKey('path') || data['path'] is! String) ||
217+
(!data.containsKey('relative') || data['relative'] is! bool)) {
218+
return null;
219+
}
220+
221+
final path = data['path'] as String;
222+
final relative = data['relative'] as bool;
223+
224+
return PubspecPackagePathDescription(
225+
path: path,
226+
relative: relative,
227+
);
228+
}
229+
230+
final String path;
231+
final bool relative;
232+
233+
@override
234+
List<Object?> get props => [path, relative];
235+
}

bricks/dart_frog_prod_server/hooks/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ environment:
66

77
dependencies:
88
dart_frog_gen: ^2.0.0
9+
equatable: ^2.0.5
910
io: ^1.0.3
1011
mason: ^0.1.0-dev.39
1112
path: ^1.8.1
1213
pubspec_lock: ^3.0.2
14+
yaml: ^3.1.2
1315

1416
dev_dependencies:
1517
mocktail: ^1.0.0

0 commit comments

Comments
 (0)