OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015, 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 | |
5 library package_config.discovery; | |
6 | |
7 import "dart:async"; | |
8 import "dart:io" show Directory, File, FileSystemEntity; | |
9 import "package:path/path.dart" as path; | |
10 import "package:http/http.dart" as http; | |
11 import "packages.dart"; | |
12 import "packages_file.dart" as pkgfile show parse; | |
13 import "src/packages_impl.dart"; | |
14 | |
15 /** | |
16 * Discover the package configuration for a Dart script. | |
17 * | |
18 * The [baseUri] points to either the Dart script or its directory. | |
19 * A package resolution strategy is found by going through the following steps, | |
20 * and stopping when something is found. | |
21 * | |
22 * * Check if a `.packages` file exists in the same directory. | |
23 * * If `baseUri`'s scheme is not `file`, then assume a `packages` directory | |
24 * in the same directory, and resolve packages relative to that. | |
25 * * If `baseUri`'s scheme *is* `file`: | |
26 * * Check if a `packages` directory exists. | |
27 * * Otherwise check each successive parent directory of `baseUri` for a | |
28 * `.packages` file. | |
29 * | |
30 * If any of these tests succeed, a `Packages` class is returned. | |
31 * Returns the constant [noPackages] if no resolution strategy is found. | |
32 * | |
33 * This function currently only supports `file`, `http` and `https` URIs. | |
34 * It needs to be able to load a `.packages` file from the URI, so only | |
35 * recognized schemes are accepted. | |
36 * | |
37 * To support other schemes, an optional [loader] function can be supplied. | |
38 * It's called to load the `.packages` file for any unsupported scheme. | |
39 * It must return the *contents* of the file identified by the URI it's given, | |
40 * which should be a UTF-8 encoded `.packages` file, and must return an | |
41 * error future if loading fails for any reason. | |
42 */ | |
pquitslund
2015/05/21 18:14:22
Nit: maybe prefer the `///` comment convention as
Lasse Reichstein Nielsen
2015/05/21 18:30:21
I'm *trying* to use the new /// convention, but I
| |
43 Future<Packages> findPackages( | |
44 Uri baseUri, | |
45 {Future<List<int>> loader(Uri unsupportedUri)}) { | |
46 if (baseUri.scheme == "file") { | |
47 return new Future<Packages>.sync(() => findPackagesFromFile(baseUri)); | |
48 } else if (baseUri.scheme == "http" || baseUri.scheme == "https") { | |
49 return findPackagesFromNonFile(baseUri, _httpGet); | |
50 } else if (loader != null) { | |
51 return findPackagesFromNonFile(baseUri, loader); | |
52 } else { | |
53 return new Future<Packages>.value(Packages.noPackages); | |
54 } | |
55 } | |
56 | |
57 /// Find the location of the package resolution file/directory for a Dart file. | |
58 /// | |
59 /// Checks for a `.packages` file in the [workingDirectory]. | |
60 /// If not found, checks for a `packages` directory in the same directory. | |
61 /// If still not found, starts checking parent directories for | |
62 /// `.packages` until reaching the root directory. | |
63 /// | |
64 /// Returns a [File] object of a `.packages` file if one is found, or a | |
65 /// [Directory] object for the `packages/` directory if that is found. | |
66 FileSystemEntity _findPackagesFile(String workingDirectory) { | |
67 var dir = new Directory(workingDirectory); | |
68 if (!dir.isAbsolute) dir = dir.absolute; | |
69 if (!dir.existsSync()) { | |
70 throw new ArgumentError.value( | |
71 workingDirectory, "workingDirectory", "Directory does not exist."); | |
72 } | |
73 File checkForConfigFile(Directory directory) { | |
74 assert(directory.isAbsolute); | |
75 var file = new File(path.join(directory.path, ".packages")); | |
76 if (file.existsSync()) return file; | |
77 return null; | |
78 } | |
79 // Check for $cwd/.packages | |
80 var packagesCfgFile = checkForConfigFile(dir); | |
81 if (packagesCfgFile != null) return packagesCfgFile; | |
82 // Check for $cwd/packages/ | |
83 var packagesDir = new Directory(path.join(dir.path, "packages")); | |
84 if (packagesDir.existsSync()) return packagesDir; | |
85 // Check for cwd(/..)+/.packages | |
86 var parentDir = dir.parent; | |
87 while (parentDir.path != dir.path) { | |
88 packagesCfgFile = checkForConfigFile(parentDir); | |
89 if (packagesCfgFile != null) break; | |
90 dir = parentDir; | |
91 parentDir = dir.parent; | |
92 } | |
93 return packagesCfgFile; | |
94 } | |
95 | |
96 Packages findPackagesFromFile(Uri fileBaseUri) { | |
Lasse Reichstein Nielsen
2015/05/22 14:39:06
Added documentation.
| |
97 Uri baseDirectoryUri = fileBaseUri; | |
98 if (!fileBaseUri.path.endsWith('/')) { | |
99 baseDirectoryUri = baseDirectoryUri.resolve("."); | |
100 } | |
101 String baseDirectoryPath = baseDirectoryUri.toFilePath(); | |
102 FileSystemEntity location = _findPackagesFile(baseDirectoryPath); | |
103 if (location == null) return Packages.noPackages; | |
104 if (location is File) { | |
105 List<int> fileBytes = location.readAsBytesSync(); | |
106 Map<String, Uri> map = pkgfile.parse(fileBytes, | |
107 new Uri.file(location.path)); | |
108 return new MapPackages(map); | |
109 } | |
110 assert(location is Directory); | |
111 return new FilePackagesDirectoryPackages(location); | |
112 } | |
113 | |
114 /// Finds a package resolution strategy for a Dart script. | |
115 /// | |
116 /// The [nonFileUri] points to either a Dart script or the directory of the | |
117 /// script. | |
118 /// The [nonFileUri] should not be a `file:` URI since the algorithm for | |
119 /// finding a package resolution strategy is more elaborate for `file:` URIs. | |
120 /// In that case, use [findPackagesFile]. | |
121 /// | |
122 /// This function first Tries to locate a `.packages` file in the [nonFileUri] | |
123 /// directory. If that is not found, it instead assumes a `packages/` directory | |
124 /// in the same place. | |
125 /// | |
126 /// By default, this function only works for `http:` and `https:` URIs. | |
127 /// To support other schemes, a loader must be provided, which is used to | |
128 /// try to load the `.packages` file. The loader should return the contents | |
129 /// of the requestsed `.packages` file as bytes, which will be assumed to be | |
130 /// UTF-8 encoded. | |
131 Future<Packages> findPackagesFromNonFile(Uri nonFileUri, | |
132 [Future<List<int>> loader(Uri name)]) { | |
133 if (loader == null) loader = _httpGet; | |
134 Uri packagesFileUri = nonFileUri.resolve(".packages"); | |
135 return loader(packagesFileUri).then((List<int> fileBytes) { | |
136 Map<String, Uri> map = pkgfile.parse(fileBytes, packagesFileUri); | |
137 return new MapPackages(map); | |
138 }, onError: (_) { | |
139 // Didn't manage to load ".packages". Assume a "packages/" directory. | |
140 Uri packagesDirectoryUri = nonFileUri.resolve("packages/"); | |
141 return new NonFilePackagesDirectoryPackages(packagesDirectoryUri); | |
142 }); | |
143 } | |
144 | |
145 /// Fetches a file using the http library. | |
146 Future<List<int>> _httpGet(Uri uri) { | |
147 return http.get(uri).then((http.Response response) { | |
148 if (response.statusCode == 200) return response.bodyBytes; | |
149 throw 0; // The error message isn't being used for anything. | |
150 }); | |
151 } | |
OLD | NEW |