Use package:package_config to parse 'package_config.json'.

We used our implementation because package:package_config was not ready.
But it is now, and long time so, I guess :-)

Change-Id: Icdc30be682ac3b2330b67a43b7e5854e6c99b5d8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/244364
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-05-11 16:03:34 +00:00 committed by Commit Bot
parent 85615ce2e8
commit e59c04ff55
4 changed files with 4 additions and 607 deletions

View file

@ -1,269 +0,0 @@
// 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.
import 'dart:convert';
import 'package:pub_semver/pub_semver.dart';
/// Parse the content of a `package_config.json` file located at the [uri].
PackageConfigJson parsePackageConfigJson(Uri uri, String content) {
assert(uri.isAbsolute);
return _PackageConfigJsonParser(uri, content).parse();
}
class LanguageVersion {
final int major;
final int minor;
const LanguageVersion(this.major, this.minor);
@override
bool operator ==(Object other) {
return other is LanguageVersion &&
other.major == major &&
other.minor == minor;
}
@override
String toString() => '$major.$minor';
}
/// Information about packages used by a Pub package.
///
/// It represents a parsed and processed `package_config.json` file.
class PackageConfigJson {
/// The absolute URI of the file.
final Uri uri;
/// The version of the format.
final int configVersion;
/// The list of packages.
final List<PackageConfigJsonPackage> packages;
/// The timestamp for when the file was generated.
///
/// Might be `null`.
final DateTime? generated;
/// The generator which created the file, typically "pub".
///
/// Might be `null`.
final String? generator;
/// The version of the generator, if the generator wants to remember that
/// information. The version must be a Semantic Version. Pub can use the
/// SDK version.
///
/// Might be `null`.
final Version? generatorVersion;
PackageConfigJson({
required this.uri,
required this.configVersion,
required this.packages,
required this.generated,
required this.generator,
required this.generatorVersion,
});
}
/// Description of a single package in [PackageConfigJson].
class PackageConfigJsonPackage {
/// The name of the package.
final String name;
/// The root directory of the package. All files inside this directory,
/// including any subdirectories, are considered to belong to the package.
final Uri rootUri;
/// The package directory, containing files available to Dart programs
/// using `package:packageName/...` URIs. It is either the [rootUri], or a
/// sub-directory of the [rootUri].
final Uri packageUri;
/// The language version for the package, or `null` if not specified.
final LanguageVersion? languageVersion;
PackageConfigJsonPackage({
required this.name,
required this.rootUri,
required this.packageUri,
required this.languageVersion,
});
}
class _PackageConfigJsonParser {
final RegExp _languageVersionRegExp = RegExp(r'(0|[1-9]\d*)\.(0|[1-9]\d*)');
final Uri uri;
final String content;
late int version;
List<PackageConfigJsonPackage> packages = [];
DateTime? generated;
String? generator;
Version? generatorVersion;
_PackageConfigJsonParser(this.uri, this.content);
PackageConfigJson parse() {
var contentObject = json.decode(content);
if (contentObject is Map<String, dynamic>) {
_parseVersion(contentObject);
_parsePackages(contentObject);
_parseGenerated(contentObject);
return PackageConfigJson(
uri: uri,
configVersion: version,
packages: packages,
generated: generated,
generator: generator,
generatorVersion: generatorVersion,
);
} else {
throw FormatException("Expected a JSON object.", content);
}
}
T? _getOptionalField<T>(Map<String, dynamic> map, String name) {
var object = map[name];
if (object is T?) {
return object;
} else {
var actualType = object.runtimeType;
throw FormatException(
"Expected '$T' value for the '$name' field, found '$actualType'.",
content,
);
}
}
T _getRequiredField<T>(Map<String, dynamic> map, String name) {
var object = map[name];
if (object is T) {
return object;
} else if (object == null) {
throw FormatException("Missing the '$name' field.", content);
} else {
var actualType = object.runtimeType;
throw FormatException(
"Expected '$T' value for the '$name' field, found '$actualType'.",
content,
);
}
}
void _parseGenerated(Map<String, dynamic> map) {
var generatedStr = _getOptionalField<String>(map, 'generated');
if (generatedStr != null) {
generated = DateTime.parse(generatedStr);
}
generator = _getOptionalField<String>(map, 'generator');
var generatorVersionStr = _getOptionalField<String>(
map,
'generatorVersion',
);
if (generatorVersionStr != null) {
generatorVersion = Version.parse(generatorVersionStr);
}
}
void _parsePackage(Map<String, Object?> map) {
var name = _getRequiredField<String>(map, 'name');
var rootUriStr = _getRequiredField<String>(map, 'rootUri');
var rootUri = uri.resolve(rootUriStr);
rootUri = _ensureDirectoryUri(rootUri);
var packageUri = rootUri;
var packageUriStr = _getOptionalField<String>(map, 'packageUri');
if (packageUriStr != null) {
var packageUriRel = Uri.parse(packageUriStr);
if (packageUriRel.isAbsolute) {
throw FormatException(
"The value of the field 'packageUri' must be relative, "
"actually '$packageUriStr', for the package '$name'.",
content,
);
}
packageUri = rootUri.resolveUri(packageUriRel);
packageUri = _ensureDirectoryUri(packageUri);
if (!_isNestedUri(packageUri, rootUri)) {
throw FormatException(
"The resolved 'packageUri' must be inside the rootUri, "
"actually '$packageUri' is not in '$rootUri', "
"for the package '$name'.",
content,
);
}
}
var languageVersion = _parsePackageLanguageVersion(map);
packages.add(
PackageConfigJsonPackage(
name: name,
rootUri: rootUri,
packageUri: packageUri,
languageVersion: languageVersion,
),
);
}
LanguageVersion? _parsePackageLanguageVersion(Map<String, Object?> map) {
var versionStr = _getOptionalField<String>(map, 'languageVersion');
if (versionStr == null) {
return null;
}
var match = _languageVersionRegExp.matchAsPrefix(versionStr);
if (match != null && match.end == versionStr.length) {
var major = int.parse(match.group(1)!);
var minor = int.parse(match.group(2)!);
return LanguageVersion(major, minor);
} else {
throw FormatException(
"Invalid 'languageVersion' format '$versionStr'.",
content,
);
}
}
void _parsePackages(Map<String, Object?> map) {
var packagesObject = _getRequiredField<List<Object?>>(map, 'packages');
for (var packageObject in packagesObject) {
if (packageObject is Map<String, dynamic>) {
_parsePackage(packageObject);
}
}
}
void _parseVersion(Map<String, Object?> map) {
version = _getRequiredField(map, 'configVersion');
if (version != 2) {
throw FormatException("Unsupported config version: $version");
}
}
static Uri _ensureDirectoryUri(Uri uri) {
var path = uri.path;
if (path.endsWith('/')) {
return uri;
} else {
return uri.replace(path: '$path/');
}
}
/// Return `true` if the [nested] is the [enclosing], or is in it.
static bool _isNestedUri(Uri nested, Uri enclosing) {
var nestedStr = '$nested';
var enclosingStr = '$enclosing';
return nestedStr.contains(enclosingStr);
}
}

