Migrated hot

This commit is contained in:
thomashii@dukefirehawk.com 2021-05-01 10:57:26 +08:00
parent 2a69ecf91b
commit 1e92db46a6
5 changed files with 56 additions and 51 deletions

View file

@ -24,8 +24,8 @@
* Migrated angel_jael to 4.0.0 (1/1 test passed) * Migrated angel_jael to 4.0.0 (1/1 test passed)
* Migrated pub_sub to 4.0.0 (16/16 tests passed) * Migrated pub_sub to 4.0.0 (16/16 tests passed)
* Migrated production to 3.0.0 (0/0 tests passed) * Migrated production to 3.0.0 (0/0 tests passed)
* Added html_builder and migrated to 2.0.0 (16/16 tests passed) * Added html_builder and migrated to 2.0.0 (1/1 tests passed)
* Updated hot to 3.0.0 (in progress) * Migrated hot to 4.0.0 (0/0 tests passed)
* Added range_header and migrated to 2.0.0 (16/16 tests passed) * Added range_header and migrated to 2.0.0 (16/16 tests passed)
* Updated static to 3.0.0 (in progress) * Updated static to 3.0.0 (in progress)
* Update basic-sdk-2.12.x boilerplate (in progress) * Update basic-sdk-2.12.x boilerplate (in progress)

