[tool][web] Create an early web plugin_registrant for dartpad. (#106921)
diff --git a/packages/flutter_tools/lib/src/flutter_plugins.dart b/packages/flutter_tools/lib/src/flutter_plugins.dart
index 8950364..8e7962e 100644
--- a/packages/flutter_tools/lib/src/flutter_plugins.dart
+++ b/packages/flutter_tools/lib/src/flutter_plugins.dart
@@ -1106,6 +1106,11 @@
///
/// In the Web platform, `destination` can point to a real filesystem (`flutter build`)
/// or an in-memory filesystem (`flutter run`).
+///
+/// This method is also used by [WebProject.ensureReadyForPlatformSpecificTooling]
+/// to inject a copy of the plugin registrant for web into .dart_tool/dartpad so
+/// dartpad can get the plugin registrant without needing to build the complete
+/// project. See: https://github.com/dart-lang/dart-services/pull/874
Future<void> injectBuildTimePluginFiles(
FlutterProject project, {
required Directory destination,
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 9f68a66..813a469 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -734,7 +734,20 @@
.childDirectory('web')
.childFile('index.html');
- Future<void> ensureReadyForPlatformSpecificTooling() async {}
+ /// The .dart_tool/dartpad directory
+ Directory get dartpadToolDirectory => parent.directory
+ .childDirectory('.dart_tool')
+ .childDirectory('dartpad');
+
+ Future<void> ensureReadyForPlatformSpecificTooling() async {
+ /// Create .dart_tool/dartpad/web_plugin_registrant.dart.
+ /// See: https://github.com/dart-lang/dart-services/pull/874
+ await injectBuildTimePluginFiles(
+ parent,
+ destination: dartpadToolDirectory,
+ webPlatform: true,
+ );
+ }
}
/// The Fuchsia sub project.
diff --git a/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart b/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart
index 562871b..314c69b 100644
--- a/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart
+++ b/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart
@@ -43,11 +43,10 @@
testUsingContext('generated plugin registrant passes analysis', () async {
await _createProject(projectDir, <String>[]);
- // We need to add a dependency with web support to trigger
- // the generated_plugin_registrant generation.
+ // We need a dependency so the plugin registrant is not completely empty.
await _addDependency(projectDir, 'shared_preferences',
version: '^2.0.0');
- // The plugin registrant is only created after a build...
+ // The plugin registrant is created on build...
await _buildWebProject(projectDir);
// Find the web_plugin_registrant, now that it lives outside "lib":
@@ -56,11 +55,77 @@
.listSync()
.firstWhere((FileSystemEntity entity) => entity is Directory) as Directory;
- expect(
- buildDir.childFile('web_plugin_registrant.dart'),
- exists,
- );
- await _analyzeEntity(buildDir.childFile('web_plugin_registrant.dart'));
+ // Ensure the file exists, and passes analysis.
+ final File registrant = buildDir.childFile('web_plugin_registrant.dart');
+ expect(registrant, exists);
+ await _analyzeEntity(registrant);
+
+ // Ensure the contents match what we expect for a non-empty plugin registrant.
+ final String contents = registrant.readAsStringSync();
+ expect(contents, contains("import 'package:shared_preferences_web/shared_preferences_web.dart';"));
+ expect(contents, contains('void registerPlugins([final Registrar? pluginRegistrar]) {'));
+ expect(contents, contains('SharedPreferencesPlugin.registerWith(registrar);'));
+ expect(contents, contains('registrar.registerMessageHandler();'));
+ }, overrides: <Type, Generator>{
+ Pub: () => Pub(
+ fileSystem: globals.fs,
+ logger: globals.logger,
+ processManager: globals.processManager,
+ usage: globals.flutterUsage,
+ botDetector: globals.botDetector,
+ platform: globals.platform,
+ ),
+ });
+
+ testUsingContext('(no-op) generated plugin registrant passes analysis', () async {
+ await _createProject(projectDir, <String>[]);
+ // No dependencies on web plugins this time!
+ await _buildWebProject(projectDir);
+
+ // Find the web_plugin_registrant, now that it lives outside "lib":
+ final Directory buildDir = projectDir
+ .childDirectory('.dart_tool/flutter_build')
+ .listSync()
+ .firstWhere((FileSystemEntity entity) => entity is Directory) as Directory;
+
+ // Ensure the file exists, and passes analysis.
+ final File registrant = buildDir.childFile('web_plugin_registrant.dart');
+ expect(registrant, exists);
+ await _analyzeEntity(registrant);
+
+ // Ensure the contents match what we expect for an empty (noop) plugin registrant.
+ final String contents = registrant.readAsStringSync();
+ expect(contents, contains('void registerPlugins() {}'));
+ }, overrides: <Type, Generator>{
+ Pub: () => Pub(
+ fileSystem: globals.fs,
+ logger: globals.logger,
+ processManager: globals.processManager,
+ usage: globals.flutterUsage,
+ botDetector: globals.botDetector,
+ platform: globals.platform,
+ ),
+ });
+
+ // See: https://github.com/dart-lang/dart-services/pull/874
+ testUsingContext('generated plugin registrant for dartpad is created on pub get', () async {
+ await _createProject(projectDir, <String>[]);
+ await _addDependency(projectDir, 'shared_preferences',
+ version: '^2.0.0');
+ // The plugin registrant for dartpad is created on flutter pub get.
+ await _doFlutterPubGet(projectDir);
+
+ final File registrant = projectDir
+ .childDirectory('.dart_tool/dartpad')
+ .childFile('web_plugin_registrant.dart');
+
+ // Ensure the file exists, and passes analysis.
+ expect(registrant, exists);
+ await _analyzeEntity(registrant);
+
+ // Assert the full build hasn't happened!
+ final Directory buildDir = projectDir.childDirectory('.dart_tool/flutter_build');
+ expect(buildDir, isNot(exists));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
@@ -258,6 +323,18 @@
}
Future<void> _buildWebProject(Directory workingDir) async {
+ return _runFlutterSnapshot(<String>['build', 'web'], workingDir);
+}
+
+Future<void> _doFlutterPubGet(Directory workingDir) async {
+ return _runFlutterSnapshot(<String>['pub', 'get'], workingDir);
+}
+
+// Runs a flutter command from a snapshot build.
+// `flutterCommandArgs` are the arguments passed to flutter, like: ['build', 'web']
+// to run `flutter build web`.
+// `workingDir` is the directory on which the flutter command will be run.
+Future<void> _runFlutterSnapshot(List<String> flutterCommandArgs, Directory workingDir) async {
final String flutterToolsSnapshotPath = globals.fs.path.absolute(
globals.fs.path.join(
'..',
@@ -270,8 +347,7 @@
final List<String> args = <String>[
flutterToolsSnapshotPath,
- 'build',
- 'web',
+ ...flutterCommandArgs
];
final ProcessResult exec = await Process.run(
@@ -279,7 +355,7 @@
args,
workingDirectory: workingDir.path,
);
- printOnFailure('Output of flutter build web:');
+ printOnFailure('Output of flutter ${flutterCommandArgs.join(" ")}:');
printOnFailure(exec.stdout.toString());
printOnFailure(exec.stderr.toString());
expect(exec.exitCode, 0);