-
Notifications
You must be signed in to change notification settings - Fork 75
/
module_metadata.dart
200 lines (172 loc) · 6.34 KB
/
module_metadata.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright (c) 2020, 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.
/// Module metadata format version
///
/// Module reader always creates the current version but is able to read
/// metadata files with later versions as long as the changes are backward
/// compatible, i.e. only minor or patch versions have changed.
///
/// See: https://goto.google.com/dart-web-debugger-metadata
class ModuleMetadataVersion {
final int majorVersion;
final int minorVersion;
final int patchVersion;
const ModuleMetadataVersion(
this.majorVersion,
this.minorVersion,
this.patchVersion,
);
/// Current metadata version
///
/// Version follows simple semantic versioning format 'major.minor.patch'
/// See https://semver.org
static const ModuleMetadataVersion current = ModuleMetadataVersion(2, 0, 0);
/// Previous version supported by the metadata reader
static const ModuleMetadataVersion previous = ModuleMetadataVersion(1, 0, 0);
/// Current metadata version created by the reader
String get version => '$majorVersion.$minorVersion.$patchVersion';
/// Is this metadata version compatible with the given version
///
/// The minor and patch version changes never remove any fields that current
/// version supports, so the reader can create current metadata version from
/// any file created with a later writer, as long as the major version does
/// not change.
bool isCompatibleWith(String version) {
final parts = version.split('.');
if (parts.length != 3) {
throw FormatException('Version: $version'
'does not follow simple semantic versioning format');
}
final major = int.parse(parts[0]);
final minor = int.parse(parts[1]);
final patch = int.parse(parts[2]);
return major == majorVersion &&
minor >= minorVersion &&
patch >= patchVersion;
}
}
/// Library metadata
///
/// Represents library metadata used in the debugger,
/// supports reading from and writing to json
/// See: https://goto.google.com/dart-web-debugger-metadata
class LibraryMetadata {
/// Library name as defined in pubspec.yaml
final String name;
/// Library importUri
///
/// Example package:path/path.dart
final String importUri;
/// All file uris from the library
///
/// Can be relative paths to the directory of the fileUri
final List<String> partUris;
LibraryMetadata(this.name, this.importUri, this.partUris);
LibraryMetadata.fromJson(Map<String, dynamic> json)
: name = _readRequiredField(json, 'name'),
importUri = _readRequiredField(json, 'importUri'),
partUris = _readOptionalList(json, 'partUris') ?? [];
Map<String, dynamic> toJson() {
return {
'name': name,
'importUri': importUri,
'partUris': [...partUris],
};
}
}
/// Module metadata
///
/// Represents module metadata used in the debugger,
/// supports reading from and writing to json
/// See: https://goto.google.com/dart-web-debugger-metadata
class ModuleMetadata {
/// Metadata format version
late final String version;
/// Module name
///
/// Used as a name of the js module created by the compiler and
/// as key to store and load modules in the debugger and the browser
final String name;
/// Name of the function enclosing the module
///
/// Used by debugger to determine the top dart scope
final String closureName;
/// Source map uri
final String sourceMapUri;
/// Module uri
final String moduleUri;
/// True if the module corresponding to this metadata was compiled with sound
/// null safety enabled.
final bool soundNullSafety;
final Map<String, LibraryMetadata> libraries = {};
ModuleMetadata(
this.name,
this.closureName,
this.sourceMapUri,
this.moduleUri,
this.soundNullSafety, {
String? ver,
}) {
version = ver ?? ModuleMetadataVersion.current.version;
}
/// Add [library] to metadata
///
/// Used for filling the metadata in the compiler or for reading from
/// stored metadata files.
void addLibrary(LibraryMetadata library) {
if (!libraries.containsKey(library.importUri)) {
libraries[library.importUri] = library;
} else {
throw Exception('Metadata creation error: '
'Cannot add library $library with uri ${library.importUri}: '
'another library "${libraries[library.importUri]}" is found '
'with the same uri');
}
}
ModuleMetadata.fromJson(Map<String, dynamic> json)
: version = _readRequiredField(json, 'version'),
name = _readRequiredField(json, 'name'),
closureName = _readRequiredField(json, 'closureName'),
sourceMapUri = _readRequiredField(json, 'sourceMapUri'),
moduleUri = _readRequiredField(json, 'moduleUri'),
soundNullSafety = _readOptionalField(json, 'soundNullSafety') ?? false {
if (!ModuleMetadataVersion.current.isCompatibleWith(version) &&
!ModuleMetadataVersion.previous.isCompatibleWith(version)) {
throw Exception('Unsupported metadata version $version. '
'\n Supported versions: '
'\n ${ModuleMetadataVersion.current.version} '
'\n ${ModuleMetadataVersion.previous.version}');
}
for (var l in _readRequiredList(json, 'libraries')) {
addLibrary(LibraryMetadata.fromJson(l as Map<String, dynamic>));
}
}
Map<String, dynamic> toJson() {
return {
'version': version,
'name': name,
'closureName': closureName,
'sourceMapUri': sourceMapUri,
'moduleUri': moduleUri,
'libraries': [for (var lib in libraries.values) lib.toJson()],
'soundNullSafety': soundNullSafety,
};
}
}
T _readRequiredField<T>(Map<String, dynamic> json, String field) {
if (!json.containsKey(field)) {
throw FormatException('Required field $field is not set in $json');
}
return json[field]! as T;
}
T? _readOptionalField<T>(Map<String, dynamic> json, String field) =>
json[field] as T?;
List<T> _readRequiredList<T>(Map<String, dynamic> json, String field) {
final list = _readRequiredField<List<dynamic>>(json, field);
return List.castFrom<dynamic, T>(list);
}
List<T>? _readOptionalList<T>(Map<String, dynamic> json, String field) {
final list = _readOptionalField<List<dynamic>>(json, field);
return list == null ? null : List.castFrom<dynamic, T>(list);
}