-
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 3 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 |
|---|---|---|
|
|
@@ -12,13 +12,16 @@ 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 _hotReloadPath = r'$hotreload'; | ||
|
|
||
| final _logger = new Logger('Serve'); | ||
|
|
||
|
|
@@ -43,10 +46,13 @@ class ServeHandler implements BuildState { | |
| final Future<AssetHandler> _assetHandler; | ||
| final Future<AssetGraphHandler> _assetGraphHandler; | ||
|
|
||
| final WebSocketHandler _webSocketHandler = WebSocketHandler(); | ||
|
||
|
|
||
| ServeHandler._(this._state, this._assetHandler, this._assetGraphHandler, | ||
| this._rootPackage) { | ||
| _state.buildResults.listen((result) { | ||
| _lastBuildResult = result; | ||
| _webSocketHandler.emitUpdateMessage(result); | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -55,14 +61,16 @@ 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) { | ||
|
|
@@ -110,6 +118,34 @@ class ServeHandler implements BuildState { | |
| } | ||
| } | ||
|
|
||
| class WebSocketHandler { | ||
|
||
| final Set<WebSocketChannel> _connectionPull = Set(); | ||
|
||
| final shelf.Handler _internalHandler; | ||
|
|
||
| // is it OK? | ||
| // ignore: implicit_this_reference_in_initializer | ||
|
||
| WebSocketHandler() : _internalHandler = webSocketHandler(_handleConnection); | ||
|
|
||
| Future<shelf.Response> handler(shelf.Request request) async { | ||
| if (!request.url.path.startsWith(_hotReloadPath)) { | ||
| return new shelf.Response.notFound(''); | ||
| } | ||
| return _internalHandler(request); | ||
| } | ||
|
|
||
| void emitUpdateMessage(BuildResult buildResult) { | ||
| for (var webSocket in _connectionPull) { | ||
| webSocket.sink.add('update'); | ||
| } | ||
| } | ||
|
|
||
| void _handleConnection(WebSocketChannel webSocket) async { | ||
| _connectionPull.add(webSocket); | ||
| await webSocket.stream.drain(); | ||
| _connectionPull.remove(webSocket); | ||
| } | ||
| } | ||
|
|
||
| class AssetHandler { | ||
| final FinalizedReader _reader; | ||
| final String _rootPackage; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,6 +82,7 @@ Future<Null> bootstrapDdc(BuildStep buildStep, | |
| var bootstrapContent = new StringBuffer('(function() {\n'); | ||
| bootstrapContent.write(_dartLoaderSetup(modulePaths)); | ||
| bootstrapContent.write(_requireJsConfig); | ||
| bootstrapContent.write(_hotReloadConfig); | ||
|
|
||
| bootstrapContent.write(_appBootstrap( | ||
| appModuleName, appModuleScope, ignoreCastFailures, enableSyncAsync)); | ||
|
|
@@ -321,6 +322,36 @@ require.config({ | |
| }); | ||
| '''; | ||
|
|
||
| /// Hot-reload config | ||
| /// | ||
| /// Listen WebSocket for updates in build results | ||
| /// | ||
| /// Now only ilve-reload functional - just reload page on update message | ||
| final _hotReloadConfig = ''' | ||
| (function() { | ||
| var wsUrl; | ||
| if (baseUrl.startsWith('http')) { | ||
| wsUrl = baseUrl.replace('http', 'ws'); | ||
| } else { | ||
| // TODO: when it may make sense? WebWorkers? Tests? | ||
| wsUrl = 'ws://' + location.host + baseUrl; | ||
| } | ||
| wsUrl += '\$hotreload'; | ||
| try { | ||
| var ws = new WebSocket(wsUrl); | ||
| ws.onmessage = function(event) { | ||
| console.log(event); | ||
| if(event.data === 'update'){ | ||
| location.reload(); | ||
| } | ||
| }; | ||
| } catch (e) { | ||
| // Live reload might be turned off. Just ignore it for now | ||
| // TODO: Find a way to pass serve option here, to not inject this code at all, if live/hot reload is turned off | ||
|
||
| } | ||
| }()); | ||
| '''; | ||
|
|
||
| final _baseUrlScript = ''' | ||
| var baseUrl = (function () { | ||
| // Attempt to detect base url using <base href> html tag | ||
|
|
||
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: lets name this
$livereloadfor now at least