-
Notifications
You must be signed in to change notification settings - Fork 220
Add simple live-reload implementation #1703
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
0c3d126
867252e
c18faa2
abe6af9
60890f4
f114ee5
12ceef4
716ddcd
03569c5
7374c99
6a581ec
86ecfc8
3c03e19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| ## 0.10.1-dev | ||
|
|
||
| - Added `--live-reload` cli option and appropriate functionality | ||
|
|
||
| ## 0.10.0 | ||
|
|
||
| ### Breaking Changes | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,13 +12,18 @@ import 'package:logging/logging.dart'; | |
| import 'package:mime/mime.dart'; | ||
| import 'package:path/path.dart' as p; | ||
| import 'package:shelf/shelf.dart' as shelf; | ||
| import 'package:shelf_web_socket/shelf_web_socket.dart'; | ||
| import 'package:web_socket_channel/web_socket_channel.dart'; | ||
|
|
||
| import '../generate/watch_impl.dart'; | ||
| import 'asset_graph_handler.dart'; | ||
| import 'path_to_asset_id.dart'; | ||
|
|
||
| const _performancePath = r'$perf'; | ||
| final _graphPath = r'$graph'; | ||
| final _buildUpdatesProtocol = r'$livereload'; | ||
| final _buildUpdatesMessage = 'update'; | ||
| final _entrypointExtensionMarker = '/* ENTRYPOINT_EXTENTION_MARKER */'; | ||
|
|
||
| final _logger = new Logger('Serve'); | ||
|
|
||
|
|
@@ -43,10 +48,13 @@ class ServeHandler implements BuildState { | |
| final Future<AssetHandler> _assetHandler; | ||
| final Future<AssetGraphHandler> _assetGraphHandler; | ||
|
|
||
| final _webSocketHandler = BuildUpdatesWebSocketHandler(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we're getting a little inconsistent about @jakemac53 - should we
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am in favor of just doing a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes we could add |
||
|
|
||
| ServeHandler._(this._state, this._assetHandler, this._assetGraphHandler, | ||
| this._rootPackage) { | ||
| _state.buildResults.listen((result) { | ||
| _lastBuildResult = result; | ||
| _webSocketHandler.emitUpdateMessage(result); | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -55,14 +63,17 @@ class ServeHandler implements BuildState { | |
| @override | ||
| Stream<BuildResult> get buildResults => _state.buildResults; | ||
|
|
||
| shelf.Handler handlerFor(String rootDir, {bool logRequests}) { | ||
| shelf.Handler handlerFor(String rootDir, | ||
| {bool logRequests, bool liveReload}) { | ||
| liveReload ??= false; | ||
| logRequests ??= false; | ||
| if (p.url.split(rootDir).length != 1) { | ||
| throw new ArgumentError.value( | ||
| rootDir, 'rootDir', 'Only top level directories are supported'); | ||
| } | ||
| _state.currentBuild.then((_) => _warnForEmptyDirectory(rootDir)); | ||
| var cascade = new shelf.Cascade() | ||
| var cascade = new shelf.Cascade(); | ||
| cascade = (liveReload ? cascade.add(_webSocketHandler.handler) : cascade) | ||
| .add(_blockOnCurrentBuild) | ||
| .add((shelf.Request request) async { | ||
| if (request.url.path == _performancePath) { | ||
|
|
@@ -76,12 +87,14 @@ class ServeHandler implements BuildState { | |
| var assetHandler = await _assetHandler; | ||
| return assetHandler.handle(request, rootDir); | ||
| }); | ||
| var handler = logRequests | ||
| ? const shelf.Pipeline() | ||
| .addMiddleware(_logRequests) | ||
| .addHandler(cascade.handler) | ||
| : cascade.handler; | ||
| return handler; | ||
| var pipeline = shelf.Pipeline(); | ||
| if (logRequests) { | ||
| pipeline = pipeline.addMiddleware(_logRequests); | ||
| } | ||
| if (liveReload) { | ||
| pipeline = pipeline.addMiddleware(_injectBuildUpdatesClientCode); | ||
| } | ||
| return pipeline.addHandler(cascade.handler); | ||
| } | ||
|
|
||
| Future<shelf.Response> _blockOnCurrentBuild(_) async { | ||
|
|
@@ -110,6 +123,64 @@ class ServeHandler implements BuildState { | |
| } | ||
| } | ||
|
|
||
| /// Class that manages web socket connection handler to inform clients about | ||
| /// build updates | ||
| class BuildUpdatesWebSocketHandler { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a Doc comment would be good here |
||
| final activeConnections = <WebSocketChannel>[]; | ||
| shelf.Handler _internalHandler; | ||
|
|
||
| BuildUpdatesWebSocketHandler() { | ||
| _internalHandler = webSocketHandler(_handleConnection, protocols: [_buildUpdatesProtocol]); | ||
| } | ||
|
|
||
| shelf.Handler get handler => _internalHandler; | ||
|
|
||
| void emitUpdateMessage(BuildResult buildResult) { | ||
| for (var webSocket in activeConnections) { | ||
| webSocket.sink.add(_buildUpdatesMessage); | ||
| } | ||
| } | ||
|
|
||
| void _handleConnection(WebSocketChannel webSocket, String protocol) async { | ||
| activeConnections.add(webSocket); | ||
| await webSocket.stream.drain(); | ||
| activeConnections.remove(webSocket); | ||
| } | ||
| } | ||
|
|
||
| shelf.Handler _injectBuildUpdatesClientCode(shelf.Handler innerHandler) { | ||
| return (shelf.Request request) { | ||
| if (!request.url.path.endsWith('.js')) { | ||
| return innerHandler(request); | ||
| } | ||
| return Future.sync(() => innerHandler(request)).then((response) async { | ||
|
||
| // TODO: Find a way how to check and/or modify body without reading it whole | ||
|
||
| var body = await response.readAsString(); | ||
| if (body.startsWith(_entrypointExtensionMarker)) { | ||
| body += _buildUpdatesInjectedJS; | ||
|
||
| } | ||
| return response.change(body: body); | ||
| }); | ||
| }; | ||
| } | ||
|
|
||
| /// Hot-reload config | ||
| /// | ||
| /// Listen WebSocket for updates in build results | ||
| /// | ||
| /// Now only ilve-reload functional - just reload page on update message | ||
|
||
| final _buildUpdatesInjectedJS = ''' | ||
| (function() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh also its probably worth adding a comment here |
||
| var ws = new WebSocket('ws://' + location.host, ['$_buildUpdatesProtocol']); | ||
| ws.onmessage = function(event) { | ||
| console.log(event); | ||
| if(event.data === '$_buildUpdatesMessage'){ | ||
| location.reload(); | ||
| } | ||
| }; | ||
| }()); | ||
| '''; | ||
|
|
||
| class AssetHandler { | ||
| final FinalizedReader _reader; | ||
| final String _rootPackage; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| name: build_runner | ||
| version: 0.10.0 | ||
| version: 0.10.1-dev | ||
| description: Tools to write binaries that run builders. | ||
| author: Dart Team <[email protected]> | ||
| homepage: https://github.com/dart-lang/build/tree/master/build_runner | ||
|
|
@@ -34,9 +34,11 @@ dependencies: | |
| pub_semver: ^1.4.0 | ||
| pubspec_parse: ^0.1.0 | ||
| shelf: ">=0.6.5 <0.8.0" | ||
| shelf_web_socket: ^0.2.2+3 | ||
| stack_trace: ^1.9.0 | ||
| stream_transform: ^0.0.9 | ||
| watcher: ^0.9.7 | ||
| web_socket_channel: ^1.0.9 | ||
| yaml: ^2.1.0 | ||
|
|
||
| dev_dependencies: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Don't include an extra newline between bullet points.