// 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. /// A package configuration is a way to assign file paths to package URIs, /// and vice-versa. /// /// This package provides functionality to find, read and write package /// configurations in the [specified format](https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/package-config-file-v2.md). library; import 'dart:io' show Directory, File; import 'dart:typed_data' show Uint8List; import 'src/discovery.dart' as discover; import 'src/errors.dart' show throwError; import 'src/package_config.dart'; import 'src/package_config_io.dart'; export 'package_config_types.dart'; /// Reads a specific package configuration file. /// /// The file must exist and be readable. /// It must be either a valid `package_config.json` file /// or a valid `.packages` file. /// It is considered a `package_config.json` file if its first character /// is a `{`. /// /// If the file is a `.packages` file (the file name is `.packages`) /// and [preferNewest] is true, the default, also checks if there is /// a `.dart_tool/package_config.json` file next /// to the original file, and if so, loads that instead. /// If [preferNewest] is set to false, a directly specified `.packages` file /// is loaded even if there is an available `package_config.json` file. /// The caller can determine this from the [PackageConfig.version] /// being 1 and look for a `package_config.json` file themselves. /// /// If [onError] is provided, the configuration file parsing will report errors /// by calling that function, and then try to recover. /// The returned package configuration is a *best effort* attempt to create /// a valid configuration from the invalid configuration file. /// If no [onError] is provided, errors are thrown immediately. Future loadPackageConfig(File file, {bool preferNewest = true, void Function(Object error)? onError}) => readAnyConfigFile(file, preferNewest, onError ?? throwError); /// Reads a specific package configuration URI. /// /// The file of the URI must exist and be readable. /// It must be either a valid `package_config.json` file /// or a valid `.packages` file. /// It is considered a `package_config.json` file if its first /// non-whitespace character is a `{`. /// /// If [preferNewest] is true, the default, and the file is a `.packages` file, /// as determined by its file name being `.packages`, /// first checks if there is a `.dart_tool/package_config.json` file /// next to the original file, and if so, loads that instead. /// The [file] *must not* be a `package:` URI. /// If [preferNewest] is set to false, a directly specified `.packages` file /// is loaded even if there is an available `package_config.json` file. /// The caller can determine this from the [PackageConfig.version] /// being 1 and look for a `package_config.json` file themselves. /// /// If [loader] is provided, URIs are loaded using that function. /// The future returned by the loader must complete with a [Uint8List] /// containing the entire file content encoded as UTF-8, /// or with `null` if the file does not exist. /// The loader may throw at its own discretion, for situations where /// it determines that an error might be need user attention, /// but it is always allowed to return `null`. /// This function makes no attempt to catch such errors. /// As such, it may throw any error that [loader] throws. /// /// If no [loader] is supplied, a default loader is used which /// only accepts `file:`, `http:` and `https:` URIs, /// and which uses the platform file system and HTTP requests to /// fetch file content. The default loader never throws because /// of an I/O issue, as long as the location URIs are valid. /// As such, it does not distinguish between a file not existing, /// and it being temporarily locked or unreachable. /// /// If [onError] is provided, the configuration file parsing will report errors /// by calling that function, and then try to recover. /// The returned package configuration is a *best effort* attempt to create /// a valid configuration from the invalid configuration file. /// If no [onError] is provided, errors are thrown immediately. Future loadPackageConfigUri(Uri file, {Future Function(Uri uri)? loader, bool preferNewest = true, void Function(Object error)? onError}) => readAnyConfigFileUri(file, loader, onError ?? throwError, preferNewest); /// Finds a package configuration relative to [directory]. /// /// If [directory] contains a package configuration, /// either a `.dart_tool/package_config.json` file or, /// if not, a `.packages`, then that file is loaded. /// /// If no file is found in the current directory, /// then the parent directories are checked recursively, /// all the way to the root directory, to check if those contains /// a package configuration. /// If [recurse] is set to `false`, this parent directory check is not /// performed. /// /// If [onError] is provided, the configuration file parsing will report errors /// by calling that function, and then try to recover. /// The returned package configuration is a *best effort* attempt to create /// a valid configuration from the invalid configuration file. /// If no [onError] is provided, errors are thrown immediately. /// /// If [minVersion] is set to something greater than its default, /// any lower-version configuration files are ignored in the search. /// /// Returns `null` if no configuration file is found. Future findPackageConfig(Directory directory, {bool recurse = true, void Function(Object error)? onError, int minVersion = 1}) { if (minVersion > PackageConfig.maxVersion) { throw ArgumentError.value(minVersion, 'minVersion', 'Maximum known version is ${PackageConfig.maxVersion}'); } return discover.findPackageConfig( directory, minVersion, recurse, onError ?? throwError); } /// Finds a package configuration relative to [location]. /// /// If [location] contains a package configuration, /// either a `.dart_tool/package_config.json` file or, /// if not, a `.packages`, then that file is loaded. /// The [location] URI *must not* be a `package:` URI. /// It should be a hierarchical URI which is supported /// by [loader]. /// /// If no file is found in the current directory, /// then the parent directories are checked recursively, /// all the way to the root directory, to check if those contains /// a package configuration. /// If [recurse] is set to `false`, this parent directory check is not /// performed. /// /// If [loader] is provided, URIs are loaded using that function. /// The future returned by the loader must complete with a [Uint8List] /// containing the entire file content, /// or with `null` if the file does not exist. /// The loader may throw at its own discretion, for situations where /// it determines that an error might be need user attention, /// but it is always allowed to return `null`. /// This function makes no attempt to catch such errors. /// /// If no [loader] is supplied, a default loader is used which /// only accepts `file:`, `http:` and `https:` URIs, /// and which uses the platform file system and HTTP requests to /// fetch file content. The default loader never throws because /// of an I/O issue, as long as the location URIs are valid. /// As such, it does not distinguish between a file not existing, /// and it being temporarily locked or unreachable. /// /// If [onError] is provided, the configuration file parsing will report errors /// by calling that function, and then try to recover. /// The returned package configuration is a *best effort* attempt to create /// a valid configuration from the invalid configuration file. /// If no [onError] is provided, errors are thrown immediately. /// /// If [minVersion] is set to something greater than its default, /// any lower-version configuration files are ignored in the search. /// /// Returns `null` if no configuration file is found. Future findPackageConfigUri(Uri location, {bool recurse = true, int minVersion = 1, Future Function(Uri uri)? loader, void Function(Object error)? onError}) { if (minVersion > PackageConfig.maxVersion) { throw ArgumentError.value(minVersion, 'minVersion', 'Maximum known version is ${PackageConfig.maxVersion}'); } return discover.findPackageConfigUri( location, minVersion, loader, onError ?? throwError, recurse); } /// Writes a package configuration to the provided directory. /// /// Writes `.dart_tool/package_config.json` relative to [directory]. /// If the `.dart_tool/` directory does not exist, it is created. /// If it cannot be created, this operation fails. /// /// Also writes a `.packages` file in [directory]. /// This will stop happening eventually as the `.packages` file becomes /// discontinued. /// A comment is generated if `[PackageConfig.extraData]` contains a /// `"generator"` entry. Future savePackageConfig( PackageConfig configuration, Directory directory) => writePackageConfigJsonFile(configuration, directory);