1
+ import 'dart:async' ;
1
2
import 'dart:io' as io;
2
3
3
4
import 'package:dart_frog_cli/src/command.dart' ;
@@ -19,11 +20,12 @@ class DevCommand extends DartFrogCommand {
19
20
DevServerRunnerBuilder ? devServerRunnerBuilder,
20
21
runtime_compatibility.RuntimeCompatibilityCallback ?
21
22
ensureRuntimeCompatibility,
23
+ io.Stdin ? stdin,
22
24
}) : _ensureRuntimeCompatibility = ensureRuntimeCompatibility ??
23
25
runtime_compatibility.ensureRuntimeCompatibility,
24
26
_generator = generator ?? MasonGenerator .fromBundle,
25
- _devServerRunnerBuilder =
26
- devServerRunnerBuilder ?? DevServerRunner . new {
27
+ _devServerRunnerBuilder = devServerRunnerBuilder ?? DevServerRunner . new ,
28
+ _stdin = stdin ?? io.stdin {
27
29
argParser
28
30
..addOption (
29
31
'port' ,
@@ -45,13 +47,60 @@ class DevCommand extends DartFrogCommand {
45
47
final DevServerRunnerBuilder _devServerRunnerBuilder;
46
48
final runtime_compatibility.RuntimeCompatibilityCallback
47
49
_ensureRuntimeCompatibility;
50
+ final io.Stdin _stdin;
48
51
49
52
@override
50
53
final String description = 'Run a local development server.' ;
51
54
52
55
@override
53
56
final String name = 'dev' ;
54
57
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
+
55
104
@override
56
105
Future <int > run () async {
57
106
_ensureRuntimeCompatibility (cwd);
@@ -61,23 +110,23 @@ class DevCommand extends DartFrogCommand {
61
110
_defaultDartVmServicePort;
62
111
final generator = await _generator (dartFrogDevServerBundle);
63
112
64
- final devServer = _devServerRunnerBuilder (
113
+ _devServerRunner = _devServerRunnerBuilder (
65
114
devServerBundleGenerator: generator,
66
115
logger: logger,
67
116
workingDirectory: cwd,
68
117
port: port,
69
118
dartVmServicePort: dartVmServicePort,
119
+ onHotReloadEnabled: _startListeningForHelpers,
70
120
);
71
121
72
122
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 ());
76
127
return ExitCode .software.code;
128
+ } finally {
129
+ _stopListeningForHelpers ();
77
130
}
78
-
79
- final result = await devServer.exitCode;
80
-
81
- return result.code;
82
131
}
83
132
}
0 commit comments