This commit is contained in:
thosakwe 2017-06-12 15:26:45 -04:00
parent b09682f515
commit cf0c3c9262
4 changed files with 46 additions and 5 deletions

View file

@ -1,5 +1,5 @@
# hot # hot
[![version 1.0.0-rc.2](https://img.shields.io/badge/pub-1.0.0--rc.2-brightgreen.svg)](https://pub.dartlang.org/packages/angel_rethink) [![Pub](https://img.shields.io/pub/v/angel_hot.svg)](https://pub.dartlang.org/packages/angel_hot)
Supports *hot reloading* of Angel servers on file changes. This is faster and Supports *hot reloading* of Angel servers on file changes. This is faster and
more reliable than merely reactively restarting a `Process`. more reliable than merely reactively restarting a `Process`.

View file

@ -9,6 +9,7 @@ import 'src/foo.dart';
main() async { main() async {
var hot = new HotReloader(createServer, [ var hot = new HotReloader(createServer, [
new Directory('src'),
new Directory('src'), new Directory('src'),
'server.dart', 'server.dart',
Uri.parse('package:angel_hot/angel_hot.dart') Uri.parse('package:angel_hot/angel_hot.dart')

View file

@ -16,6 +16,8 @@ typedef FutureOr<Angel> AngelGenerator();
class HotReloader { class HotReloader {
VMServiceClient _client; VMServiceClient _client;
final StreamController<WatchEvent> _onChange =
new StreamController<WatchEvent>.broadcast();
final List _paths = []; final List _paths = [];
final StringRenderer _renderer = new StringRenderer(pretty: false); final StringRenderer _renderer = new StringRenderer(pretty: false);
final Queue<HttpRequest> _requestQueue = new Queue<HttpRequest>(); final Queue<HttpRequest> _requestQueue = new Queue<HttpRequest>();
@ -25,6 +27,10 @@ class HotReloader {
/// Invoked to load a new instance of [Angel] on file changes. /// Invoked to load a new instance of [Angel] on file changes.
final AngelGenerator generator; final AngelGenerator generator;
/// Fires whenever a file change. You might consider using this to trigger
/// page reloads in a client.
Stream<WatchEvent> get onChange => _onChange.stream;
/// The maximum amount of time to queue incoming requests for if there is no [server] available. /// The maximum amount of time to queue incoming requests for if there is no [server] available.
/// ///
/// If the timeout expires, then the request will be immediately terminated with a `502 Bad Gateway` error. /// If the timeout expires, then the request will be immediately terminated with a `502 Bad Gateway` error.
@ -46,6 +52,10 @@ class HotReloader {
_paths.addAll(paths ?? []); _paths.addAll(paths ?? []);
} }
Future close() async {
_onChange.close();
}
Future handleRequest(HttpRequest request) async { Future handleRequest(HttpRequest request) async {
if (_server != null) if (_server != null)
return await _server.handleRequest(request); return await _server.handleRequest(request);
@ -109,6 +119,10 @@ class HotReloader {
var s = _server = await _generateServer(); var s = _server = await _generateServer();
while (!_requestQueue.isEmpty) while (!_requestQueue.isEmpty)
await s.handleRequest(_requestQueue.removeFirst()); await s.handleRequest(_requestQueue.removeFirst());
_onChange.stream
.transform(new _Debounce(new Duration(seconds: 1)))
.listen(_handleWatchEvent);
await _listenToFilesystem(); await _listenToFilesystem();
var server = await HttpServer.bind( var server = await HttpServer.bind(
@ -130,7 +144,10 @@ class HotReloader {
} else if (path is Uri) { } else if (path is Uri) {
if (path.scheme == 'package') { if (path.scheme == 'package') {
var uri = await Isolate.resolvePackageUri(path); var uri = await Isolate.resolvePackageUri(path);
await _listenToStat(uri.toFilePath()); if (uri != null)
await _listenToStat(uri.toFilePath());
else
await _listenToStat(path.toFilePath());
} else } else
await _listenToStat(path.toFilePath()); await _listenToStat(path.toFilePath());
} else { } else {
@ -158,8 +175,11 @@ class HotReloader {
return null; return null;
var watcher = new Watcher(path); var watcher = new Watcher(path);
//await watcher.ready;
watcher.events.listen(_handleWatchEvent); watcher.events.listen(_onChange.add, onError: (e) {
stderr.writeln('Could not listen to file changes at ${path}: $e');
});
print('Listening for file changes at ${path}...'); print('Listening for file changes at ${path}...');
return true; return true;
} catch (e) { } catch (e) {
@ -218,3 +238,23 @@ class HotReloader {
await s.handleRequest(_requestQueue.removeFirst()); await s.handleRequest(_requestQueue.removeFirst());
} }
} }
class _Debounce<S> implements StreamTransformer<S, S> {
final Duration _delay;
const _Debounce(this._delay);
Stream<S> bind(Stream<S> stream) {
var initial = new DateTime.now();
var next = initial.subtract(this._delay);
return stream.where((S data) {
var now = new DateTime.now();
if (now.isAfter(next)) {
next = now.add(this._delay);
return true;
} else {
return false;
}
});
}
}

View file

@ -1,6 +1,6 @@
name: angel_hot name: angel_hot
description: Supports hot reloading of Angel servers on file changes. description: Supports hot reloading of Angel servers on file changes.
version: 1.0.0-rc.3 version: 1.0.0
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/hot homepage: https://github.com/angel-dart/hot
environment: environment: