DAS plugins: Add plugin package generator

This generator will be used create shared plugin package entrypoints from
plugin configurations.

Work towards https://github.com/dart-lang/sdk/issues/53402

Change-Id: If5c6aeb7bc7845311975928fba1cb9c8d273423c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/384681
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Sam Rawlins
2024-09-11 16:54:29 +00:00
committed by Commit Queue
parent 5c1ceb0020
commit 5ee4eb52e6
8 changed files with 203 additions and 1 deletions
@@ -0,0 +1,3 @@
This directory contains sources for the "prototype" plugin system introduced
circa 2017, which is being replaced by the "new" plugin system introduced
circa 2024. The sources for the "new" plugin system are located in `../plugin2`.
@@ -0,0 +1,8 @@
This directory contains sources for the "new" plugin system introduced circa
2024, which is the replacement for the "prototype" plugin system introduced
circa 2017. The sources for the "prototype" plugin system are located in
`../plugin`.
The "new" plugin system is partially based on the "prototype" plugin system. As
the "prototype" plugin system becomes deprecated and unsupported, any features
that the "new" plugin system relies on can be migrated to this directory.
@@ -0,0 +1,79 @@
// Copyright (c) 2024, 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.
/// This class can generate various files to make up the shared plugin package.
class PluginPackageGenerator {
/// The plugin configuration, a map of plugin names to each plugin's
/// configuration.
///
/// This typically stems from plugin configuration in an analysis options
/// file.
final Map<String, Object> _pluginConfiguration;
PluginPackageGenerator(this._pluginConfiguration);
/// Generates the Dart entrpoint file which is to be spawned in a Dart
/// isolate by the analysis server.
String generateEntrypoint() {
var imports = [
"'package:analysis_server_plugin/src/plugin_server.dart'",
"'package:analyzer/file_system/physical_file_system.dart'",
"'package:analyzer_plugin/starter.dart'",
for (var name in _pluginConfiguration.keys)
"'package:$name/main.dart' as $name",
];
var buffer = StringBuffer("import 'dart:isolate';");
for (var import in imports..sort()) {
buffer.writeln('import $import;');
}
buffer.write('''
Future<void> main(List<String> args, SendPort sendPort) async {
var pluginServer = PluginServer(
resourceProvider: PhysicalResourceProvider.INSTANCE,
plugins: [
''');
// TODO(srawlins): Format with the formatter, for readability.
for (var name in _pluginConfiguration.keys) {
buffer.writeln(' $name.plugin,');
}
buffer.write('''
],
);
await startPlugin();
ServerPluginStarter(wrangler).start(sendPort);
}
''');
return buffer.toString();
}
/// Generates a pubspec file which spells out where to retreive plugin package
/// sources.
String generatePubspec() {
var buffer = StringBuffer();
buffer.write('''
name: plugin_entrypoint
version: 0.0.1
dependencies:
''');
for (var MapEntry(key: name, value: configuration)
in _pluginConfiguration.entries) {
switch (configuration) {
case String():
buffer.writeln(' $name: $configuration');
case Map<String, Object>():
if (configuration case {'path': String pathValue}) {
buffer.writeln(' $name:\n path: $pathValue');
} else if (configuration case {'git': String gitValue}) {
buffer.writeln(' $name:\n git: $gitValue');
}
}
}
return buffer.toString();
}
}
@@ -23,5 +23,5 @@ void main() {
result_collector_test.main();
result_converter_test.main();
result_merger_test.main();
});
}, name: 'plugin');
}
@@ -0,0 +1,5 @@
This directory contains tests for the "new" plugin system introduced circa 2024,
which is the replacement for the "prototype" plugin system introduced circa
2017. The tests for the "prototype" plugin are located in `../plugin`.
Eventually, all tests in `../plugin` which test some equivalent behavior in the
"new" plugin system should be migrated here.
@@ -0,0 +1,92 @@
// Copyright (c) 2024, 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:analysis_server/src/plugin2/generator.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(GeneratorTest);
});
}
@reflectiveTest
class GeneratorTest {
void test_entrypointImportsPluginEntrypoints() {
var pluginPackageGenerator = PluginPackageGenerator({
'no_bools': '^1.0.0',
'no_ints': '^1.2.0',
});
expect(
pluginPackageGenerator.generateEntrypoint(),
contains('''
import 'package:no_bools/main.dart' as no_bools;
import 'package:no_ints/main.dart' as no_ints;
'''),
);
}
void test_entrypointListsPluginInstances() {
var pluginPackageGenerator = PluginPackageGenerator({
'no_bools': '^1.0.0',
'no_ints': '^1.2.0',
});
expect(
pluginPackageGenerator.generateEntrypoint(),
contains('''
plugins: [
no_bools.plugin,
no_ints.plugin,
],
'''),
);
}
void test_pubspecContainsGitDependencies() {
var pluginPackageGenerator = PluginPackageGenerator({
'no_bools': {'git': 'https://example.com/example.git'},
});
expect(
pluginPackageGenerator.generatePubspec(),
contains('''
dependencies:
no_bools:
git: https://example.com/example.git
'''),
);
}
void test_pubspecContainsPathDependencies() {
var pluginPackageGenerator = PluginPackageGenerator({
'no_bools': {'path': '../no_bools_plugin'},
'no_ints': {'path': 'tools/no_ints_plugin'},
});
expect(
pluginPackageGenerator.generatePubspec(),
contains('''
dependencies:
no_bools:
path: ../no_bools_plugin
no_ints:
path: tools/no_ints_plugin
'''),
);
}
void test_pubspecContainsVersionedDependencies() {
var pluginPackageGenerator = PluginPackageGenerator({
'no_bools': '^1.0.0',
'no_ints': '^1.2.0',
});
expect(
pluginPackageGenerator.generatePubspec(),
contains('''
dependencies:
no_bools: ^1.0.0
no_ints: ^1.2.0
'''),
);
}
}
@@ -0,0 +1,13 @@
// Copyright (c) 2024, 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:test_reflective_loader/test_reflective_loader.dart';
import 'generator_test.dart' as generator_test;
void main() {
defineReflectiveSuite(() {
generator_test.main();
}, name: 'plugin2');
}
@@ -12,6 +12,7 @@ import 'flutter/test_all.dart' as flutter;
import 'g3/test_all.dart' as g3;
import 'lsp/test_all.dart' as lsp;
import 'plugin/test_all.dart' as plugin;
import 'plugin2/test_all.dart' as plugin2;
import 'server/test_all.dart' as server;
import 'services/test_all.dart' as services;
import 'utilities/test_all.dart' as utilities;
@@ -26,6 +27,7 @@ void main() {
g3.main();
lsp.main();
plugin.main();
plugin2.main();
server.main();
services.main();
utilities.main();