| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library package_config.discovery; | 5 library package_config.discovery; |
| 6 | 6 |
| 7 import "dart:async"; | 7 import "dart:async"; |
| 8 import "dart:io"; | 8 import "dart:io"; |
| 9 import "dart:typed_data" show Uint8List; | 9 import "dart:typed_data" show Uint8List; |
| 10 | 10 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 /// Currently that means the URI must have a `file`, `http` or `https` scheme, | 21 /// Currently that means the URI must have a `file`, `http` or `https` scheme, |
| 22 /// and that the file can be loaded and its contents parsed correctly. | 22 /// and that the file can be loaded and its contents parsed correctly. |
| 23 /// | 23 /// |
| 24 /// If the [loader] is provided, it is used to fetch non-`file` URIs, and | 24 /// If the [loader] is provided, it is used to fetch non-`file` URIs, and |
| 25 /// it can support other schemes or set up more complex HTTP requests. | 25 /// it can support other schemes or set up more complex HTTP requests. |
| 26 /// | 26 /// |
| 27 /// This function can be used to load an explicitly configured package | 27 /// This function can be used to load an explicitly configured package |
| 28 /// resolution file, for example one specified using a `--packages` | 28 /// resolution file, for example one specified using a `--packages` |
| 29 /// command-line parameter. | 29 /// command-line parameter. |
| 30 Future<Packages> loadPackagesFile(Uri packagesFile, | 30 Future<Packages> loadPackagesFile(Uri packagesFile, |
| 31 {Future<List<int>> loader(Uri uri)}) { | 31 {Future<List<int>> loader(Uri uri)}) async { |
| 32 Packages parseBytes(List<int> bytes) { | 32 Packages parseBytes(List<int> bytes) { |
| 33 Map<String, Uri> packageMap = pkgfile.parse(bytes, packagesFile); | 33 Map<String, Uri> packageMap = pkgfile.parse(bytes, packagesFile); |
| 34 return new MapPackages(packageMap); | 34 return new MapPackages(packageMap); |
| 35 } | 35 } |
| 36 if (packagesFile.scheme == "file") { | 36 if (packagesFile.scheme == "file") { |
| 37 File file = new File.fromUri(packagesFile); | 37 File file = new File.fromUri(packagesFile); |
| 38 return file.readAsBytes().then(parseBytes); | 38 return parseBytes(await file.readAsBytes()); |
| 39 } | 39 } |
| 40 if (loader == null) { | 40 if (loader == null) { |
| 41 return _httpGet(packagesFile).then(parseBytes); | 41 return parseBytes(await _httpGet(packagesFile)); |
| 42 } | 42 } |
| 43 return loader(packagesFile).then(parseBytes); | 43 return parseBytes(await loader(packagesFile)); |
| 44 } | 44 } |
| 45 | 45 |
| 46 | |
| 47 /// Create a [Packages] object for a package directory. | 46 /// Create a [Packages] object for a package directory. |
| 48 /// | 47 /// |
| 49 /// The [packagesDir] URI should refer to a directory. | 48 /// The [packagesDir] URI should refer to a directory. |
| 50 /// Package names are resolved as relative to sub-directories of the | 49 /// Package names are resolved as relative to sub-directories of the |
| 51 /// package directory. | 50 /// package directory. |
| 52 /// | 51 /// |
| 53 /// This function can be used for explicitly configured package directories, | 52 /// This function can be used for explicitly configured package directories, |
| 54 /// for example one specified using a `--package-root` comand-line parameter. | 53 /// for example one specified using a `--package-root` comand-line parameter. |
| 55 Packages getPackagesDirectory(Uri packagesDir) { | 54 Packages getPackagesDirectory(Uri packagesDir) { |
| 56 if (packagesDir.scheme == "file") { | 55 if (packagesDir.scheme == "file") { |
| 57 Directory directory = new Directory.fromUri(packagesDir); | 56 Directory directory = new Directory.fromUri(packagesDir); |
| 58 return new FilePackagesDirectoryPackages(directory); | 57 return new FilePackagesDirectoryPackages(directory); |
| 59 } | 58 } |
| 60 if (!packagesDir.path.endsWith('/')) { | 59 if (!packagesDir.path.endsWith('/')) { |
| 61 packagesDir = packagesDir.replace(path: packagesDir.path + '/'); | 60 packagesDir = packagesDir.replace(path: packagesDir.path + '/'); |
| 62 } | 61 } |
| 63 return new NonFilePackagesDirectoryPackages(packagesDir); | 62 return new NonFilePackagesDirectoryPackages(packagesDir); |
| 64 } | 63 } |
| 65 | 64 |
| 66 | |
| 67 /// Discover the package configuration for a Dart script. | 65 /// Discover the package configuration for a Dart script. |
| 68 /// | 66 /// |
| 69 /// The [baseUri] points to either the Dart script or its directory. | 67 /// The [baseUri] points to either the Dart script or its directory. |
| 70 /// A package resolution strategy is found by going through the following steps, | 68 /// A package resolution strategy is found by going through the following steps, |
| 71 /// and stopping when something is found. | 69 /// and stopping when something is found. |
| 72 /// | 70 /// |
| 73 /// * Check if a `.packages` file exists in the same directory. | 71 /// * Check if a `.packages` file exists in the same directory. |
| 74 /// * If `baseUri`'s scheme is not `file`, then assume a `packages` directory | 72 /// * If `baseUri`'s scheme is not `file`, then assume a `packages` directory |
| 75 /// in the same directory, and resolve packages relative to that. | 73 /// in the same directory, and resolve packages relative to that. |
| 76 /// * If `baseUri`'s scheme *is* `file`: | 74 /// * If `baseUri`'s scheme *is* `file`: |
| 77 /// * Check if a `packages` directory exists. | 75 /// * Check if a `packages` directory exists. |
| 78 /// * Otherwise check each successive parent directory of `baseUri` for a | 76 /// * Otherwise check each successive parent directory of `baseUri` for a |
| 79 /// `.packages` file. | 77 /// `.packages` file. |
| 80 /// | 78 /// |
| 81 /// If any of these tests succeed, a `Packages` class is returned. | 79 /// If any of these tests succeed, a `Packages` class is returned. |
| 82 /// Returns the constant [noPackages] if no resolution strategy is found. | 80 /// Returns the constant [noPackages] if no resolution strategy is found. |
| 83 /// | 81 /// |
| 84 /// This function currently only supports `file`, `http` and `https` URIs. | 82 /// This function currently only supports `file`, `http` and `https` URIs. |
| 85 /// It needs to be able to load a `.packages` file from the URI, so only | 83 /// It needs to be able to load a `.packages` file from the URI, so only |
| 86 /// recognized schemes are accepted. | 84 /// recognized schemes are accepted. |
| 87 /// | 85 /// |
| 88 /// To support other schemes, or more complex HTTP requests, | 86 /// To support other schemes, or more complex HTTP requests, |
| 89 /// an optional [loader] function can be supplied. | 87 /// an optional [loader] function can be supplied. |
| 90 /// It's called to load the `.packages` file for a non-`file` scheme. | 88 /// It's called to load the `.packages` file for a non-`file` scheme. |
| 91 /// The loader function returns the *contents* of the file | 89 /// The loader function returns the *contents* of the file |
| 92 /// identified by the URI it's given. | 90 /// identified by the URI it's given. |
| 93 /// The content should be a UTF-8 encoded `.packages` file, and must return an | 91 /// The content should be a UTF-8 encoded `.packages` file, and must return an |
| 94 /// error future if loading fails for any reason. | 92 /// error future if loading fails for any reason. |
| 95 Future<Packages> findPackages(Uri baseUri, | 93 Future<Packages> findPackages(Uri baseUri, |
| 96 {Future<List<int>> loader(Uri unsupportedUri)}) { | 94 {Future<List<int>> loader(Uri unsupportedUri)}) { |
| 97 if (baseUri.scheme == "file") { | 95 if (baseUri.scheme == "file") { |
| 98 return new Future<Packages>.sync(() => findPackagesFromFile(baseUri)); | 96 return new Future<Packages>.sync(() => findPackagesFromFile(baseUri)); |
| 99 } else if (loader != null) { | 97 } else if (loader != null) { |
| 100 return findPackagesFromNonFile(baseUri, loader: loader); | 98 return findPackagesFromNonFile(baseUri, loader: loader); |
| 101 } else if (baseUri.scheme == "http" || baseUri.scheme == "https") { | 99 } else if (baseUri.scheme == "http" || baseUri.scheme == "https") { |
| 102 return findPackagesFromNonFile(baseUri, loader: _httpGet); | 100 return findPackagesFromNonFile(baseUri, loader: _httpGet); |
| 103 } else { | 101 } else { |
| 104 return new Future<Packages>.value(Packages.noPackages); | 102 return new Future<Packages>.value(Packages.noPackages); |
| 105 } | 103 } |
| 106 } | 104 } |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 /// This function first tries to locate a `.packages` file in the [nonFileUri] | 182 /// This function first tries to locate a `.packages` file in the [nonFileUri] |
| 185 /// directory. If that is not found, it instead assumes a `packages/` directory | 183 /// directory. If that is not found, it instead assumes a `packages/` directory |
| 186 /// in the same place. | 184 /// in the same place. |
| 187 /// | 185 /// |
| 188 /// By default, this function only works for `http:` and `https:` URIs. | 186 /// By default, this function only works for `http:` and `https:` URIs. |
| 189 /// To support other schemes, a loader must be provided, which is used to | 187 /// To support other schemes, a loader must be provided, which is used to |
| 190 /// try to load the `.packages` file. The loader should return the contents | 188 /// try to load the `.packages` file. The loader should return the contents |
| 191 /// of the requested `.packages` file as bytes, which will be assumed to be | 189 /// of the requested `.packages` file as bytes, which will be assumed to be |
| 192 /// UTF-8 encoded. | 190 /// UTF-8 encoded. |
| 193 Future<Packages> findPackagesFromNonFile(Uri nonFileUri, | 191 Future<Packages> findPackagesFromNonFile(Uri nonFileUri, |
| 194 {Future<List<int>> loader(Uri name)}) { | 192 {Future<List<int>> loader(Uri name)}) async { |
| 195 if (loader == null) loader = _httpGet; | 193 if (loader == null) loader = _httpGet; |
| 196 Uri packagesFileUri = nonFileUri.resolve(".packages"); | 194 Uri packagesFileUri = nonFileUri.resolve(".packages"); |
| 197 return loader(packagesFileUri).then((List<int> fileBytes) { | 195 |
| 196 try { |
| 197 List<int> fileBytes = await loader(packagesFileUri); |
| 198 Map<String, Uri> map = pkgfile.parse(fileBytes, packagesFileUri); | 198 Map<String, Uri> map = pkgfile.parse(fileBytes, packagesFileUri); |
| 199 return new MapPackages(map); | 199 return new MapPackages(map); |
| 200 }, onError: (_) { | 200 } catch (_) { |
| 201 // Didn't manage to load ".packages". Assume a "packages/" directory. | 201 // Didn't manage to load ".packages". Assume a "packages/" directory. |
| 202 Uri packagesDirectoryUri = nonFileUri.resolve("packages/"); | 202 Uri packagesDirectoryUri = nonFileUri.resolve("packages/"); |
| 203 return new NonFilePackagesDirectoryPackages(packagesDirectoryUri); | 203 return new NonFilePackagesDirectoryPackages(packagesDirectoryUri); |
| 204 }); | 204 } |
| 205 } | 205 } |
| 206 | 206 |
| 207 /// Fetches a file over http. | 207 /// Fetches a file over http. |
| 208 Future<List<int>> _httpGet(Uri uri) async { | 208 Future<List<int>> _httpGet(Uri uri) async { |
| 209 HttpClient client = new HttpClient(); | 209 HttpClient client = new HttpClient(); |
| 210 HttpClientRequest request = await client.getUrl(uri); | 210 HttpClientRequest request = await client.getUrl(uri); |
| 211 HttpClientResponse response = await request.close(); | 211 HttpClientResponse response = await request.close(); |
| 212 if (response.statusCode != HttpStatus.OK) { | 212 if (response.statusCode != HttpStatus.OK) { |
| 213 throw 'Failure getting $uri: ' | 213 throw 'Failure getting $uri: ' |
| 214 '${response.statusCode} ${response.reasonPhrase}'; | 214 '${response.statusCode} ${response.reasonPhrase}'; |
| 215 } | 215 } |
| 216 List<List<int>> splitContent = await response.toList(); | 216 List<List<int>> splitContent = await response.toList(); |
| 217 int totalLength = 0; | 217 int totalLength = 0; |
| 218 for (var list in splitContent) { | 218 for (var list in splitContent) { |
| 219 totalLength += list.length; | 219 totalLength += list.length; |
| 220 } | 220 } |
| 221 Uint8List result = new Uint8List(totalLength); | 221 Uint8List result = new Uint8List(totalLength); |
| 222 int offset = 0; | 222 int offset = 0; |
| 223 for (List<int> contentPart in splitContent) { | 223 for (List<int> contentPart in splitContent) { |
| 224 result.setRange(offset, offset + contentPart.length, contentPart); | 224 result.setRange(offset, offset + contentPart.length, contentPart); |
| 225 offset += contentPart.length; | 225 offset += contentPart.length; |
| 226 } | 226 } |
| 227 return result; | 227 return result; |
| 228 } | 228 } |
| OLD | NEW |