Skip to content

Commit 3c379fe

Browse files
committed
Detect ready dart 3
1 parent 3f16c75 commit 3c379fe

2 files changed

Lines changed: 96 additions & 1 deletion

File tree

‎lib/src/serve_command.dart‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'dart:async';
1616
import 'dart:io';
1717

1818
import 'package:args/command_runner.dart';
19+
import 'package:http/http.dart' as http;
1920
import 'package:io/ansi.dart';
2021
import 'package:io/io.dart';
2122

@@ -149,6 +150,8 @@ class ServeCommand extends Command<int> {
149150
for (final dir in portsToServeByDir.keys) dir: await findUnusedPort()
150151
};
151152

153+
final startupTimer = Stopwatch()..start();
154+
152155
// Start the underlying `webdev serve` process.
153156
webdevServer = await WebdevServer.start([
154157
if (hostname != 'localhost') '--hostname=$hostname',
@@ -186,6 +189,69 @@ class ServeCommand extends Command<int> {
186189
}
187190
}
188191

192+
if (!proxiesFailed) {
193+
final hostToProbe = hostname == 'any' ? 'localhost' : hostname;
194+
final readyByDir = {
195+
for (final dir in portsToServeByDir.keys) dir: Completer<void>()
196+
};
197+
198+
for (final dir in portsToServeByDir.keys) {
199+
unawaited(() async {
200+
try {
201+
await _waitForProxyToServe(
202+
host: hostToProbe,
203+
port: portsToServeByDir[dir]!,
204+
);
205+
if (!readyByDir[dir]!.isCompleted) {
206+
readyByDir[dir]!.complete();
207+
}
208+
} catch (e, stackTrace) {
209+
if (!readyByDir[dir]!.isCompleted) {
210+
readyByDir[dir]!.completeError(e, stackTrace);
211+
}
212+
}
213+
}());
214+
}
215+
// to reduce exception noise, wait a few seconds
216+
await Future.delayed(const Duration(seconds: 3));
217+
218+
await Future.any([
219+
Future.wait(readyByDir.values.map((completer) => completer.future)),
220+
exitCodeCompleter.future,
221+
]);
222+
223+
if (!exitCodeCompleter.isCompleted) {
224+
final elapsedSeconds =
225+
(startupTimer.elapsedMilliseconds / 1000).toStringAsFixed(1);
226+
stdout.writeln('[INFO] Succeeded after $elapsedSeconds seconds');
227+
startupTimer.stop();
228+
}
229+
}
230+
189231
return exitCodeCompleter.future;
190232
}
233+
234+
static Future<void> _waitForProxyToServe({
235+
required String host,
236+
required int port,
237+
}) async {
238+
const pollInterval = Duration(milliseconds: 1000);
239+
240+
while (true) {
241+
final uri = Uri.parse('http://$host:$port');
242+
try {
243+
stdout.writeln('[INFO] Pinging server $uri to detect responsiveness...an exception from the server means it is not ready.');
244+
final response = await http.get(uri);
245+
if (response.statusCode == 200) {
246+
stdout.writeln('[INFO] $uri responded with 200, server is ready!');
247+
return;
248+
}
249+
stdout.writeln('[INFO] $uri responded with ${response.statusCode}, server is not ready yet.');
250+
} catch (_) {
251+
// Keep polling until webdev starts listening and serving successfully.
252+
stdout.writeln('[INFO] $uri responded with an error, server is not ready yet.');
253+
}
254+
await Future.delayed(pollInterval);
255+
}
256+
}
191257
}

‎lib/src/webdev_server.dart‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
// limitations under the License.
1414

1515
import 'dart:async';
16+
import 'dart:convert';
1617
import 'dart:io';
1718

19+
import 'package:http/http.dart' as http;
1820
import 'package:webdev_proxy/src/logging.dart';
1921

2022
/// A simple abstraction over a `webdev serve ...` process.
@@ -39,13 +41,40 @@ class WebdevServer {
3941
/// [WebdevServer] abstraction over said process.
4042
static Future<WebdevServer> start(List<String> args,
4143
{ProcessStartMode mode = ProcessStartMode.inheritStdio}) async {
42-
final webdevArgs = ['pub', 'global', 'run', 'webdev', 'serve', ...args];
44+
final webdevArgs = [
45+
'pub',
46+
'global',
47+
'run',
48+
'webdev',
49+
'serve',
50+
'-v',
51+
...args
52+
];
4353
log.fine('Running `dart ${webdevArgs.join(' ')}');
4454
final process = await Process.start(
4555
'dart',
4656
webdevArgs,
4757
mode: mode,
4858
);
59+
60+
61+
// `inheritStdio` and `detached` do not expose readable stdout streams.
62+
// if (mode == ProcessStartMode.normal ||
63+
// mode == ProcessStartMode.detachedWithStdio) {
64+
// // `webdev serve` doesn't expose a dedicated "ready" signal, so key off
65+
// // the "Built with" log line.
66+
// var loggedReadyMessage = false;
67+
// unawaited(process.stdout
68+
// .transform(utf8.decoder)
69+
// .transform(const LineSplitter())
70+
// .listen((line) {
71+
// if (!loggedReadyMessage && line.contains('[INFO] : Built with')) {
72+
// loggedReadyMessage = true;
73+
// stdout.writeln('[INFO] Succeeded after');
74+
// }
75+
// }).asFuture<void>());
76+
// }
77+
4978
return WebdevServer._(process);
5079
}
5180
}

0 commit comments

Comments
 (0)