-
Notifications
You must be signed in to change notification settings - Fork 81
/
Copy pathdart_uri.dart
260 lines (227 loc) · 9.26 KB
/
dart_uri.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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// Copyright (c) 2019, 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:dwds/src/config/tool_configuration.dart';
import 'package:logging/logging.dart';
import 'package:package_config/package_config.dart';
import 'package:path/path.dart' as p;
/// The URI for a particular Dart file, able to canonicalize from various
/// different representations.
class DartUri {
DartUri._(this.serverPath);
/// Accepts various forms of URI and can convert between forms.
///
/// The accepted forms are:
///
/// - package:packageName/pathUnderLib/file.dart
/// - org-dartlang-app:///prefix/path/file.dart, where prefix is ignored.
/// e.g. org-dartlang-app:example/hello_world/main.dart,
/// - /packages/packageName/foo.dart, the web server form of a package URI,
/// e.g. /packages/path/src/utils.dart
/// - /path/foo.dart or path/foo.dart, e.g. /hello_world/web/main.dart, where
/// path is a web server path and so relative to the directory being
/// served, not to the package.
///
/// The optional [root] is the directory the app is served from.
factory DartUri(String uri, [String? root]) {
// TODO(annagrin): Support creating DartUris from `dart:` uris.
// Issue: https://github.com/dart-lang/webdev/issues/1584
if (uri.startsWith('org-dartlang-app:') || uri.startsWith('google3:')) {
return DartUri._fromDartLangUri(uri);
}
if (uri.startsWith('package:')) {
return DartUri._fromPackageUri(uri, root: root);
}
if (uri.startsWith('file:')) {
return DartUri._fromFileUri(uri, root: root);
}
if (uri.startsWith('packages/') || uri.startsWith('/packages/')) {
return DartUri._fromRelativePath(uri, root: root);
}
if (uri.startsWith('/')) {
return DartUri._fromRelativePath(uri);
}
if (uri.startsWith('http:') || uri.startsWith('https:')) {
return DartUri(Uri.parse(uri).path);
}
throw FormatException('Unsupported URI form: $uri');
}
@override
String toString() => 'DartUri: $serverPath';
/// Construct from a package: URI
factory DartUri._fromDartLangUri(String uri) {
var serverPath =
globalToolConfiguration.loadStrategy.serverPathForAppUri(uri);
if (serverPath == null) {
_logger.severe('Cannot find server path for $uri');
serverPath = uri;
}
return DartUri._(serverPath);
}
/// Construct from a package: URI
factory DartUri._fromPackageUri(String uri, {String? root}) {
var serverPath =
globalToolConfiguration.loadStrategy.serverPathForAppUri(uri);
if (serverPath == null) {
_logger.severe('Cannot find server path for $uri');
serverPath = uri;
}
return DartUri._fromRelativePath(serverPath, root: root);
}
/// Construct from a file: URI
factory DartUri._fromFileUri(String uri, {String? root}) {
final libraryName = _resolvedUriToUri[uri];
if (libraryName != null) return DartUri(libraryName, root);
// This is not one of our recorded libraries.
throw ArgumentError.value(uri, 'uri', 'Unknown library');
}
/// Construct from a path, relative to the directory being served.
factory DartUri._fromRelativePath(String uri, {String? root}) {
uri = uri[0] == '.' ? uri.substring(1) : uri;
uri = uri[0] == '/' ? uri.substring(1) : uri;
if (root != null) {
return DartUri._fromRelativePath(p.url.join(root, uri));
}
return DartUri._(uri);
}
/// The canonical web server path part of the URI.
///
/// This is a relative path, which can be used to fetch the corresponding file
/// from the server. For example, 'hello_world/main.dart' or
/// 'packages/path/src/utils.dart'.
final String serverPath;
static final _logger = Logger('DartUri');
/// The way we resolve file: URLs into package: URLs
static PackageConfig? _packageConfig;
/// All of the known absolute library paths, indexed by their library URL.
///
/// Examples:
///
/// We are assuming that all library uris are coming from
/// https://github.com/dart-lang/sdk/blob/main/runtime/vm/service/service.md#getscripts)
/// and can be translated to their absolute paths and back.
///
/// dart:html <->
/// org-dartlang-sdk:///sdk/lib/html/html.dart
/// (not supported, issue: https://github.com/dart-lang/webdev/issues/1457)
///
/// org-dartlang-app:///example/hello_world/main.dart <->
/// file:///source/webdev/fixtures/_test/example/hello_world/main.dart,
///
/// org-dartlang-app:///example/hello_world/part.dart <->
/// file:///source/webdev/fixtures/_test/example/hello_world/part.dart,
///
/// package:path/path.dart <->
/// file:///.pub-cache/hosted/pub.dev/path-1.8.0/lib/path.dart,
///
/// package:path/src/path_set.dart <->
/// file:///.pub-cache/hosted/pub.dev/path-1.8.0/lib/src/path_set.dart,
static final Map<String, String> _uriToResolvedUri = {};
static Map<String, String> get uriToResolvedUri => _uriToResolvedUri;
/// All of the known libraries, indexed by their absolute file URL.
static final Map<String, String> _resolvedUriToUri = {};
/// Returns a resolved path for a g3-relative URI.
///
/// This map is empty if not a google3 app.
static final Map<String, String> _g3RelativeUriToResolvedUri = {};
static Map<String, String> get resolvedUriToUri => _resolvedUriToUri;
/// Returns package, app, or dart uri for a resolved path.
static String? toPackageUri(String uri) {
final packageUri = _resolvedUriToUri[uri];
if (packageUri != null) return packageUri;
// If this is an internal app, then the given uri might be g3-relative:
if (globalToolConfiguration.appMetadata.isInternalBuild) {
// TODO(https://github.com/dart-lang/webdev/issues/2198): Verify if the
// intermediary conversion to resolvedUri is causing performance issues.
final resolvedUri = _g3RelativeUriToResolvedUri[uri];
return _resolvedUriToUri[resolvedUri];
}
return null;
}
/// Returns resolved path for a package, app, or dart uri.
static String? toResolvedUri(String uri) => _uriToResolvedUri[uri];
/// The directory in which we're running.
///
/// We store this here because for tests we may want to act as if we're
/// running in the directory of a target package, even if the current
/// directory of the tests is actually the main dwds directory.
static String currentDirectory = p.current;
/// The current directory as a file: Uri, saved here to avoid re-computing.
static final String currentDirectoryUri = '${p.toUri(currentDirectory)}';
/// Record library and script uris to enable resolving library and script paths.
static Future<void> initialize() async {
clear();
await _loadPackageConfig(
p.toUri(globalToolConfiguration.loadStrategy.packageConfigPath),
);
}
/// Clear the uri resolution tables.
static void clear() {
_packageConfig = null;
_resolvedUriToUri.clear();
_uriToResolvedUri.clear();
_g3RelativeUriToResolvedUri.clear();
}
/// Record all of the libraries, indexed by their absolute file: URI.
static void recordAbsoluteUris(Iterable<String> libraryUris) {
for (var uri in libraryUris) {
_recordAbsoluteUri(uri);
if (globalToolConfiguration.appMetadata.isInternalBuild) {
_recordG3RelativeUri(uri);
}
}
}
static void _recordG3RelativeUri(String libraryUri) {
final absoluteUri = _uriToResolvedUri[libraryUri];
if (absoluteUri == null) return;
final g3RelativeUri =
globalToolConfiguration.loadStrategy.g3RelativePath(absoluteUri);
if (g3RelativeUri != null) {
_g3RelativeUriToResolvedUri[g3RelativeUri] = absoluteUri;
}
}
/// Load the .dart_tool/package_config.json file associated with the running
/// application so we can resolve file URLs into package: URLs appropriately.
static Future<void> _loadPackageConfig(Uri uri) async {
_packageConfig = await loadPackageConfigUri(
uri,
onError: (e) {
_logger.warning('Cannot read packages spec: $uri', e);
},
);
}
/// Record the library represented by package: or org-dartlang-app: uris
/// indexed by absolute file: URI.
static void _recordAbsoluteUri(String libraryUri) {
final uri = Uri.parse(libraryUri);
if (uri.scheme.isEmpty && !uri.path.endsWith('.dart')) {
// ignore non-dart files
return;
}
String? libraryPath;
switch (uri.scheme) {
case 'dart':
// TODO(annagrin): Support resolving `dart:` uris.
// Issue: https://github.com/dart-lang/webdev/issues/1584
return;
case 'org-dartlang-app':
case 'google3':
// Both currentDirectoryUri and the libraryUri path should have '/'
// separators, so we can join them as url paths to get the absolute file
// url.
libraryPath = p.url.join(currentDirectoryUri, uri.path.substring(1));
break;
case 'package':
libraryPath = _packageConfig?.resolve(uri)?.toString();
break;
default:
throw ArgumentError.value(libraryUri, 'URI scheme not allowed');
}
if (libraryPath != null) {
_uriToResolvedUri[libraryUri] = libraryPath;
_resolvedUriToUri[libraryPath] = libraryUri;
} else {
_logger.fine('Unresolved uri: $uri');
}
}
}