diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd45d0..0b5e156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.9.2 + +- Updated to support new rules for picking `package_config.json` over + a specified `.packages`. + ## 1.9.1 - Remove accidental transitive import of `dart:io` from entrypoints that are diff --git a/lib/package_config.dart b/lib/package_config.dart index bca865d..1113ac8 100644 --- a/lib/package_config.dart +++ b/lib/package_config.dart @@ -24,9 +24,10 @@ export "package_config_types.dart"; /// It is considered a `package_config.json` file if its first character /// is a `{`. /// -/// If the file is a `.packages` file 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 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] @@ -50,6 +51,7 @@ Future loadPackageConfig(File file, /// 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. diff --git a/lib/src/package_config_io.dart b/lib/src/package_config_io.dart index 954be6b..31bc1cc 100644 --- a/lib/src/package_config_io.dart +++ b/lib/src/package_config_io.dart @@ -30,6 +30,13 @@ import "util_io.dart"; /// The file must exist and be a normal file. Future 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(); @@ -37,28 +44,7 @@ Future readAnyConfigFile( onError(e); return const SimplePackageConfig.empty(); } - var firstChar = firstNonWhitespaceChar(bytes); - if (firstChar != $lbrace) { - // Definitely not a JSON object, probably a .packages. - if (preferNewest) { - var alternateFile = File( - pathJoin(dirName(file.path), ".dart_tool", "package_config.json")); - if (alternateFile.existsSync()) { - Uint8List /*?*/ bytes; - try { - bytes = await alternateFile.readAsBytes(); - } catch (e) { - onError(e); - return const SimplePackageConfig.empty(); - } - if (bytes != null) { - return parsePackageConfigBytes(bytes, alternateFile.uri, onError); - } - } - } - return packages_file.parse(bytes, file.uri, onError); - } - return parsePackageConfigBytes(bytes, file.uri, onError); + return parseAnyConfigFile(bytes, file.uri, onError); } /// Like [readAnyConfigFile] but uses a URI and an optional loader. @@ -73,11 +59,24 @@ Future readAnyConfigFileUri( } if (loader == null) { if (file.isScheme("file")) { - return readAnyConfigFile(File.fromUri(file), preferNewest, onError); + return await readAnyConfigFile(File.fromUri(file), preferNewest, onError); } loader = defaultLoader; } - Uint8List bytes; + 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) { @@ -89,23 +88,18 @@ Future readAnyConfigFileUri( 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. - if (preferNewest) { - // Check if there is a package_config.json file. - var alternateFile = file.resolveUri(packageConfigJsonPath); - Uint8List alternateBytes; - try { - alternateBytes = await loader(alternateFile); - } catch (e) { - onError(e); - return const SimplePackageConfig.empty(); - } - if (alternateBytes != null) { - return parsePackageConfigBytes(alternateBytes, alternateFile, onError); - } - } return packages_file.parse(bytes, file, onError); } return parsePackageConfigBytes(bytes, file, onError); diff --git a/lib/src/package_config_json.dart b/lib/src/package_config_json.dart index b9b3416..27abf50 100644 --- a/lib/src/package_config_json.dart +++ b/lib/src/package_config_json.dart @@ -39,7 +39,7 @@ PackageConfig parsePackageConfigBytes( try { jsonObject = _jsonUtf8Decoder.convert(bytes); } on FormatException catch (e) { - onError(PackageConfigFormatException(e.message, e.source, e.offset)); + onError(PackageConfigFormatException.from(e)); return const SimplePackageConfig.empty(); } return parsePackageConfigJson(jsonObject, file, onError); @@ -51,7 +51,7 @@ PackageConfig parsePackageConfigString( try { jsonObject = jsonDecode(source); } on FormatException catch (e) { - onError(PackageConfigFormatException(e.message, e.source, e.offset)); + onError(PackageConfigFormatException.from(e)); return const SimplePackageConfig.empty(); } return parsePackageConfigJson(jsonObject, file, onError); @@ -271,7 +271,6 @@ void writeDotPackages(PackageConfig config, Uri baseUri, StringSink output) { } } packages_file.write(output, config, baseUri: baseUri, comment: comment); - return; } /// If "extraData" is a JSON map, then return it, otherwise return null. @@ -304,12 +303,10 @@ bool _validateJson(dynamic object) { if (object == null || true == object || false == object) return true; if (object is num || object is String) return true; if (object is List) { - for (var element in object) if (!_validateJson(element)) return false; - return true; + return object.every(_validateJson); } if (object is Map) { - for (var value in object.values) if (!_validateJson(value)) return false; - return true; + return object.values.every(_validateJson); } return false; } diff --git a/lib/src/util_io.dart b/lib/src/util_io.dart index 7f21a8d..2aa8c94 100644 --- a/lib/src/util_io.dart +++ b/lib/src/util_io.dart @@ -13,7 +13,7 @@ Future defaultLoader(Uri uri) async { if (uri.isScheme("file")) { var file = File.fromUri(uri); try { - return file.readAsBytes(); + return await file.readAsBytes(); } catch (_) { return null; } diff --git a/pubspec.yaml b/pubspec.yaml index 853c051..3f5ec27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: package_config -version: 1.9.1 +version: 1.9.2 description: Support for working with Package Configuration files. homepage: https://github.com/dart-lang/package_config diff --git a/test/discovery_test.dart b/test/discovery_test.dart index 1a9a61c..5cbc992 100644 --- a/test/discovery_test.dart +++ b/test/discovery_test.dart @@ -301,13 +301,13 @@ void main() { }); // Find package_config.json in subdir even if initial file syntax error. - fileTest("specified file syntax error", { - "anyname": "syntax error", + fileTest("specified file syntax onError", { + ".packages": "syntax error", ".dart_tool": { "package_config.json": packageConfigFile, }, }, (Directory directory) async { - var file = dirFile(directory, "anyname"); + var file = dirFile(directory, ".packages"); var config = await loadPackageConfig(file); expect(config.version, 2); validatePackagesFile(config, directory); diff --git a/test/discovery_uri_test.dart b/test/discovery_uri_test.dart index 52fca3f..23c02d7 100644 --- a/test/discovery_uri_test.dart +++ b/test/discovery_uri_test.dart @@ -60,7 +60,7 @@ void main() { ".dart_tool": { "package_config.json": packageConfigFile, } - }, (Uri directory, loader) async { + }, (directory, loader) async { var config = await findPackageConfigUri(directory, loader: loader); expect(config.version, 2); // Found package_config.json file. validatePackagesFile(config, directory); @@ -71,7 +71,7 @@ void main() { ".packages": packagesFile, "script.dart": "main(){}", "packages": {"shouldNotBeFound": {}} - }, (Uri directory, loader) async { + }, (directory, loader) async { var config = await findPackageConfigUri(directory, loader: loader); expect(config.version, 1); // Found .packages file. validatePackagesFile(config, directory); @@ -86,7 +86,7 @@ void main() { "subdir": { "script.dart": "main(){}", } - }, (Uri directory, loader) async { + }, (directory, loader) async { var config = await findPackageConfigUri(directory.resolve("subdir/"), loader: loader); expect(config.version, 2); @@ -97,7 +97,7 @@ void main() { loaderTest(".packages recursive", { ".packages": packagesFile, "subdir": {"script.dart": "main(){}"} - }, (Uri directory, loader) async { + }, (directory, loader) async { var config; config = await findPackageConfigUri(directory.resolve("subdir/"), loader: loader); @@ -240,9 +240,9 @@ void main() { throwsFormatException); }); - loaderTest("specified file syntax error", { + loaderTest("specified file syntax onError", { "anyname": "syntax error", - }, (Uri directory, loader) async { + }, (directory, loader) async { var file = directory.resolve("anyname"); var hadError = false; await loadPackageConfigUri(file, @@ -254,23 +254,22 @@ void main() { expect(hadError, true); }); - // Find package_config.json in subdir even if initial file syntax error. - loaderTest("specified file syntax error", { + // Don't look for package_config.json if original file not named .packages. + loaderTest("specified file syntax error with alternative", { "anyname": "syntax error", ".dart_tool": { "package_config.json": packageConfigFile, }, - }, (Uri directory, loader) async { + }, (directory, loader) async { var file = directory.resolve("anyname"); - var config = await loadPackageConfigUri(file, loader: loader); - expect(config.version, 2); - validatePackagesFile(config, directory); + expect(() => loadPackageConfigUri(file, loader: loader), + throwsFormatException); }); // A file starting with `{` is a package_config.json file. loaderTest("file syntax error with {", { ".packages": "{syntax error", - }, (Uri directory, loader) async { + }, (directory, loader) async { var file = directory.resolve(".packages"); var hadError = false; await loadPackageConfigUri(file,