Skip to content

Commit bbde74b

Browse files
authored
feat(dart_frog_cli): press R to reload (#814)
1 parent 7c6563a commit bbde74b

File tree

4 files changed

+268
-18
lines changed

4 files changed

+268
-18
lines changed

packages/dart_frog_cli/lib/src/commands/dev/dev.dart

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:io' as io;
23

34
import 'package:dart_frog_cli/src/command.dart';
@@ -19,11 +20,12 @@ class DevCommand extends DartFrogCommand {
1920
DevServerRunnerBuilder? devServerRunnerBuilder,
2021
runtime_compatibility.RuntimeCompatibilityCallback?
2122
ensureRuntimeCompatibility,
23+
io.Stdin? stdin,
2224
}) : _ensureRuntimeCompatibility = ensureRuntimeCompatibility ??
2325
runtime_compatibility.ensureRuntimeCompatibility,
2426
_generator = generator ?? MasonGenerator.fromBundle,
25-
_devServerRunnerBuilder =
26-
devServerRunnerBuilder ?? DevServerRunner.new {
27+
_devServerRunnerBuilder = devServerRunnerBuilder ?? DevServerRunner.new,
28+
_stdin = stdin ?? io.stdin {
2729
argParser
2830
..addOption(
2931
'port',
@@ -45,13 +47,60 @@ class DevCommand extends DartFrogCommand {
4547
final DevServerRunnerBuilder _devServerRunnerBuilder;
4648
final runtime_compatibility.RuntimeCompatibilityCallback
4749
_ensureRuntimeCompatibility;
50+
final io.Stdin _stdin;
4851

4952
@override
5053
final String description = 'Run a local development server.';
5154

5255
@override
5356
final String name = 'dev';
5457

58+
StreamSubscription<List<int>>? _stdinSubscription;
59+
60+
late final DevServerRunner _devServerRunner;
61+
62+
void _startListeningForHelpers() {
63+
if (_stdinSubscription != null) return;
64+
if (!_stdin.hasTerminal) return;
65+
66+
// listen for the R key
67+
_stdin
68+
..echoMode = false
69+
..lineMode = false;
70+
71+
_stdinSubscription = _stdin.listen(
72+
(event) {
73+
if (event.length == 1 && event.first == 'R'.codeUnitAt(0)) {
74+
_devServerRunner.reload();
75+
}
76+
},
77+
onError: (dynamic error) {
78+
logger.err(error.toString());
79+
_stopListeningForHelpers();
80+
},
81+
cancelOnError: true,
82+
onDone: _stopListeningForHelpers,
83+
);
84+
85+
logger.info('Press R to reload');
86+
}
87+
88+
void _stopListeningForHelpers() {
89+
_stdinSubscription?.cancel();
90+
_stdinSubscription = null;
91+
92+
// The command may lose terminal after sigint, even though
93+
// the stdin subscription may have been created when the
94+
// devserver started.
95+
// That is why this check is made after the subscription
96+
// is canceled, if existent.
97+
if (!_stdin.hasTerminal) return;
98+
99+
_stdin
100+
..lineMode = true
101+
..echoMode = true;
102+
}
103+
55104
@override
56105
Future<int> run() async {
57106
_ensureRuntimeCompatibility(cwd);
@@ -61,23 +110,23 @@ class DevCommand extends DartFrogCommand {
61110
_defaultDartVmServicePort;
62111
final generator = await _generator(dartFrogDevServerBundle);
63112

64-
final devServer = _devServerRunnerBuilder(
113+
_devServerRunner = _devServerRunnerBuilder(
65114
devServerBundleGenerator: generator,
66115
logger: logger,
67116
workingDirectory: cwd,
68117
port: port,
69118
dartVmServicePort: dartVmServicePort,
119+
onHotReloadEnabled: _startListeningForHelpers,
70120
);
71121

72122
try {
73-
await devServer.start();
74-
} on DartFrogDevServerException catch (e) {
75-
logger.err(e.message);
123+
await _devServerRunner.start();
124+
return (await _devServerRunner.exitCode).code;
125+
} catch (e) {
126+
logger.err(e.toString());
76127
return ExitCode.software.code;
128+
} finally {
129+
_stopListeningForHelpers();
77130
}
78-
79-
final result = await devServer.exitCode;
80-
81-
return result.code;
82131
}
83132
}

packages/dart_frog_cli/lib/src/dev_server_runner/dev_server_runner.dart

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ typedef DevServerRunnerBuilder = DevServerRunner Function({
4747
required MasonGenerator devServerBundleGenerator,
4848
required String dartVmServicePort,
4949
required io.Directory workingDirectory,
50+
void Function()? onHotReloadEnabled,
5051
});
5152

5253
/// {@template dev_server_runner}
@@ -70,6 +71,7 @@ class DevServerRunner {
7071
required this.devServerBundleGenerator,
7172
required this.dartVmServicePort,
7273
required this.workingDirectory,
74+
this.onHotReloadEnabled,
7375
@visibleForTesting DirectoryWatcherBuilder? directoryWatcher,
7476
@visibleForTesting
7577
RestorableDirectoryGeneratorTargetBuilder? generatorTarget,
@@ -105,6 +107,9 @@ class DevServerRunner {
105107
/// The working directory of the dart_frog project.
106108
final io.Directory workingDirectory;
107109

110+
/// Callback for when hot reload is enabled.
111+
final void Function()? onHotReloadEnabled;
112+
108113
final DirectoryWatcherBuilder _directoryWatcher;
109114
final ProcessStart _startProcess;
110115
final ProcessRun _runProcess;
@@ -156,12 +161,19 @@ class DevServerRunner {
156161
logger.detail('[codegen] complete.');
157162
}
158163

159-
Future<void> _reload() async {
160-
logger.detail('[codegen] reloading...');
164+
Future<void> _reload([bool verbose = false]) async {
165+
final void Function(String) log;
166+
if (verbose) {
167+
log = logger.info;
168+
} else {
169+
log = logger.detail;
170+
}
171+
172+
log('[codegen] reloading...');
161173
_isReloading = true;
162174
await _codegen();
163175
_isReloading = false;
164-
logger.detail('[codegen] reload complete.');
176+
log('[codegen] reload complete.');
165177
}
166178

167179
// Internal method to kill the server process.
@@ -289,8 +301,11 @@ class DevServerRunner {
289301
process.stdout.listen((_) {
290302
final message = utf8.decode(_).trim();
291303
final containsHotReload = message.contains('[hotreload]');
292-
if (containsHotReload) isHotReloadingEnabled = true;
293304
if (message.isNotEmpty) logger.info(message);
305+
if (containsHotReload) {
306+
isHotReloadingEnabled = true;
307+
onHotReloadEnabled?.call();
308+
}
294309
final shouldCacheSnapshot = containsHotReload && !hasError;
295310
if (shouldCacheSnapshot) _target.cacheLatestSnapshot();
296311
hasError = false;
@@ -370,7 +385,7 @@ class DevServerRunner {
370385
/// server.
371386
Future<void> reload() async {
372387
if (isCompleted || !isServerRunning || _isReloading) return;
373-
return _reload();
388+
return _reload(true);
374389
}
375390
}
376391

@@ -383,4 +398,7 @@ class DartFrogDevServerException implements Exception {
383398

384399
/// The exception message.
385400
final String message;
401+
402+
@override
403+
String toString() => message;
386404
}

0 commit comments

Comments
 (0)