Skip to content

Commit 7bf146a

Browse files
committed
setup css hotreload
1 parent d585d02 commit 7bf146a

5 files changed

Lines changed: 291 additions & 48 deletions

File tree

packages/jaspr/lib/src/client/client_binding.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:async';
2+
import 'dart:convert';
23
import 'dart:developer';
34

45
import 'package:universal_web/js_interop.dart';
@@ -21,6 +22,51 @@ class ClientAppBinding extends AppBinding with ComponentsBinding {
2122
rootElement?.visitChildren((element) => element.reassemble());
2223
return ServiceExtensionResponse.result('{}');
2324
});
25+
registerExtension('ext.jaspr.reload_stylesheets', (method, parameters) async {
26+
final urls = (jsonDecode(parameters['urls']!) as List).cast<String>();
27+
28+
void reloadStylesheet(web.Element oldLink, String url, {int retries = 5}) {
29+
final newLink = web.document.createElement('link') as web.HTMLLinkElement;
30+
newLink.rel = 'stylesheet';
31+
newLink.href = '$url?v=${DateTime.now().millisecondsSinceEpoch}';
32+
33+
StreamSubscription<void>? loadSub;
34+
StreamSubscription<void>? errorSub;
35+
36+
void cleanup() {
37+
loadSub?.cancel();
38+
errorSub?.cancel();
39+
}
40+
41+
loadSub = web.EventStreamProvider<web.Event>('load').forElement(newLink).listen((_) {
42+
cleanup();
43+
oldLink.remove();
44+
});
45+
46+
errorSub = web.EventStreamProvider<web.Event>('error').forElement(newLink).listen((_) {
47+
cleanup();
48+
newLink.remove();
49+
if (retries > 0) {
50+
Future.delayed(const Duration(milliseconds: 500), () {
51+
reloadStylesheet(oldLink, url, retries: retries - 1);
52+
});
53+
} else {
54+
print('Failed to reload stylesheet $url after 5 retries.');
55+
}
56+
});
57+
58+
oldLink.parentNode?.insertBefore(newLink, oldLink.nextSibling);
59+
}
60+
61+
// Reload all stylesheet <link> tags.
62+
for (final url in urls) {
63+
final link = web.document.querySelector('link[rel="stylesheet"][href^="$url"]');
64+
if (link != null) {
65+
reloadStylesheet(link, url);
66+
}
67+
}
68+
return ServiceExtensionResponse.result('{}');
69+
});
2470
return true;
2571
}());
2672
}

packages/jaspr_builder/lib/src/styles/styles_standalone_builder.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ class StylesStandaloneBuilder implements Builder {
6666
return;
6767
}
6868

69+
final outputName = buildStep.inputId.path
70+
.replaceFirst('lib/', '')
71+
.replaceFirst('.server.dart', '.css')
72+
.replaceFirst('.client.dart', '.css');
73+
6974
final outputId = buildStep.inputId.changeExtension('.styles.dart');
7075
final runnerCode = ImportsWriter().resolve('''
7176
import 'dart:io';
@@ -74,11 +79,15 @@ class StylesStandaloneBuilder implements Builder {
7479
import 'package:jaspr/src/dom/styles/rules.dart' show StyleRule, StyleRulesRender;
7580
[[/]]
7681
77-
void main() {
82+
void run() {
7883
final List<StyleRule> styles = ${styles.toOutputString()};
7984
80-
stdout.write(jsonEncode({
81-
'css': styles.render(),
85+
stdout.writeln(jsonEncode({
86+
'event': 'css',
87+
'file': '$outputName',
88+
'data': {
89+
'css': styles.render(),
90+
}
8291
}));
8392
}
8493
''');

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper {
120120

121121
handleClientWorkflow(workflow);
122122

123+
runCssGeneration(workflow);
124+
123125
if (project.flutterMode == FlutterMode.embedded) {
124126
final flutterProcess = await serveFlutter(useWasm);
125127

@@ -427,7 +429,6 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper {
427429
} else {
428430
logger.write('Rebuilt web assets.', tag: Tag.cli, progress: ProgressState.completed);
429431
}
430-
_runBuildCallback();
431432
} else if (event.status == BuildStatus.failed) {
432433
logger.write(
433434
'Failed building web assets. There is probably more output above.',
@@ -480,10 +481,6 @@ abstract class DevCommand extends BaseCommand with ProxyHelper, FlutterHelper {
480481

481482
return workflow;
482483
}
483-
484-
void _runBuildCallback() {
485-
generateCss();
486-
}
487484
}
488485

489486
String serverEntrypoint(String import) =>

packages/jaspr_cli/lib/src/dev/dev_proxy.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,28 @@ class DevProxy {
6060
if (buildResult.status == BuildStatus.succeeded) {
6161
for (final clientConnection in _clientConnections.values) {
6262
await clientConnection.performHotReload();
63+
64+
for (var callback in _postReloadCallbacks) {
65+
callback();
66+
}
6367
}
6468
}
6569
}
6670
}
6771
}
6872

73+
List<Function> _postReloadCallbacks = [];
74+
75+
void registerPostReloadCallback(Function callback) {
76+
_postReloadCallbacks.add(callback);
77+
}
78+
79+
void unregisterPostReloadCallback(Function callback) {
80+
_postReloadCallbacks.remove(callback);
81+
}
82+
83+
List<ClientConnection> getClientConnections() => _clientConnections.values.toList();
84+
6985
ClientConnection? getClientConnection(String? appId) {
7086
if (appId == null) return null;
7187
return _clientConnections[appId];

0 commit comments

Comments
 (0)