mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:09:48 +00:00
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:
parent
85615ce2e8
commit
e59c04ff55
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue