Fixed compatibility issues with 1.1.3
This commit is contained in:
parent
4639efd67f
commit
0c3936d602
10 changed files with 128 additions and 95 deletions
|
@ -2,6 +2,7 @@
|
||||||
<module type="WEB_MODULE" version="4">
|
<module type="WEB_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
|
8
.idea/runConfigurations/main_dart.xml
Normal file
8
.idea/runConfigurations/main_dart.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="main.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
|
||||||
|
<option name="VMOptions" value="--observe" />
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/example/basic/main.dart" />
|
||||||
|
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
7
.idea/runConfigurations/main_dart__No_VM_service_.xml
Normal file
7
.idea/runConfigurations/main_dart__No_VM_service_.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="main.dart (No VM service)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/example/basic/main.dart" />
|
||||||
|
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -1,8 +0,0 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="server.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
|
|
||||||
<option name="VMOptions" value="--observe" />
|
|
||||||
<option name="filePath" value="$PROJECT_DIR$/example/basic/server.dart" />
|
|
||||||
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="server.dart (No VM service)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
|
||||||
<option name="filePath" value="$PROJECT_DIR$/example/basic/server.dart" />
|
|
||||||
<option name="workingDirectory" value="$PROJECT_DIR$/example/basic" />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# 1.1.1
|
||||||
|
* Disable the observatory from pausing the isolate
|
||||||
|
on exceptions, because Angel already handles
|
||||||
|
all exceptions by itself.
|
|
@ -1,8 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:io' show Directory;
|
||||||
import 'dart:io';
|
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_hot/angel_hot.dart';
|
import 'package:angel_hot/angel_hot.dart';
|
||||||
|
import 'package:dart2_constant/convert.dart';
|
||||||
|
import 'package:dart2_constant/io.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'src/foo.dart';
|
import 'src/foo.dart';
|
||||||
|
|
||||||
|
@ -10,19 +11,19 @@ main() async {
|
||||||
var hot = new HotReloader(createServer, [
|
var hot = new HotReloader(createServer, [
|
||||||
new Directory('src'),
|
new Directory('src'),
|
||||||
new Directory('src'),
|
new Directory('src'),
|
||||||
'server.dart',
|
'main.dart',
|
||||||
Uri.parse('package:angel_hot/angel_hot.dart')
|
Uri.parse('package:angel_hot/angel_hot.dart')
|
||||||
]);
|
]);
|
||||||
var server = await hot.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
|
var server = await hot.startServer('127.0.0.1', 3000);
|
||||||
print(
|
print('Hot server listening at http://${server.address.address}:${server
|
||||||
'Hot server listening at http://${server.address.address}:${server.port}');
|
.port}');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Angel> createServer() async {
|
Future<Angel> createServer() async {
|
||||||
var app = new Angel();
|
var app = new Angel();
|
||||||
|
|
||||||
app.lazyParseBodies = true;
|
app.lazyParseBodies = true;
|
||||||
app.injectSerializer(JSON.encode);
|
app.serializer = json.encode;
|
||||||
|
|
||||||
// Edit this line, and then refresh the page in your browser!
|
// Edit this line, and then refresh the page in your browser!
|
||||||
app.get('/', {'hello': 'hot world!'});
|
app.get('/', {'hello': 'hot world!'});
|
||||||
|
@ -31,18 +32,18 @@ Future<Angel> createServer() async {
|
||||||
app.use(() => throw new AngelHttpException.notFound());
|
app.use(() => throw new AngelHttpException.notFound());
|
||||||
|
|
||||||
app.injectEncoders({
|
app.injectEncoders({
|
||||||
'gzip': GZIP.encoder,
|
'gzip': gzip.encoder,
|
||||||
'deflate': ZLIB.encoder,
|
'deflate': zlib.encoder,
|
||||||
});
|
});
|
||||||
|
|
||||||
app.logger = new Logger('angel')
|
app.logger = new Logger('angel')
|
||||||
..onRecord.listen((rec) {
|
..onRecord.listen((rec) {
|
||||||
print(rec);
|
print(rec);
|
||||||
if (rec.error != null) {
|
if (rec.error != null) {
|
||||||
print(rec.error);
|
print(rec.error);
|
||||||
print(rec.stackTrace);
|
print(rec.stackTrace);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
|
@ -1,10 +1,25 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:io'
|
||||||
import 'dart:io';
|
show
|
||||||
|
ContentType,
|
||||||
|
Directory,
|
||||||
|
File,
|
||||||
|
FileStat,
|
||||||
|
FileSystemEntity,
|
||||||
|
FileSystemException,
|
||||||
|
HttpHeaders,
|
||||||
|
HttpRequest,
|
||||||
|
HttpServer,
|
||||||
|
Link,
|
||||||
|
Platform,
|
||||||
|
exit,
|
||||||
|
stderr;
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_websocket/server.dart';
|
import 'package:angel_websocket/server.dart';
|
||||||
|
import 'package:dart2_constant/convert.dart';
|
||||||
|
import 'package:dart2_constant/io.dart';
|
||||||
import 'package:glob/glob.dart';
|
import 'package:glob/glob.dart';
|
||||||
import 'package:html_builder/elements.dart';
|
import 'package:html_builder/elements.dart';
|
||||||
import 'package:html_builder/html_builder.dart';
|
import 'package:html_builder/html_builder.dart';
|
||||||
|
@ -15,13 +30,15 @@ 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;
|
vm.VmService _client;
|
||||||
|
vm.IsolateRef _mainIsolate;
|
||||||
final StreamController<WatchEvent> _onChange =
|
final StreamController<WatchEvent> _onChange =
|
||||||
new StreamController<WatchEvent>.broadcast();
|
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>();
|
||||||
Angel _server;
|
AngelHttp _server;
|
||||||
Duration _timeout;
|
Duration _timeout;
|
||||||
|
vm.VM _vmachine;
|
||||||
|
|
||||||
/// Invoked to load a new instance of [Angel] on file changes.
|
/// Invoked to load a new instance of [Angel] on file changes.
|
||||||
final FutureOr<Angel> Function() generator;
|
final FutureOr<Angel> Function() generator;
|
||||||
|
@ -62,9 +79,44 @@ class HotReloader {
|
||||||
_onChange.close();
|
_onChange.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendError(HttpRequest request, int status, String title_, e) {
|
||||||
|
var doc = html(lang: 'en', c: [
|
||||||
|
head(c: [
|
||||||
|
meta(name: 'viewport', content: 'width=device-width, initial-scale=1'),
|
||||||
|
title(c: [text(title_)])
|
||||||
|
]),
|
||||||
|
body(c: [
|
||||||
|
h1(c: [text(title_)]),
|
||||||
|
i(c: [text(e.toString())])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
var response = request.response;
|
||||||
|
response.statusCode = HttpStatus.badGateway;
|
||||||
|
response.headers
|
||||||
|
..contentType = ContentType.HTML
|
||||||
|
..set(HttpHeaders.SERVER, 'angel_hot');
|
||||||
|
|
||||||
|
if (request.headers
|
||||||
|
.value(HttpHeaders.ACCEPT_ENCODING)
|
||||||
|
?.toLowerCase()
|
||||||
|
?.contains('gzip') ==
|
||||||
|
true) {
|
||||||
|
response
|
||||||
|
..headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip')
|
||||||
|
..add(gzip.encode(utf8.encode(_renderer.render(doc))));
|
||||||
|
} else
|
||||||
|
response.write(_renderer.render(doc));
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _handle(HttpRequest request) {
|
||||||
|
return _server.handleRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
Future handleRequest(HttpRequest request) async {
|
Future handleRequest(HttpRequest request) async {
|
||||||
if (_server != null)
|
if (_server != null)
|
||||||
return await _server.handleRequest(request);
|
return await _handle(request);
|
||||||
else if (timeout == null)
|
else if (timeout == null)
|
||||||
_requestQueue.add(request);
|
_requestQueue.add(request);
|
||||||
else {
|
else {
|
||||||
|
@ -72,48 +124,18 @@ class HotReloader {
|
||||||
new Timer(timeout, () {
|
new Timer(timeout, () {
|
||||||
if (_requestQueue.remove(request)) {
|
if (_requestQueue.remove(request)) {
|
||||||
// Send 502 response
|
// Send 502 response
|
||||||
var doc = html(lang: 'en', c: [
|
sendError(request, HttpStatus.badGateway, '502 Bad Gateway',
|
||||||
head(c: [
|
'Request timed out after ${timeout.inMilliseconds}ms.');
|
||||||
meta(
|
|
||||||
name: 'viewport',
|
|
||||||
content: 'width=device-width, initial-scale=1'),
|
|
||||||
title(c: [text('502 Bad Gateway')])
|
|
||||||
]),
|
|
||||||
body(c: [
|
|
||||||
h1(c: [text('502 Bad Gateway')]),
|
|
||||||
i(c: [
|
|
||||||
text('Request timed out after ${timeout.inMilliseconds}ms.')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
|
|
||||||
var response = request.response;
|
|
||||||
response.statusCode = HttpStatus.BAD_GATEWAY;
|
|
||||||
response.headers
|
|
||||||
..contentType = ContentType.HTML
|
|
||||||
..set(HttpHeaders.SERVER, 'angel_hot');
|
|
||||||
|
|
||||||
if (request.headers
|
|
||||||
.value(HttpHeaders.ACCEPT_ENCODING)
|
|
||||||
?.toLowerCase()
|
|
||||||
?.contains('gzip') ==
|
|
||||||
true) {
|
|
||||||
response
|
|
||||||
..headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip')
|
|
||||||
..add(GZIP.encode(UTF8.encode(_renderer.render(doc))));
|
|
||||||
} else
|
|
||||||
response.write(_renderer.render(doc));
|
|
||||||
response.close();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Angel> _generateServer() async {
|
Future<AngelHttp> _generateServer() async {
|
||||||
var s = await generator() as Angel;
|
var s = await generator();
|
||||||
await Future.forEach(s.startupHooks, s.configure);
|
await Future.forEach(s.startupHooks, s.configure);
|
||||||
s.optimizeForProduction();
|
s.optimizeForProduction();
|
||||||
return s;
|
return new AngelHttp(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts listening to requests and filesystem events.
|
/// Starts listening to requests and filesystem events.
|
||||||
|
@ -122,17 +144,21 @@ class HotReloader {
|
||||||
print(
|
print(
|
||||||
'WARNING: You have instantiated a HotReloader without providing any filesystem paths to watch.');
|
'WARNING: You have instantiated a HotReloader without providing any filesystem paths to watch.');
|
||||||
|
|
||||||
var s = _server = await _generateServer();
|
_client = await vm.vmServiceConnect(
|
||||||
while (!_requestQueue.isEmpty)
|
vmServiceHost ?? 'localhost', vmServicePort ?? 8181);
|
||||||
await s.handleRequest(_requestQueue.removeFirst());
|
_vmachine ??= await _client.getVM();
|
||||||
|
_mainIsolate ??= _vmachine.isolates.first;
|
||||||
|
await _client.setExceptionPauseMode(_mainIsolate.id, 'None');
|
||||||
|
|
||||||
|
_server = await _generateServer();
|
||||||
|
while (!_requestQueue.isEmpty) await _handle(_requestQueue.removeFirst());
|
||||||
|
|
||||||
_onChange.stream
|
_onChange.stream
|
||||||
.transform(new _Debounce(new Duration(seconds: 1)))
|
.transform(new _Debounce(new Duration(seconds: 1)))
|
||||||
.listen(_handleWatchEvent);
|
.listen(_handleWatchEvent);
|
||||||
await _listenToFilesystem();
|
await _listenToFilesystem();
|
||||||
|
|
||||||
var server = await HttpServer.bind(
|
var server = await HttpServer.bind(address ?? '127.0.0.1', port ?? 0);
|
||||||
address ?? InternetAddress.LOOPBACK_IP_V4, port ?? 0);
|
|
||||||
server.listen(handleRequest);
|
server.listen(handleRequest);
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
@ -174,14 +200,14 @@ class HotReloader {
|
||||||
_listen() async {
|
_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) {
|
||||||
var lnk = new Link(path);
|
var lnk = new Link(path);
|
||||||
var p = await lnk.resolveSymbolicLinks();
|
var p = await lnk.resolveSymbolicLinks();
|
||||||
return await _listenToStat(p);
|
return await _listenToStat(p);
|
||||||
} else if (stat.type == FileSystemEntityType.FILE) {
|
} else if (stat.type == FileSystemEntityType.file) {
|
||||||
var file = new File(path);
|
var file = new File(path);
|
||||||
if (!await file.exists()) return null;
|
if (!await file.exists()) return null;
|
||||||
} else if (stat.type == FileSystemEntityType.DIRECTORY) {
|
} else if (stat.type == FileSystemEntityType.directory) {
|
||||||
var dir = new Directory(path);
|
var dir = new Directory(path);
|
||||||
if (!await dir.exists()) return null;
|
if (!await dir.exists()) return null;
|
||||||
} else
|
} else
|
||||||
|
@ -204,7 +230,8 @@ class HotReloader {
|
||||||
|
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
print(
|
print(
|
||||||
'WARNING: Unable to watch path "$path" from working directory "${Directory.current.path}". Please ensure that it exists.');
|
'WARNING: Unable to watch path "$path" from working directory "${Directory
|
||||||
|
.current.path}". Please ensure that it exists.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,27 +243,24 @@ class HotReloader {
|
||||||
// Do this asynchronously, because we really don't care about the old server anymore.
|
// Do this asynchronously, because we really don't care about the old server anymore.
|
||||||
new Future(() async {
|
new Future(() async {
|
||||||
// Disconnect active WebSockets
|
// Disconnect active WebSockets
|
||||||
var ws = old.container.make(AngelWebSocket) as AngelWebSocket;
|
var ws = old.app.container.make(AngelWebSocket) as AngelWebSocket;
|
||||||
|
|
||||||
for (var client in ws.clients) {
|
for (var client in ws.clients) {
|
||||||
try {
|
try {
|
||||||
await client.close(WebSocketStatus.GOING_AWAY);
|
await client.close(WebSocketStatus.goingAway);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
stderr.writeln(
|
stderr.writeln(
|
||||||
'Couldn\'t close WebSocket from session #${client.request.session.id}: $e');
|
'Couldn\'t close WebSocket from session #${client.request
|
||||||
|
.session.id}: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future.forEach(old.shutdownHooks, old.configure);
|
Future.forEach(old.app.shutdownHooks, old.app.configure);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_server = null;
|
_server = null;
|
||||||
_client ??= await vm.vmServiceConnect(
|
var report = await _client.reloadSources(_mainIsolate.id);
|
||||||
vmServiceHost ?? 'localhost', vmServicePort ?? 8181);
|
|
||||||
var vmachine = await _client.getVM();
|
|
||||||
var mainIsolate = vmachine.isolates.first;
|
|
||||||
var report = await _client.reloadSources(mainIsolate.id);
|
|
||||||
|
|
||||||
if (!report.success) {
|
if (!report.success) {
|
||||||
stderr.writeln('Hot reload failed!!!');
|
stderr.writeln('Hot reload failed!!!');
|
||||||
|
@ -246,12 +270,11 @@ class HotReloader {
|
||||||
|
|
||||||
var s = await _generateServer();
|
var s = await _generateServer();
|
||||||
_server = s;
|
_server = s;
|
||||||
while (!_requestQueue.isEmpty)
|
while (!_requestQueue.isEmpty) await _handle(_requestQueue.removeFirst());
|
||||||
await s.handleRequest(_requestQueue.removeFirst());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Debounce<S> implements StreamTransformer<S, S> {
|
class _Debounce<S> extends StreamTransformerBase<S, S> {
|
||||||
final Duration _delay;
|
final Duration _delay;
|
||||||
|
|
||||||
const _Debounce(this._delay);
|
const _Debounce(this._delay);
|
||||||
|
|
12
pubspec.yaml
12
pubspec.yaml
|
@ -1,16 +1,20 @@
|
||||||
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.1.0
|
version: 1.1.1
|
||||||
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:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0 <3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^1.1.0-alpha
|
angel_framework: ^1.1.0
|
||||||
angel_websocket: ^1.1.0-alpha
|
angel_websocket: ^1.1.0-alpha
|
||||||
|
dart2_constant: ^1.0.0
|
||||||
|
glob: ^1.0.0
|
||||||
html_builder: ^1.0.0
|
html_builder: ^1.0.0
|
||||||
vm_service_lib: 0.3.5
|
vm_service_lib: ^0.3.5
|
||||||
|
watcher: ^0.9.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel_test: ^1.0.0
|
angel_test: ^1.0.0
|
||||||
http: ^0.11.3
|
http: ^0.11.3
|
||||||
|
logging: ^0.11.0
|
||||||
test: ^0.12.15
|
test: ^0.12.15
|
Loading…
Reference in a new issue