Skip to content

Commit cecf6eb

Browse files
authored
fix flutter plugins and embedded mode (#719)
1 parent 3c20d1e commit cecf6eb

8 files changed

Lines changed: 132 additions & 76 deletions

File tree

examples/flutter_plugin_interop/lib/main.client.options.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ import 'package:shared_preferences_web/shared_preferences_web.dart'
3333
ClientOptions get defaultClientOptions => ClientOptions(
3434
initialize: () {
3535
final Registrar registrar = webPluginRegistrar;
36-
_shared_preferences_web.SharedPreferencesPlugin.registerWith(registrar);
37-
_firebase_core_web.FirebaseCoreWeb.registerWith(registrar);
38-
_firebase_auth_web.FirebaseAuthWeb.registerWith(registrar);
3936
_cloud_firestore_web.FirebaseFirestoreWeb.registerWith(registrar);
37+
_firebase_auth_web.FirebaseAuthWeb.registerWith(registrar);
38+
_firebase_core_web.FirebaseCoreWeb.registerWith(registrar);
39+
_shared_preferences_web.SharedPreferencesPlugin.registerWith(registrar);
4040
registrar.registerMessageHandler();
4141
},
4242
clients: {'app': ClientLoader((p) => _app.App(), loader: _app.loadLibrary)},

packages/jaspr/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Unreleased patch
22

3+
- Fixed bug when using `flutter: plugins` or `flutter: embedded` on Windows.
4+
- Fixed generation of `*.client.options.dart` file in client mode.
5+
- Improved detection of Flutter plugins by reading the generated `.flutter-plugins-dependencies` file.
36
- Fixed bug preventing event handlers from being updated in rebuilds.
47

58
## 0.22.0

packages/jaspr/lib/src/server/adapters/client_component_adapter.dart

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import '../../dom/validator.dart';
2+
import '../../foundation/constants.dart';
23
import '../../framework/framework.dart';
34
import '../markup_render_object.dart';
45
import '../options.dart';
@@ -35,7 +36,7 @@ class ClientComponentRegistry extends ObserverComponent {
3536
class ClientComponentRegistryElement extends ObserverElement {
3637
ClientComponentRegistryElement(super.component);
3738

38-
bool _didAddClientScript = false;
39+
bool _didHandleClientScript = false;
3940
final List<Element> _clientElements = [];
4041
final List<ClientTarget> _clientTargets = [];
4142

@@ -46,9 +47,13 @@ class ClientComponentRegistryElement extends ObserverElement {
4647
void didRebuildElement(Element element) {
4748
final binding = this.binding as ServerAppBinding;
4849

49-
if (!_didAddClientScript && binding.options.clientId != null) {
50-
(binding).addRenderAdapter(ClientScriptAdapter(binding.options.clientId!));
51-
_didAddClientScript = true;
50+
if (!_didHandleClientScript) {
51+
if (binding.options.clientId != null) {
52+
(binding).addRenderAdapter(ClientScriptAdapter(binding.options.clientId!));
53+
} else if (kDebugMode) {
54+
(binding).addRenderAdapter(NoClientScriptAdapter());
55+
}
56+
_didHandleClientScript = true;
5257
}
5358

5459
final entry = binding.options.clients?[element.component.runtimeType];

packages/jaspr/lib/src/server/adapters/client_script_adapter.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:io';
2+
13
import '../markup_render_object.dart';
24
import 'head_scope_adapter.dart';
35

@@ -23,3 +25,34 @@ class ClientScriptAdapter extends HeadScopeAdapter {
2325
return true;
2426
}
2527
}
28+
29+
class NoClientScriptAdapter extends HeadScopeAdapter {
30+
static bool _didOutputWarning = false;
31+
32+
@override
33+
bool applyHead(MarkupRenderObject head) {
34+
final script = head.children.findWhere<MarkupRenderElement>(
35+
(c) => c.tag == 'script' && c.attributes?['src']?.endsWith('.client.dart.js') == true,
36+
);
37+
38+
if (script != null) {
39+
_didOutputWarning = false;
40+
return false;
41+
}
42+
43+
if (_didOutputWarning) {
44+
return false;
45+
}
46+
47+
stdout.writeln(
48+
'[WARNING] Could not find a respective \'.client.dart\' file for the current server entrypoint, and no client '
49+
'script was specified\nin the document\'s <head>. This may lead to missing client-side functionality. To fix '
50+
'this, either:\n'
51+
' - ensure that a \'.client.dart\' file with the same name as the \'.server.dart\' entrypoint exists, or\n'
52+
' - add a `script(src: \'<filename>.client.dart.js\', defer: true)` to `Document(head: [...])` manually.',
53+
);
54+
_didOutputWarning = true;
55+
56+
return false;
57+
}
58+
}

packages/jaspr_builder/lib/src/options/client_options_builder.dart

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import 'dart:async';
2+
import 'dart:convert';
3+
import 'dart:io';
24

35
import 'package:build/build.dart';
46
import 'package:collection/collection.dart';
@@ -36,10 +38,7 @@ class ClientOptionsBuilder implements Builder {
3638
};
3739

3840
Future<void> generateClientOptions(BuildStep buildStep) async {
39-
final (mode, flutter) = await buildStep.loadProjectMode(options, buildStep);
40-
if (mode != 'static' && mode != 'server') {
41-
return;
42-
}
41+
final (_, flutter) = await buildStep.loadProjectMode(options, buildStep);
4342

4443
final serverId = AssetId(
4544
buildStep.inputId.package,
@@ -122,51 +121,64 @@ class ClientOptionsBuilder implements Builder {
122121
}
123122

124123
Future<List<Plugin>> loadWebPlugins(BuildStep buildStep) async {
125-
final pubspecId = AssetId(buildStep.inputId.package, 'pubspec.yaml');
126-
if (!await buildStep.canRead(pubspecId)) {
127-
return [];
124+
final pluginsDependenciesId = AssetId(buildStep.inputId.package, '.flutter-plugins-dependencies');
125+
126+
Future<String?> readPluginsDependencies() async {
127+
if (await buildStep.canRead(pluginsDependenciesId)) {
128+
return buildStep.readAsString(pluginsDependenciesId);
129+
}
130+
final file = File('.flutter-plugins-dependencies');
131+
if (await file.exists()) {
132+
return file.readAsString();
133+
}
134+
return null;
128135
}
129136

130-
final pubspecYaml = loadYaml(await buildStep.readAsString(pubspecId));
137+
var content = await readPluginsDependencies();
131138

132-
final isWorkspace = switch (pubspecYaml) {
133-
{'resolution': 'workspace'} => true,
134-
_ => false,
135-
};
136-
final dependencies = switch (pubspecYaml) {
137-
{'dependencies': final Map<Object?, Object?> deps} => deps.keys.cast<String>().toSet(),
138-
_ => <String>{},
139-
};
139+
if (content == null) {
140+
try {
141+
final result = await Process.run('flutter', ['packages', 'get']);
142+
if (result.exitCode != 0) {
143+
log.warning('Failed to run `flutter packages get` in order to find Flutter web plugins.\n${result.stderr}');
144+
}
145+
} catch (_) {
146+
return [];
147+
}
148+
content = await readPluginsDependencies();
149+
}
140150

141-
final packageConfig = await buildStep.packageConfig;
151+
if (content == null) {
152+
return [];
153+
}
142154

143-
final packages = {for (final p in packageConfig.packages) p.name: p};
155+
return _parseWebPlugins(content, buildStep);
156+
}
144157

145-
final plugins = <String, Plugin>{};
146-
// If we're in a workspace, only consider packages that are direct dependencies.
147-
final toVisit = isWorkspace ? dependencies.toList() : packages.keys.toList();
158+
Future<List<Plugin>> _parseWebPlugins(String content, BuildStep buildStep) async {
159+
final pluginsDependencies = jsonDecode(content);
148160

149-
while (toVisit.isNotEmpty) {
150-
final packageName = toVisit.removeLast();
151-
if (plugins.containsKey(packageName)) {
152-
continue;
153-
}
154-
final package = packages[packageName];
155-
if (package == null) {
156-
continue;
157-
}
161+
if (pluginsDependencies case {'plugins': {'web': final List<Object?> webPluginsDependencies}}) {
162+
final plugins = <Plugin>[];
158163

159-
final plugin = await _loadPluginForPackage(package.name, package.root, buildStep, toVisit);
160-
if (plugin != null) {
161-
plugins[packageName] = plugin;
164+
for (final webPluginDependency in webPluginsDependencies) {
165+
if (webPluginDependency case {'name': final String name, 'path': final String path, 'dev_dependency': false}) {
166+
final plugin = await _loadPluginForPackage(name, Uri.parse(path), buildStep);
167+
if (plugin != null) {
168+
plugins.add(plugin);
169+
}
170+
}
162171
}
163-
}
164172

165-
return plugins.values.toList();
173+
return plugins;
174+
} else {
175+
log.warning('Failed to find Flutter web plugins in .flutter-plugins-dependencies file.');
176+
return [];
177+
}
166178
}
167179

168-
Future<Plugin?> _loadPluginForPackage(String pluginName, Uri root, BuildStep buildStep, List<String> toVisit) async {
169-
final pubspecId = AssetId.resolve(root.resolve('pubspec.yaml'));
180+
Future<Plugin?> _loadPluginForPackage(String pluginName, Uri root, BuildStep buildStep) async {
181+
final pubspecId = AssetId(pluginName, 'pubspec.yaml');
170182
Object? pubspec;
171183
try {
172184
pubspec = loadYaml(await buildStep.readAsString(pubspecId));
@@ -180,7 +192,9 @@ Future<Plugin?> _loadPluginForPackage(String pluginName, Uri root, BuildStep bui
180192
'flutter': {'plugin': {'platforms': {'web': final YamlMap webPlatformYaml}}},
181193
}) {
182194
if (webPlatformYaml case {'default_package': final String defaultPackageName}) {
183-
toVisit.add(defaultPackageName);
195+
log.warning(
196+
'Failed to read Flutter web plugin for $pluginName. Default package $defaultPackageName not resolved.',
197+
);
184198
return null;
185199
}
186200

packages/jaspr_cli/lib/src/commands/build_command.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,9 @@ class BuildCommand extends BaseCommand with ProxyHelper, FlutterHelper {
436436
'--define=build_web_compilers:ddc_modules=use-ui-libraries=true',
437437
'--define=build_web_compilers:dart2js_modules=use-ui-libraries=true',
438438
'--define=build_web_compilers:dart2wasm_modules=use-ui-libraries=true',
439-
'--define=build_web_compilers:entrypoint=libraries-path="$librariesPath"',
439+
'--define=build_web_compilers:entrypoint=libraries-path=${jsonEncode(librariesPath)}',
440440
'--define=build_web_compilers:entrypoint=unsafe-allow-unsupported-modules=true',
441-
'--define=build_web_compilers:sdk_js=use-prebuilt-sdk-from-path="$sdkJsPath"',
441+
'--define=build_web_compilers:sdk_js=use-prebuilt-sdk-from-path=${jsonEncode(sdkJsPath)}',
442442
];
443443
}
444444

@@ -448,7 +448,7 @@ class BuildCommand extends BaseCommand with ProxyHelper, FlutterHelper {
448448
'--delete-conflicting-outputs',
449449
if (managedBuildOptions) ...[
450450
'--define=build_web_compilers:entrypoint=compiler=$compiler',
451-
'--define=build_web_compilers:entrypoint=${compiler}_args=[${args.map((a) => '"$a"').join(',')}]',
451+
'--define=build_web_compilers:entrypoint=${compiler}_args=${jsonEncode(args)}',
452452
if (project.flutterMode != FlutterMode.none) ...additionalFlutterBuildArgs(),
453453
if (includeSourceMaps) ...[
454454
'--define=build_web_compilers:dart2js_archive_extractor=filter_outputs=false',

packages/jaspr_cli/lib/src/commands/dev_command.dart

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// ignore_for_file: implementation_imports
22

33
import 'dart:async';
4+
import 'dart:convert';
45
import 'dart:io';
56

67
import 'package:dwds/data/build_result.dart';
@@ -293,18 +294,18 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper {
293294
project.checkFlutterBuildSupport();
294295
}
295296

296-
final ddcDefines = [
297-
'"jaspr.flags.verbose":$debug',
298-
for (final e in dartDefines.entries) '"${e.key}":"${e.value}"',
299-
].join(',');
297+
final ddcDefines = {
298+
'jaspr.flags.verbose': debug,
299+
...dartDefines,
300+
};
300301

301302
final dart2jsDefines = [
302-
'"-Djaspr.flags.release=$release"',
303-
if (!release) '"--enable-asserts"',
303+
'-Djaspr.flags.release=$release',
304+
if (!release) '--enable-asserts',
304305
if (useWasm && project.flutterMode != FlutterMode.none)
305-
'"--extra-compiler-option=--platform=${p.join(webSdkDir, 'kernel', 'dart2wasm_platform.dill')}"',
306-
for (final e in dartDefines.entries) '"-D${e.key}=${e.value}"',
307-
].join(',');
306+
'--extra-compiler-option=--platform=${p.join(webSdkDir, 'kernel', 'dart2wasm_platform.dill')}',
307+
for (final e in dartDefines.entries) '-D${e.key}=${e.value}',
308+
];
308309

309310
List<String> additionalFlutterBuildArgs() {
310311
final sdkKernelPath = p.url.join(
@@ -324,13 +325,13 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper {
324325
'--define=build_web_compilers:ddc_modules=use-ui-libraries=true',
325326
'--define=build_web_compilers:dart2js_modules=use-ui-libraries=true',
326327
'--define=build_web_compilers:dart2wasm_modules=use-ui-libraries=true',
327-
'--define=build_web_compilers:entrypoint=libraries-path="$librariesPath"',
328+
'--define=build_web_compilers:entrypoint=libraries-path=${jsonEncode(librariesPath)}',
328329
'--define=build_web_compilers:entrypoint=unsafe-allow-unsupported-modules=true',
329-
'--define=build_web_compilers:sdk_js=use-prebuilt-sdk-from-path="$sdkJsPath"',
330+
'--define=build_web_compilers:sdk_js=use-prebuilt-sdk-from-path=${jsonEncode(sdkJsPath)}',
330331
if (compiler == 'dartdevc') ...[
331-
'--define=build_web_compilers:ddc=ddc-kernel-path="$sdkKernelPath"',
332-
'--define=build_web_compilers:ddc=libraries-path="$librariesPath"',
333-
'--define=build_web_compilers:ddc=platform-sdk="$webSdkDir"',
332+
'--define=build_web_compilers:ddc=ddc-kernel-path=${jsonEncode(sdkKernelPath)}',
333+
'--define=build_web_compilers:ddc=libraries-path=${jsonEncode(librariesPath)}',
334+
'--define=build_web_compilers:ddc=platform-sdk=${jsonEncode(webSdkDir)}',
334335
],
335336
];
336337
}
@@ -342,8 +343,8 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper {
342343
'--define=build_web_compilers:ddc=generate-full-dill=true',
343344
'--define=build_web_compilers:entrypoint=compiler=$compiler',
344345
switch (compiler) {
345-
'dartdevc' => '--define=build_web_compilers:ddc=environment={$ddcDefines}',
346-
_ => '--define=build_web_compilers:entrypoint=${compiler}_args=[$dart2jsDefines]',
346+
'dartdevc' => '--define=build_web_compilers:ddc=environment=${jsonEncode(ddcDefines)}',
347+
_ => '--define=build_web_compilers:entrypoint=${compiler}_args=${jsonEncode(dart2jsDefines)}',
347348
},
348349
if (project.flutterMode != FlutterMode.none) ...additionalFlutterBuildArgs(),
349350
],

pubspec.lock

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,10 @@ packages:
237237
dependency: transitive
238238
description:
239239
name: characters
240-
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
240+
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
241241
url: "https://pub.dev"
242242
source: hosted
243-
version: "1.4.0"
243+
version: "1.4.1"
244244
charcode:
245245
dependency: transitive
246246
description:
@@ -1107,18 +1107,18 @@ packages:
11071107
dependency: transitive
11081108
description:
11091109
name: matcher
1110-
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
1110+
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
11111111
url: "https://pub.dev"
11121112
source: hosted
1113-
version: "0.12.17"
1113+
version: "0.12.18"
11141114
material_color_utilities:
11151115
dependency: transitive
11161116
description:
11171117
name: material_color_utilities
1118-
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
1118+
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
11191119
url: "https://pub.dev"
11201120
source: hosted
1121-
version: "0.11.1"
1121+
version: "0.13.0"
11221122
melos:
11231123
dependency: "direct dev"
11241124
description:
@@ -1768,26 +1768,26 @@ packages:
17681768
dependency: transitive
17691769
description:
17701770
name: test
1771-
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
1771+
sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae"
17721772
url: "https://pub.dev"
17731773
source: hosted
1774-
version: "1.26.3"
1774+
version: "1.28.0"
17751775
test_api:
17761776
dependency: transitive
17771777
description:
17781778
name: test_api
1779-
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
1779+
sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8"
17801780
url: "https://pub.dev"
17811781
source: hosted
1782-
version: "0.7.7"
1782+
version: "0.7.8"
17831783
test_core:
17841784
dependency: transitive
17851785
description:
17861786
name: test_core
1787-
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
1787+
sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4
17881788
url: "https://pub.dev"
17891789
source: hosted
1790-
version: "0.6.12"
1790+
version: "0.6.14"
17911791
timezone:
17921792
dependency: transitive
17931793
description:

0 commit comments

Comments
 (0)