View file

@ -3,8 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/package_config_json.dart';
import 'package:analyzer/src/util/uri.dart';
import 'package:package_config/package_config_types.dart';
import 'package:package_config/src/packages_file.dart'
as package_config_packages_file;
import 'package:pub_semver/pub_semver.dart';
@ -74,7 +74,7 @@ Packages parseDotPackagesFile(ResourceProvider provider, File file) {
Packages parsePackageConfigJsonFile(ResourceProvider provider, File file) {
var uri = file.toUri();
var content = file.readAsStringSync();
var jsonConfig = parsePackageConfigJson(uri, content);
var jsonConfig = PackageConfig.parseString(content, uri);
var map = <String, Package>{};
for (var jsonPackage in jsonConfig.packages) {
@ -82,12 +82,12 @@ Packages parsePackageConfigJsonFile(ResourceProvider provider, File file) {
var rootPath = fileUriToNormalizedPath(
provider.pathContext,
jsonPackage.rootUri,
jsonPackage.root,
);
var libPath = fileUriToNormalizedPath(
provider.pathContext,
jsonPackage.packageUri,
jsonPackage.packageUriRoot,
);
Version? languageVersion;

View file

@ -1,332 +0,0 @@
// 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.
import 'package:analyzer/src/context/package_config_json.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(PackageConfigJsonTest);
});
}
@reflectiveTest
class PackageConfigJsonTest with ResourceProviderMixin {
void assertPackage(
PackageConfigJsonPackage actual, _ExpectedPackage expected) {
expect(actual.name, expected.name);
expect(actual.rootUri, toUri(expected.rootUriPath));
expect(actual.packageUri, toUri(expected.packageUriPath));
expect(actual.languageVersion, expected.languageVersion);
}
void setUp() {
newFile('/test/lib/test.dart', '');
}
test_configVersion_2() {
var config = _parse('''
{
"configVersion": 2,
"packages": []
}
''');
expect(config.configVersion, 2);
expect(config.packages, isEmpty);
}
test_configVersion_3() {
_throwsFormatException('''
{
"configVersion": 3,
"packages": []
}
''', 'config version');
}
test_configVersion_missing() {
_throwsFormatException('''
{
"packages": []
}
''', 'configVersion');
}
test_configVersion_notInt() {
_throwsFormatException('''
{
"configVersion": "2",
"packages": []
}
''', 'configVersion');
}
test_format_notMap() {
_throwsFormatException('42', 'JSON object');
}
test_generated() {
var config = _parse('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.6"
}
],
"generated": "2019-12-10T18:29:14.336160Z",
"generator": "pub",
"generatorVersion": "2.8.0-edge.28b0f1839726c0743e25a2765c5322a24f6e2afa"
}
''');
var generated = config.generated!;
expect(generated.year, 2019);
expect(generated.month, 12);
expect(generated.day, 10);
expect(generated.hour, 18);
expect(generated.minute, 29);
expect(generated.second, 14);
expect(config.generator, 'pub');
var generatorVersion = config.generatorVersion!;
expect(generatorVersion.major, 2);
expect(generatorVersion.minor, 8);
expect(generatorVersion.patch, 0);
}
test_packages() {
var config = _parse('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.6"
},
{
"name": "aaa",
"rootUri": "${toUriStr('/packages/aaa')}",
"packageUri": "lib/",
"languageVersion": "2.0"
},
{
"name": "bbb",
"rootUri": "${toUriStr('/packages/bbb/lib')}",
"languageVersion": "2.1"
}
]
}
''');
assertPackage(
config.packages[0],
_ExpectedPackage(
name: 'test',
rootUriPath: '/test/',
packageUriPath: '/test/lib/',
languageVersion: LanguageVersion(2, 6),
),
);
assertPackage(
config.packages[1],
_ExpectedPackage(
name: 'aaa',
rootUriPath: '/packages/aaa/',
packageUriPath: '/packages/aaa/lib/',
languageVersion: LanguageVersion(2, 0),
),
);
assertPackage(
config.packages[2],
_ExpectedPackage(
name: 'bbb',
rootUriPath: '/packages/bbb/lib/',
packageUriPath: '/packages/bbb/lib/',
languageVersion: LanguageVersion(2, 1),
),
);
}
test_packages_languageVersion_default() {
var config = _parse('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": "lib/"
}
]
}
''');
assertPackage(
config.packages[0],
_ExpectedPackage(
name: 'test',
rootUriPath: '/test/',
packageUriPath: '/test/lib/',
languageVersion: null,
),
);
}
test_packages_languageVersion_invalidPrefix() {
_throwsFormatException('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": " 2.6"
}
]
}
''', 'languageVersion');
}
test_packages_languageVersion_invalidSuffix() {
_throwsFormatException('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.6 "
}
]
}
''', 'languageVersion');
}
test_packages_missing() {
_throwsFormatException('''
{
"configVersion": 2
}
''', 'packages');
}
test_packages_packageUri_absolute() {
_throwsFormatException('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": "${toUriStr('/test/lib')}",
"languageVersion": "2.6"
}
]
}
''', 'packageUri');
}
test_packages_packageUri_empty() {
var config = _parse('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "../",
"packageUri": ""
}
]
}
''');
assertPackage(
config.packages[0],
_ExpectedPackage(
name: 'test',
rootUriPath: '/test/',
packageUriPath: '/test/',
languageVersion: null,
),
);
}
test_packages_packageUri_notInRootUri() {
_throwsFormatException('''
{
"configVersion": 2,
"packages": [
{
"name": "aaa",
"rootUri": "${toUriStr('/packages/aaa')}",
"packageUri": "..",
"languageVersion": "2.6"
}
]
}
''', 'packageUri');
}
test_packages_rootUri_doesNotEndWithSlash() {
var config = _parse('''
{
"configVersion": 2,
"packages": [
{
"name": "test",
"rootUri": "..",
"packageUri": "lib"
}
]
}
''');
assertPackage(
config.packages[0],
_ExpectedPackage(
name: 'test',
rootUriPath: '/test/',
packageUriPath: '/test/lib/',
languageVersion: null,
),
);
}
PackageConfigJson _parse(String content) {
var path = '/test/.dart_tool/package_config.json';
newFile(path, content);
var uri = toUri(path);
return parsePackageConfigJson(uri, content);
}
void _throwsFormatException(String content, String expectedSubString) {
try {
_parse(content);
fail('Expected to throw FormatException');
} on FormatException catch (e) {
expect(e.message, contains(expectedSubString));
}
}
}
class _ExpectedPackage {
final String name;
final String rootUriPath;
final String packageUriPath;
final LanguageVersion? languageVersion;
_ExpectedPackage({
required this.name,
required this.rootUriPath,
required this.packageUriPath,
required this.languageVersion,
});
}

View file

@ -5,14 +5,12 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'builder_test.dart' as builder_test;
import 'package_config_json_test.dart' as package_config_json_test;
import 'packages_test.dart' as packages_test;
/// Utility for manually running all tests.
main() {
defineReflectiveSuite(() {
builder_test.main();
package_config_json_test.main();
packages_test.main();
}, name: 'context');
}