View file

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:angel_hot/angel_hot.dart'; import 'package:angel_hot/angel_hot.dart';
import 'server.dart'; import 'server.dart';
main() async { void main() async {
var hot = HotReloader(createServer, [ var hot = HotReloader(createServer, [
Directory('src'), Directory('src'),
'server.dart', 'server.dart',

View file

@ -1,5 +1,5 @@
class Foo { class Foo {
final String bar; final String? bar;
Foo({this.bar}); Foo({this.bar});

View file

@ -20,17 +20,17 @@ import 'package:watcher/watcher.dart';
/// A utility class that watches the filesystem for changes, and starts new instances of an Angel server. /// A utility class that watches the filesystem for changes, and starts new instances of an Angel server.
class HotReloader { class HotReloader {
vm.VmService _client; late vm.VmService _client;
vm.IsolateRef _mainIsolate; vm.IsolateRef? _mainIsolate;
final StreamController<WatchEvent> _onChange = final StreamController<WatchEvent> _onChange =
StreamController<WatchEvent>.broadcast(); StreamController<WatchEvent>.broadcast();
final List _paths = []; final List _paths = [];
final StringRenderer _renderer = StringRenderer(pretty: false); final StringRenderer _renderer = StringRenderer(pretty: false);
final Queue<HttpRequest> _requestQueue = Queue<HttpRequest>(); final Queue<HttpRequest> _requestQueue = Queue<HttpRequest>();
HttpServer _io; late HttpServer _io;
AngelHttp _server; AngelHttp? _server;
Duration _timeout; Duration? _timeout;
vm.VM _vmachine; vm.VM? _vmachine;
/// If `true` (default), then developers can `press 'r' to reload` the application on-the-fly. /// If `true` (default), then developers can `press 'r' to reload` the application on-the-fly.
/// ///
@ -48,7 +48,7 @@ class HotReloader {
/// ///
/// 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.
/// Default: `5s` /// Default: `5s`
Duration get timeout => _timeout; Duration? get timeout => _timeout;
/// The Dart VM service host. /// The Dart VM service host.
/// ///
@ -65,16 +65,16 @@ class HotReloader {
/// [paths] can contain [FileSystemEntity], [Uri], [String] and [Glob] only. /// [paths] can contain [FileSystemEntity], [Uri], [String] and [Glob] only.
/// URI's can be `package:` URI's as well. /// URI's can be `package:` URI's as well.
HotReloader(this.generator, Iterable paths, HotReloader(this.generator, Iterable paths,
{Duration timeout, {Duration? timeout,
this.vmServiceHost = 'localhost', this.vmServiceHost = 'localhost',
this.vmServicePort = 8181, this.vmServicePort = 8181,
this.enableHotkeys = true}) { this.enableHotkeys = true}) {
_timeout = timeout ?? Duration(seconds: 5); _timeout = timeout ?? Duration(seconds: 5);
_paths.addAll(paths ?? []); _paths.addAll(paths);
} }
Future close() async { Future close() async {
await _io?.close(force: true); await _io.close(force: true);
await _onChange.close(); await _onChange.close();
} }
@ -99,7 +99,7 @@ class HotReloader {
if (request.headers if (request.headers
.value(HttpHeaders.acceptEncodingHeader) .value(HttpHeaders.acceptEncodingHeader)
?.toLowerCase() ?.toLowerCase()
?.contains('gzip') == .contains('gzip') ==
true) { true) {
response response
..headers.set(HttpHeaders.contentEncodingHeader, 'gzip') ..headers.set(HttpHeaders.contentEncodingHeader, 'gzip')
@ -111,7 +111,7 @@ class HotReloader {
} }
Future _handle(HttpRequest request) { Future _handle(HttpRequest request) {
return _server.handleRequest(request); return _server!.handleRequest(request);
} }
Future handleRequest(HttpRequest request) async { Future handleRequest(HttpRequest request) async {
@ -121,11 +121,11 @@ class HotReloader {
_requestQueue.add(request); _requestQueue.add(request);
} else { } else {
_requestQueue.add(request); _requestQueue.add(request);
Timer(timeout, () { Timer(timeout!, () {
if (_requestQueue.remove(request)) { if (_requestQueue.remove(request)) {
// Send 502 response // Send 502 response
sendError(request, HttpStatus.badGateway, '502 Bad Gateway', sendError(request, HttpStatus.badGateway, '502 Bad Gateway',
'Request timed out after ${timeout.inMilliseconds}ms.'); 'Request timed out after ${timeout!.inMilliseconds}ms.');
} }
}); });
} }
@ -139,27 +139,27 @@ class HotReloader {
} }
void _logWarning(String msg) { void _logWarning(String msg) {
if (_server?.app?.logger != null) { if (_server?.app.logger != null) {
_server.app.logger.warning(msg); _server!.app.logger!.warning(msg);
} else { } else {
print(yellow.wrap('WARNING: $msg')); print(yellow.wrap('WARNING: $msg'));
} }
} }
void _logInfo(String msg) { void _logInfo(String msg) {
if (_server?.app?.logger != null) { if (_server?.app.logger != null) {
_server.app.logger.info(msg); _server!.app.logger!.info(msg);
} else { } else {
print(lightGray.wrap(msg)); print(lightGray.wrap(msg));
} }
} }
/// Starts listening to requests and filesystem events. /// Starts listening to requests and filesystem events.
Future<HttpServer> startServer([address, int port]) async { Future<HttpServer> startServer([address, int? port]) async {
var isHot = true; var isHot = true;
_server = await _generateServer(); _server = await _generateServer();
if (_paths?.isNotEmpty != true) { if (_paths.isNotEmpty != true) {
_logWarning( _logWarning(
'You have instantiated a HotReloader without providing any filesystem paths to watch.'); 'You have instantiated a HotReloader without providing any filesystem paths to watch.');
} }
@ -174,7 +174,7 @@ class HotReloader {
isHot = false; isHot = false;
} else { } else {
var info = await dev.Service.getInfo(); var info = await dev.Service.getInfo();
var uri = info.serverUri; var uri = info.serverUri!;
uri = uri.replace(path: p.join(uri.path, 'ws')); uri = uri.replace(path: p.join(uri.path, 'ws'));
if (uri.scheme == 'https') { if (uri.scheme == 'https') {
uri = uri.replace(scheme: 'wss'); uri = uri.replace(scheme: 'wss');
@ -183,10 +183,10 @@ class HotReloader {
} }
_client = await vm.vmServiceConnectUri(uri.toString()); _client = await vm.vmServiceConnectUri(uri.toString());
_vmachine ??= await _client.getVM(); _vmachine ??= await _client.getVM();
_mainIsolate ??= _vmachine.isolates.first; _mainIsolate ??= _vmachine!.isolates!.first;
for (var isolate in _vmachine.isolates) { for (var isolate in _vmachine!.isolates!) {
await _client.setExceptionPauseMode(isolate.id, 'None'); await _client.setExceptionPauseMode(isolate.id!, 'None');
} }
await _listenToFilesystem(); await _listenToFilesystem();
@ -206,7 +206,8 @@ class HotReloader {
if (enableHotkeys) { if (enableHotkeys) {
var serverUri = var serverUri =
Uri(scheme: 'http', host: server.address.address, port: server.port); Uri(scheme: 'http', host: server.address.address, port: server.port);
var observatoryUri = await dev.Service.getInfo().then((i) => i.serverUri); var observatoryUri =
await dev.Service.getInfo().then((i) => i.serverUri!);
print(styleBold.wrap( print(styleBold.wrap(
'\n🔥 To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".')); '\n🔥 To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".'));
@ -234,7 +235,7 @@ class HotReloader {
stdin.lineMode = stdin.echoMode = false; stdin.lineMode = stdin.echoMode = false;
} catch (_) {} } catch (_) {}
StreamSubscription<int> sub; late StreamSubscription<int> sub;
try { try {
sub = stdin.expand((l) => l).listen((ch) async { sub = stdin.expand((l) => l).listen((ch) async {
@ -245,10 +246,10 @@ class HotReloader {
if (ch == $R) { if (ch == $R) {
_logInfo('Manually restarting server...\n'); _logInfo('Manually restarting server...\n');
await _killServer(); await _killServer();
await _server.close(); await _server!.close();
var addr = _io.address.address; var addr = _io.address.address;
var port = _io.port; var port = _io.port;
await _io?.close(force: true); await _io.close(force: true);
await startServer(addr, port); await startServer(addr, port);
} else if (ch == $q) { } else if (ch == $q) {
stdin.echoMode = stdin.lineMode = true; stdin.echoMode = stdin.lineMode = true;
@ -271,7 +272,7 @@ class HotReloader {
return server; return server;
} }
_listenToFilesystem() async { Future<void> _listenToFilesystem() async {
for (var path in _paths) { for (var path in _paths) {
if (path is String) { if (path is String) {
await _listenToStat(path); await _listenToStat(path);
@ -299,8 +300,8 @@ class HotReloader {
} }
} }
_listenToStat(String path) async { Future<void> _listenToStat(String path) async {
_listen() async { Future _listen() async {
try { try {
var stat = await FileStat.stat(path); var stat = await FileStat.stat(path);
if (stat.type == FileSystemEntityType.link) { if (stat.type == FileSystemEntityType.link) {
@ -320,7 +321,7 @@ class HotReloader {
var watcher = Watcher(path); var watcher = Watcher(path);
watcher.events.listen(_onChange.add, onError: (e) { watcher.events.listen(_onChange.add, onError: (e) {
_logWarning('Could not listen to file changes at ${path}: $e'); _logWarning('Could not listen to file changes at $path: $e');
}); });
// print('Listening for file changes at ${path}...'); // print('Listening for file changes at ${path}...');
@ -344,21 +345,21 @@ class HotReloader {
scheduleMicrotask(() async { scheduleMicrotask(() async {
// Disconnect active WebSockets // Disconnect active WebSockets
try { try {
var ws = _server.app.container.make<AngelWebSocket>(); var ws = _server!.app.container!.make<AngelWebSocket>()!;
for (var client in ws.clients) { for (var client in ws.clients) {
try { try {
await client.close(); await client.close();
} catch (e) { } catch (e) {
_logWarning( _logWarning(
'Couldn\'t close WebSocket from session #${client.request.session.id}: $e'); 'Couldn\'t close WebSocket from session #${client.request.session!.id}: $e');
} }
} }
// await Future.forEach( // await Future.forEach(
// _server.app.shutdownHooks, _server.app.configure); // _server.app.shutdownHooks, _server.app.configure);
await _server.app.close(); await _server!.app.close();
_server.app.logger?.clearListeners(); _server!.app.logger?.clearListeners();
} catch (_) { } catch (_) {
// Fail silently... // Fail silently...
} }
@ -372,7 +373,7 @@ class HotReloader {
_server = null; _server = null;
if (hot) { if (hot) {
var report = await _client.reloadSources(_mainIsolate.id); var report = await _client.reloadSources(_mainIsolate!.id!);
if (report.success != null) { if (report.success != null) {
_logWarning( _logWarning(

View file

@ -5,7 +5,7 @@ author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/hot homepage: https://github.com/angel-dart/hot
publish_to: none publish_to: none
environment: environment:
sdk: ">=2.10.0 <3.0.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel_framework: angel_framework:
git: git:
@ -17,14 +17,18 @@ dependencies:
url: https://github.com/dukefirehawk/angel.git url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd ref: sdk-2.12.x_nnbd
path: packages/websocket path: packages/websocket
charcode: ^1.0.0 html_builder:
glob: ^2.0.0 git:
html_builder: ^1.0.0 url: https://github.com/dukefirehawk/angel.git
io: ^0.3.5 ref: sdk-2.12.x_nnbd
path: ^1.0.0 path: packages/html_builder
vm_service: ^5.5.0 charcode: ^1.2.0
glob: ^2.0.1
io: ^1.0.0
path: ^1.8.0
vm_service: ^6.2.0
watcher: ^1.0.0 watcher: ^1.0.0
dev_dependencies: dev_dependencies:
http: ^0.13.0 http: ^0.13.2
logging: ^1.0.0 logging: ^1.0.1
pedantic: ^1.0.0 pedantic: ^1.11.0