| // Copyright 2014 The Flutter Authors. 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:async'; | |
| import 'package:flutter_tools_core/flutter_tools_core.dart'; | |
| import 'package:flutter_tools_extension/flutter_tools_extension.dart'; | |
| import '../base/file_system.dart'; | |
| import '../base/logger.dart'; | |
| import '../cache.dart'; | |
| import '../features.dart'; | |
| import 'extension_discovery.dart'; | |
| import 'extension_manager.dart'; | |
| const Duration _kRpcTimeout = Duration(seconds: 5); | |
| /// Manages querying project templates from extension isolates. | |
| /// | |
| /// This manager interacts with active [ExtensionConnection]s to discover custom | |
| /// project templates, resolve their directories on the host, and generate | |
| /// template parameters over the extension protocol RPC. | |
| base class ExtensionTemplateManager extends TemplateService { | |
| /// Creates an [ExtensionTemplateManager]. | |
| ExtensionTemplateManager({ | |
| required this._extensionManager, | |
| required this._featureFlags, | |
| required this._fileSystem, | |
| required this._logger, | |
| }); | |
| final ExtensionManager _extensionManager; | |
| final FeatureFlags _featureFlags; | |
| final FileSystem _fileSystem; | |
| final Logger _logger; | |
| List<ProjectTemplate>? _cachedTemplates; | |
| /// Retrieve the cached templates synchronously. | |
| /// | |
| /// Returns the list of project templates cached from the last [getProjectTemplates] call. | |
| List<ProjectTemplate> get cachedTemplates => _cachedTemplates ?? const <ProjectTemplate>[]; | |
| /// Retrieve templates by routing `template.getProjectTemplates` to active tool extensions. | |
| /// | |
| /// Results are cached after the first successful call. | |
| Future<List<ProjectTemplate>> getProjectTemplates() async { | |
| if (!_featureFlags.isToolExtensionsEnabled) { | |
| return const <ProjectTemplate>[]; | |
| } | |
| if (_cachedTemplates != null) { | |
| return _cachedTemplates!; | |
| } | |
| await _extensionManager.ensureInitialized(); | |
| final templates = <ProjectTemplate>[]; | |
| final List<ExtensionConnection> connections = _extensionManager.connections | |
| .where( | |
| (ExtensionConnection c) => | |
| c.capabilities.services.contains(TemplateService.serviceNamespace), | |
| ) | |
| .toList(); | |
| final List<List<ExtensionProjectTemplate>> results = await Future.wait( | |
| connections.map((ExtensionConnection connection) async { | |
| try { | |
| final Object? rpcResult = await connection | |
| .sendRequest(TemplateService.getProjectTemplatesMethod) | |
| .timeout(_kRpcTimeout); | |
| return ExtensionProjectTemplate.listFromJson(rpcResult); | |
| } on Object catch (e) { | |
| _logger.printTrace( | |
| 'Failed to get results from extension for ${TemplateService.getProjectTemplatesMethod}: $e', | |
| ); | |
| return const <ExtensionProjectTemplate>[]; | |
| } | |
| }), | |
| ); | |
| results.forEach(templates.addAll); | |
| _cachedTemplates = templates; | |
| return templates; | |
| } | |
| /// Resolves a template package URI to a local directory. | |
| /// | |
| /// Currently only supports 'package:flutter_tools/' and | |
| /// 'package:flutter_tools_extension_linux_prototype/' URIs, resolving them | |
| /// relative to the Flutter SDK root. | |
| Directory resolveTemplateDirectory(String templatePath) { | |
| if (templatePath.startsWith('package:flutter_tools/')) { | |
| final String relativePath = templatePath.substring('package:flutter_tools/'.length); | |
| final String absolutePath = _fileSystem.path.join( | |
| Cache.flutterRoot!, | |
| 'packages', | |
| 'flutter_tools', | |
| 'lib', | |
| relativePath, | |
| ); | |
| return _fileSystem.directory(absolutePath); | |
| } | |
| // TODO(bkonyi): Resolve package URIs generically using package config or discovered paths. | |
| if (templatePath.startsWith('package:flutter_tools_extension_linux_prototype/')) { | |
| final String relativePath = templatePath.substring( | |
| 'package:flutter_tools_extension_linux_prototype/'.length, | |
| ); | |
| final String absolutePath = _fileSystem.path.join( | |
| Cache.flutterRoot!, | |
| 'packages', | |
| 'flutter_tools', | |
| 'packages', | |
| 'flutter_tools_extension_linux_prototype', | |
| 'lib', | |
| relativePath, | |
| ); | |
| return _fileSystem.directory(absolutePath); | |
| } | |
| throw ArgumentError('Unsupported template path format: $templatePath'); | |
| } | |
| /// Request template parameter generation over extension protocol RPC. | |
| /// | |
| /// Delegates the parameter generation for [templateName] to the active tool | |
| /// extension, passing the host's [toolParameters]. Falls back to returning | |
| /// [toolParameters] unchanged if the extension fails or is not available. | |
| Future<Map<String, Object?>> generateTemplateParameters( | |
| String templateName, | |
| Map<String, Object?> toolParameters, | |
| ) async { | |
| if (!_featureFlags.isToolExtensionsEnabled) { | |
| return toolParameters; | |
| } | |
| final List<ExtensionConnection> connections = _extensionManager.connections | |
| .where( | |
| (ExtensionConnection c) => | |
| c.capabilities.services.contains(TemplateService.serviceNamespace), | |
| ) | |
| .toList(); | |
| for (final connection in connections) { | |
| try { | |
| final Object? result = await connection | |
| .sendRequest(TemplateService.generateTemplateParametersMethod, <String, Object?>{ | |
| 'templateName': templateName, | |
| 'toolParameters': toolParameters, | |
| }) | |
| .timeout(_kRpcTimeout); | |
| if (result is Map) { | |
| return result.cast<String, Object?>(); | |
| } | |
| } on Object catch (e) { | |
| _logger.printTrace('Failed to generate template parameters from extension: $e'); | |
| } | |
| } | |
| return toolParameters; | |
| } | |
| @override | |
| Set<ProjectTemplate> get projectTemplates => cachedTemplates.toSet(); | |
| @override | |
| Set<String> get appPlatformTemplates => const <String>{}; | |
| @override | |
| Set<String> get pluginPlatformTemplates => const <String>{}; | |
| } |