-
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 1 commit
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 |
|---|---|---|
|
|
@@ -127,10 +127,14 @@ class ServeHandler implements BuildState { | |
| /// build updates | ||
| class BuildUpdatesWebSocketHandler { | ||
| final activeConnections = <WebSocketChannel>[]; | ||
| final Function _handlerFactory; | ||
|
||
| shelf.Handler _internalHandler; | ||
|
|
||
| BuildUpdatesWebSocketHandler() { | ||
| _internalHandler = webSocketHandler(_handleConnection, protocols: [_buildUpdatesProtocol]); | ||
| BuildUpdatesWebSocketHandler([this._handlerFactory = webSocketHandler]) { | ||
| var untypedTearOff = (webSocket, protocol) => | ||
|
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. one last nit, can you document why we need to do this, and ideally point at an issue in the original package?
Contributor
Author
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. Created dart-archive/shelf_web_socket#11 |
||
| _handleConnection(webSocket as WebSocketChannel, protocol as String); | ||
| _internalHandler = _handlerFactory( | ||
| untypedTearOff, protocols: [_buildUpdatesProtocol]) as shelf.Handler; | ||
| } | ||
|
|
||
| shelf.Handler get handler => _internalHandler; | ||
|
|
@@ -149,27 +153,26 @@ class BuildUpdatesWebSocketHandler { | |
| } | ||
|
|
||
| shelf.Handler _injectBuildUpdatesClientCode(shelf.Handler innerHandler) { | ||
| return (shelf.Request request) { | ||
| return (shelf.Request request) async { | ||
| 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); | ||
| }); | ||
| var response = await innerHandler(request); | ||
| // 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 = ''' | ||
| /// Now only live-reload functional - just reload page on update message | ||
| final _buildUpdatesInjectedJS = '''\n | ||
| (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) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ import 'dart:io'; | |
|
|
||
| import 'package:logging/logging.dart'; | ||
| import 'package:shelf/shelf.dart'; | ||
| import 'package:stream_channel/stream_channel.dart'; | ||
| import 'package:test/test.dart'; | ||
|
|
||
| import 'package:build_runner/build_runner.dart'; | ||
|
|
@@ -20,6 +21,7 @@ import 'package:build_runner/src/server/server.dart'; | |
|
|
||
| import 'package:_test_common/common.dart'; | ||
| import 'package:_test_common/package_graphs.dart'; | ||
| import 'package:web_socket_channel/web_socket_channel.dart'; | ||
|
|
||
| void main() { | ||
| ServeHandler serveHandler; | ||
|
|
@@ -162,6 +164,97 @@ void main() { | |
| expect(await response.readAsString(), contains('--track-performance')); | ||
| }); | ||
| }); | ||
|
|
||
| group('build updates', () { | ||
| final _entrypointExtensionMarker = '/* ENTRYPOINT_EXTENTION_MARKER */'; | ||
|
||
|
|
||
| test('injects client code if enabled', () async { | ||
| _addSource('a|web/some.js', _entrypointExtensionMarker); | ||
|
||
| var response = await serveHandler.handlerFor('web', liveReload: true)( | ||
| new Request('GET', Uri.parse('http://server.com/some.js'))); | ||
| expect(await response.readAsString(), contains('\$livereload')); | ||
| }); | ||
|
|
||
| test('doesn\'t inject client code if disabled', () async { | ||
| _addSource('a|web/some.js', _entrypointExtensionMarker); | ||
| var response = await serveHandler.handlerFor('web', liveReload: false)( | ||
| new Request('GET', Uri.parse('http://server.com/some.js'))); | ||
| expect(await response.readAsString(), isNot(contains('\$livereload'))); | ||
| }); | ||
|
|
||
| test('doesn\'t inject client code in non-js files', () async { | ||
| _addSource('a|web/some.html', _entrypointExtensionMarker); | ||
| var response = await serveHandler.handlerFor('web', liveReload: true)( | ||
| new Request('GET', Uri.parse('http://server.com/some.html'))); | ||
| expect(await response.readAsString(), isNot(contains('\$livereload'))); | ||
| }); | ||
|
|
||
| test('doesn\'t inject client code in non-marked files', () async { | ||
| _addSource('a|web/some.js', 'content'); | ||
| var response = await serveHandler.handlerFor('web', liveReload: true)( | ||
| new Request('GET', Uri.parse('http://server.com/some.js'))); | ||
| expect(await response.readAsString(), isNot(contains('\$livereload'))); | ||
| }); | ||
|
|
||
| test('emmits a message to all listners', () async { | ||
|
||
| Function exposedOnConnect; | ||
| var mockHandlerFactory = (Function onConnect, {protocols}) { | ||
| exposedOnConnect = onConnect; | ||
| }; | ||
| var buildUpdatesWebSocketHandler = BuildUpdatesWebSocketHandler( | ||
| mockHandlerFactory); | ||
|
|
||
| var streamControllerFirst1 = StreamController<List<int>>(); | ||
|
||
| var streamControllerFirst2 = StreamController<List<int>>(); | ||
| var serverChannelFirst = WebSocketChannel(StreamChannel(streamControllerFirst1.stream, streamControllerFirst2.sink), serverSide: true); | ||
| var clientChannelFirst = WebSocketChannel(StreamChannel(streamControllerFirst2.stream, streamControllerFirst1.sink), serverSide: false); | ||
|
|
||
| var streamControllerSecond1 = StreamController<List<int>>(); | ||
| var streamControllerSecond2 = StreamController<List<int>>(); | ||
| var serverChannelSecond = WebSocketChannel(StreamChannel(streamControllerSecond1.stream, streamControllerSecond2.sink), serverSide: true); | ||
| var clientChannelSecond = WebSocketChannel(StreamChannel(streamControllerSecond2.stream, streamControllerSecond1.sink), serverSide: false); | ||
|
|
||
| var deferredExpect1 = clientChannelFirst.stream.single.then((value) => expect(value, 'update')); | ||
| var deferredExpect2 = clientChannelSecond.stream.single.then((value) => expect(value, 'update')); | ||
| exposedOnConnect(serverChannelFirst, ''); | ||
| exposedOnConnect(serverChannelSecond, ''); | ||
| buildUpdatesWebSocketHandler.emitUpdateMessage(null); | ||
| await clientChannelFirst.sink.close(); | ||
| await clientChannelSecond.sink.close(); | ||
| await deferredExpect1; | ||
| await deferredExpect2; | ||
| }); | ||
|
|
||
| test('deletes listners on disconect', () async { | ||
| Function exposedOnConnect; | ||
| var mockHandlerFactory = (Function onConnect, {protocols}) { | ||
| exposedOnConnect = onConnect; | ||
| }; | ||
| var buildUpdatesWebSocketHandler = BuildUpdatesWebSocketHandler( | ||
| mockHandlerFactory); | ||
|
|
||
| var streamControllerFirst1 = StreamController<List<int>>(); | ||
| var streamControllerFirst2 = StreamController<List<int>>(); | ||
| var serverChannelFirst = WebSocketChannel(StreamChannel(streamControllerFirst1.stream, streamControllerFirst2.sink), serverSide: true); | ||
| var clientChannelFirst = WebSocketChannel(StreamChannel(streamControllerFirst2.stream, streamControllerFirst1.sink), serverSide: false); | ||
|
|
||
| var streamControllerSecond1 = StreamController<List<int>>(); | ||
| var streamControllerSecond2 = StreamController<List<int>>(); | ||
| var serverChannelSecond = WebSocketChannel(StreamChannel(streamControllerSecond1.stream, streamControllerSecond2.sink), serverSide: true); | ||
| var clientChannelSecond = WebSocketChannel(StreamChannel(streamControllerSecond2.stream, streamControllerSecond1.sink), serverSide: false); | ||
|
|
||
| var deferredExpect1 = clientChannelFirst.stream.toList().then((value) => expect(value, ['update', 'update'])); | ||
|
||
| var deferredExpect2 = clientChannelSecond.stream.single.then((value) => expect(value, 'update')); | ||
| exposedOnConnect(serverChannelFirst, ''); | ||
| exposedOnConnect(serverChannelSecond, ''); | ||
| buildUpdatesWebSocketHandler.emitUpdateMessage(null); | ||
| await clientChannelSecond.sink.close(); | ||
| buildUpdatesWebSocketHandler.emitUpdateMessage(null); | ||
| await clientChannelFirst.sink.close(); | ||
| await deferredExpect1; | ||
| await deferredExpect2; | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| class MockWatchImpl implements WatchImpl { | ||
|
|
||
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.
a Doc comment would be good here