blob: 0a71b65c6a2f0683d16729e30530ecfb605dec57 [file] [log] [blame]
// Copyright (c) 2022, 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:analyzer/file_system/memory_file_system.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;
import 'package:yaml/yaml.dart' as yaml;
export 'package:test_descriptor/test_descriptor.dart';
/// Builds and returns the content of a pubspec file.
String buildPubspecText({
String sdkConstraint = '>=3.2.0 <4.0.0',
Map<String, String> dependencies = const {},
}) {
var buffer = StringBuffer('''
name: test_package
version: 0.0.1
environment:
sdk: '$sdkConstraint'
''');
if (dependencies.isNotEmpty) {
buffer.writeln('dependencies:');
dependencies.forEach((name, path) {
buffer.writeln(' $name:');
buffer.writeln(' path: $path');
});
}
return buffer.toString();
}
/// Creates a pub package in a directory named [name].
///
/// If [resourceProvider] is given, then the pub package is created in the file
/// system provided by [resourceProvider]; otherwise it is created in the
/// physical file system, under [d.sandbox].
Future<String> createPackage(
String name, {
String? pubspec,
String? dartdocOptions,
String? analysisOptions,
List<d.Descriptor> libFiles = const [],
List<d.Descriptor> files = const [],
MemoryResourceProvider? resourceProvider,
}) async {
pubspec ??= buildPubspecText();
final parsedYaml = yaml.loadYaml(pubspec) as Map;
final packageName = parsedYaml['name'];
final versionConstraint = (parsedYaml['environment'] as Map)['sdk'] as String;
final languageVersion = RegExp(r'>=(\S*)\.0(-0)?(-0.0-dev)? ')
.firstMatch(versionConstraint)!
.group(1);
final packagesInfo = StringBuffer('''{
"name": "$packageName",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "$languageVersion"
}''');
if (parsedYaml.containsKey('dependencies')) {
final dependencies = parsedYaml['dependencies'] as Map;
for (var dep in dependencies.keys) {
// This only accepts 'path' deps.
final depConfig = dependencies[dep];
if (depConfig is! Map) {
throw StateError('dep in pubspec must be a Map, but is: "$depConfig"');
}
final pathDep = depConfig['path'];
packagesInfo.writeln(''',{
"name": "$dep",
"rootUri": "../$pathDep",
"packageUri": "lib/"
}''');
}
}
final packageDir = d.dir(name, [
d.file('pubspec.yaml', pubspec),
if (dartdocOptions != null) d.file('dartdoc_options.yaml', dartdocOptions),
if (analysisOptions != null)
d.file('analysis_options.yaml', analysisOptions),
d.dir('lib', [...libFiles]),
...files,
// Write out '.dart_tool/package_config.json' to avoid needing `pub get`.
d.dir(
'.dart_tool',
[
d.file('package_config.json', '''
{
"configVersion": 2,
"packages": [
$packagesInfo
],
"generated": "2024-02-14T20:36:04.604099Z",
"generator": "pub",
"generatorVersion": "3.2.0"
}
''')
],
),
]);
if (resourceProvider == null) {
await packageDir.create();
return packageDir.io.path;
} else {
return await packageDir.createInMemory(resourceProvider);
}
}
extension DescriptorExtensions on d.Descriptor {
/// Creates this [d.Descriptor] in the [MemoryResourceProvider].
///
/// For a [d.DirectoryDescriptor], the subtree will be created. For a
/// [d.FileDescriptor], the file contents will be written.
Future<String> createInMemory(MemoryResourceProvider resourceProvider,
[String? parent]) {
var self = this;
return switch (self) {
d.DirectoryDescriptor() => self.createInMemory(resourceProvider, parent),
d.FileDescriptor() => self.createInMemory(resourceProvider, parent!),
_ => throw StateError(
'$runtimeType is not a DirectoryDescriptor, nor a FileDescriptor!')
};
}
}
extension on d.DirectoryDescriptor {
Future<String> createInMemory(MemoryResourceProvider resourceProvider,
[String? parent]) async {
parent ??= resourceProvider.pathContext
.canonicalize(resourceProvider.convertPath('/temp'));
resourceProvider.newFolder(parent).create();
var fullPath = resourceProvider.pathContext.join(parent, name);
resourceProvider.newFolder(fullPath).create();
for (var e in contents) {
await e.createInMemory(resourceProvider, fullPath);
}
return fullPath;
}
}
extension on d.FileDescriptor {
Future<String> createInMemory(
MemoryResourceProvider resourceProvider, String parent) async {
var content = await readAsBytes().transform(utf8.decoder).join('');
var fullPath = resourceProvider
.convertPath(resourceProvider.pathContext.join(parent, name));
resourceProvider.newFile(fullPath, content);
return fullPath;
}
}