| // 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. |
| |
| // dart:io dependent functionality for reading and writing configuration files. |
| |
| import "dart:convert"; |
| import "dart:io"; |
| import "dart:typed_data"; |
| |
| import "errors.dart"; |
| import "package_config_impl.dart"; |
| import "package_config_json.dart"; |
| import "packages_file.dart" as packages_file; |
| import "util.dart"; |
| import "util_io.dart"; |
| |
| /// Reads a package configuration file. |
| /// |
| /// Detects whether the [file] is a version one `.packages` file or |
| /// a version two `package_config.json` file. |
| /// |
| /// If the [file] is a `.packages` file and [preferNewest] is true, |
| /// first checks whether there is an adjacent `.dart_tool/package_config.json` |
| /// file, and if so, reads that instead. |
| /// If [preferNewset] is false, the specified file is loaded even if it is |
| /// a `.packages` file and there is an available `package_config.json` file. |
| /// |
| /// The file must exist and be a normal file. |
| Future<PackageConfig> readAnyConfigFile( |
| File file, bool preferNewest, void onError(Object error)) async { |
| if (preferNewest && fileName(file.path) == ".packages") { |
| var alternateFile = |
| File(pathJoin(dirName(file.path), ".dart_tool", "package_config.json")); |
| if (alternateFile.existsSync()) { |
| return await readPackageConfigJsonFile(alternateFile, onError); |
| } |
| } |
| Uint8List bytes; |
| try { |
| bytes = await file.readAsBytes(); |
| } catch (e) { |
| onError(e); |
| return const SimplePackageConfig.empty(); |
| } |
| return parseAnyConfigFile(bytes, file.uri, onError); |
| } |
| |
| /// Like [readAnyConfigFile] but uses a URI and an optional loader. |
| Future<PackageConfig> readAnyConfigFileUri( |
| Uri file, |
| Future<Uint8List /*?*/ > loader(Uri uri) /*?*/, |
| void onError(Object error), |
| bool preferNewest) async { |
| if (file.isScheme("package")) { |
| throw PackageConfigArgumentError( |
| file, "file", "Must not be a package: URI"); |
| } |
| if (loader == null) { |
| if (file.isScheme("file")) { |
| return await readAnyConfigFile(File.fromUri(file), preferNewest, onError); |
| } |
| loader = defaultLoader; |
| } |
| if (preferNewest && file.pathSegments.last == ".packages") { |
| var alternateFile = file.resolve(".dart_tool/package_config.json"); |
| Uint8List /*?*/ bytes; |
| try { |
| bytes = await loader(alternateFile); |
| } catch (e) { |
| onError(e); |
| return const SimplePackageConfig.empty(); |
| } |
| if (bytes != null) { |
| return parsePackageConfigBytes(bytes, alternateFile, onError); |
| } |
| } |
| Uint8List /*?*/ bytes; |
| try { |
| bytes = await loader(file); |
| } catch (e) { |
| onError(e); |
| return const SimplePackageConfig.empty(); |
| } |
| if (bytes == null) { |
| onError(PackageConfigArgumentError( |
| file.toString(), "file", "File cannot be read")); |
| return const SimplePackageConfig.empty(); |
| } |
| return parseAnyConfigFile(bytes, file, onError); |
| } |
| |
| /// Parses a `.packages` or `package_config.json` file's contents. |
| /// |
| /// Assumes it's a JSON file if the first non-whitespace character |
| /// is `{`, otherwise assumes it's a `.packages` file. |
| PackageConfig parseAnyConfigFile( |
| Uint8List bytes, Uri file, void onError(Object error)) { |
| var firstChar = firstNonWhitespaceChar(bytes); |
| if (firstChar != $lbrace) { |
| // Definitely not a JSON object, probably a .packages. |
| return packages_file.parse(bytes, file, onError); |
| } |
| return parsePackageConfigBytes(bytes, file, onError); |
| } |
| |
| Future<PackageConfig> readPackageConfigJsonFile( |
| File file, void onError(Object error)) async { |
| Uint8List bytes; |
| try { |
| bytes = await file.readAsBytes(); |
| } catch (error) { |
| onError(error); |
| return const SimplePackageConfig.empty(); |
| } |
| return parsePackageConfigBytes(bytes, file.uri, onError); |
| } |
| |
| Future<PackageConfig> readDotPackagesFile( |
| File file, void onError(Object error)) async { |
| Uint8List bytes; |
| try { |
| bytes = await file.readAsBytes(); |
| } catch (error) { |
| onError(error); |
| return const SimplePackageConfig.empty(); |
| } |
| return packages_file.parse(bytes, file.uri, onError); |
| } |
| |
| Future<void> writePackageConfigJsonFile( |
| PackageConfig config, Directory targetDirectory) async { |
| // Write .dart_tool/package_config.json first. |
| var file = |
| File(pathJoin(targetDirectory.path, ".dart_tool", "package_config.json")); |
| var baseUri = file.uri; |
| var sink = file.openWrite(encoding: utf8); |
| writePackageConfigJsonUtf8(config, baseUri, sink); |
| var doneJson = sink.close(); |
| |
| // Write .packages too. |
| file = File(pathJoin(targetDirectory.path, ".packages")); |
| baseUri = file.uri; |
| sink = file.openWrite(encoding: utf8); |
| writeDotPackages(config, baseUri, sink); |
| var donePackages = sink.close(); |
| |
| await Future.wait([doneJson, donePackages]); |
| } |