| // 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; |
| } |
| } |