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