Migrated hot
This commit is contained in:
parent
2a69ecf91b
commit
1e92db46a6
5 changed files with 56 additions and 51 deletions
|
@ -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)
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Foo {
|
class Foo {
|
||||||
final String bar;
|
final String? bar;
|
||||||
|
|
||||||
Foo({this.bar});
|
Foo({this.bar});
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue