Skip to content

Commit 88ab2bd

Browse files
authored
Public API to support Flutter CLI (#483)
- Rename `dwds/lib/service.dart` to `dwds/lib/src/services/debug_service.dart` - Move `chrome_proxy_service.dart` and `app_debug_services.dart` to `dwds/lib/src/services` - Rename `DevConnection` to `AppConnection` and promote as a public abstraction - Create new public `DebugConnection` abstraction - Create new public entrypoint `dwds.dart` - This entrypoint exposes a `handler` to be used with shelf (see changes to `webdev`) - Update `webdev` to no longer import from `dwds`'s source This mostly completes #475. In a follow up PR I'll shuffle the tests around so there is a better separation between `webdev` and `dwds` tests. cc @jonahwilliams
1 parent 7aad2b5 commit 88ab2bd

27 files changed

+275
-141
lines changed

dwds/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- Move `data` abstractions from `package:webdev` into `package:dwds`.
44
- Move debugging related handlers from `package:webdev` into `package:dwds`.
55
- Move injected client from `package:webdev` into `package:dwds`.
6+
- Create new public entrypoint `dwds.dart`. Existing public API `services.dart`
7+
is now private.
68

79
## 0.3.3
810

dwds/lib/dwds.dart

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:build_daemon/data/build_status.dart';
8+
import 'package:logging/logging.dart';
9+
import 'package:meta/meta.dart';
10+
import 'package:shelf/shelf.dart';
11+
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
12+
13+
import 'src/connections/app_connection.dart';
14+
import 'src/connections/debug_connection.dart';
15+
import 'src/devtools.dart';
16+
import 'src/handlers/asset_handler.dart';
17+
import 'src/handlers/dev_handler.dart';
18+
import 'src/handlers/injected_handler.dart';
19+
20+
export 'src/connections/app_connection.dart' show AppConnection;
21+
export 'src/connections/debug_connection.dart' show DebugConnection;
22+
23+
typedef LogWriter = void Function(Level, String);
24+
typedef ConnectionProvider = Future<ChromeConnection> Function();
25+
enum ReloadConfiguration { none, hotReload, hotRestart, liveReload }
26+
27+
/// The Dart Web Debug Service.
28+
class Dwds {
29+
final Handler handler;
30+
final DevTools _devTools;
31+
final DevHandler _devHandler;
32+
33+
Dwds._(this.handler, this._devTools, this._devHandler);
34+
35+
Stream<AppConnection> get connectedApps => _devHandler.connectedApps;
36+
37+
Future<void> stop() async {
38+
await _devTools?.close();
39+
await _devHandler.close();
40+
}
41+
42+
Future<DebugConnection> debugConnection(AppConnection appConnection) async {
43+
var appDebugServices = await _devHandler.loadAppServices(
44+
appConnection.request.appId, appConnection.request.instanceId);
45+
return DebugConnection(appDebugServices);
46+
}
47+
48+
static Future<Dwds> start({
49+
@required String hostname,
50+
@required int applicationPort,
51+
@required int assetServerPort,
52+
@required String applicationTarget,
53+
@required ReloadConfiguration reloadConfiguration,
54+
@required Stream<BuildResult> buildResults,
55+
@required ConnectionProvider chromeConnection,
56+
@required bool serveDevTools,
57+
@required LogWriter logWriter,
58+
@required bool verbose,
59+
}) async {
60+
var assetHandler = AssetHandler(
61+
assetServerPort,
62+
applicationTarget,
63+
hostname,
64+
applicationPort,
65+
);
66+
var cascade = Cascade();
67+
var pipeline = const Pipeline();
68+
69+
pipeline =
70+
pipeline.addMiddleware(createInjectedHandler(reloadConfiguration));
71+
72+
DevTools devTools;
73+
if (serveDevTools) {
74+
devTools = await DevTools.start(hostname);
75+
logWriter(Level.INFO,
76+
'Serving DevTools at http://${devTools.hostname}:${devTools.port}\n');
77+
}
78+
var devHandler = DevHandler(
79+
chromeConnection,
80+
buildResults,
81+
devTools,
82+
assetHandler,
83+
hostname,
84+
verbose,
85+
logWriter,
86+
);
87+
cascade = cascade.add(devHandler.handler).add(assetHandler.handler);
88+
89+
return Dwds._(pipeline.addHandler(cascade.handler), devTools, devHandler);
90+
}
91+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
import 'package:sse/server/sse_handler.dart';
8+
9+
import '../../data/connect_request.dart';
10+
import '../../data/run_request.dart';
11+
import '../../data/serializers.dart';
12+
13+
/// A connection between the application loaded in the browser and DWDS.
14+
class AppConnection {
15+
/// The initial connection request sent from the application in the browser.
16+
final ConnectRequest request;
17+
18+
final SseConnection _connection;
19+
var _isStarted = false;
20+
21+
AppConnection(this.request, this._connection);
22+
23+
void runMain() {
24+
if (_isStarted) throw StateError('Main has already started.');
25+
_connection.sink.add(jsonEncode(serializers.serialize(RunRequest())));
26+
_isStarted = true;
27+
}
28+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:vm_service_lib/vm_service_lib.dart';
8+
9+
import '../services/app_debug_services.dart';
10+
11+
/// A debug connection between the application in the browser and DWDS.
12+
///
13+
/// Supports debugging your running application through the Dart VM Service
14+
/// Protocol.
15+
class DebugConnection {
16+
final AppDebugServices _appDebugServices;
17+
final _onDoneCompleter = Completer();
18+
19+
DebugConnection(this._appDebugServices) {
20+
_appDebugServices.chromeProxyService.tabConnection.onClose.first.then((_) {
21+
close();
22+
});
23+
}
24+
25+
/// The port of the host Dart VM Service.
26+
int get port => _appDebugServices.debugService.port;
27+
28+
/// The websocket endpoint of the Dart VM Service.
29+
String get wsUri => _appDebugServices.debugService.wsUri;
30+
31+
/// A client of the Dart VM Service with DWDS specific extensions.
32+
VmService get vmService => _appDebugServices.dwdsVmClient.client;
33+
34+
Future<void> close() async {
35+
if (!_onDoneCompleter.isCompleted) _onDoneCompleter.complete();
36+
await _appDebugServices.chromeProxyService.tabConnection.close();
37+
await _appDebugServices.close();
38+
}
39+
40+
Future<void> get onDone => _onDoneCompleter.future;
41+
}

dwds/lib/src/debugger.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import 'dart:async';
77
import 'package:vm_service_lib/vm_service_lib.dart';
88
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
99

10-
import 'chrome_proxy_service.dart';
1110
import 'dart_uri.dart';
1211
import 'domain.dart';
1312
import 'location.dart';
1413
import 'objects.dart';
14+
import 'services/chrome_proxy_service.dart';
1515
import 'sources.dart';
1616

1717
/// Converts from ExceptionPauseMode strings to [PauseState] enums.

dwds/lib/src/domain.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.import 'dart:async';
44

5-
import 'package:dwds/src/chrome_proxy_service.dart';
65
import 'package:vm_service_lib/vm_service_lib.dart';
76

87
import 'inspector.dart';
8+
import 'services/chrome_proxy_service.dart';
99

1010
/// A common superclass to allow implementations of different parts of the
1111
/// protocol to get access to the inspector and utility functions.

dwds/lib/src/dwds_vm_client.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
import 'dart:async';
66
import 'dart:convert';
77

8-
import 'package:dwds/service.dart';
98
import 'package:vm_service_lib/vm_service_lib.dart';
109

11-
import 'chrome_proxy_service.dart' show ChromeProxyService;
10+
import 'services/chrome_proxy_service.dart' show ChromeProxyService;
11+
import 'services/debug_service.dart';
1212

1313
// A client of the vm service that registers some custom extensions like
1414
// hotRestart.

dwds/lib/src/handlers/asset_handler.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ import 'package:shelf_proxy/shelf_proxy.dart';
1111
///
1212
/// Proxies requests to the build daemon asset server.
1313
class AssetHandler {
14-
final int _daemonPort;
14+
final int _assetServerPort;
1515
final String _target;
1616
final int _applicationPort;
1717
final String _applicationHost;
1818

1919
Handler _handler;
2020

21-
AssetHandler(this._daemonPort, this._target, this._applicationHost,
21+
AssetHandler(this._assetServerPort, this._target, this._applicationHost,
2222
this._applicationPort);
2323

2424
Handler get handler =>
25-
_handler ??= proxyHandler('http://localhost:$_daemonPort/$_target/');
25+
_handler ??= proxyHandler('http://localhost:$_assetServerPort/$_target/');
2626

2727
/// Returns the asset from a relative [path].
2828
///

dwds/lib/src/handlers/dev_handler.dart

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'dart:async';
66
import 'dart:convert';
77

88
import 'package:build_daemon/data/build_status.dart';
9-
import 'package:build_daemon/data/serializers.dart';
9+
import 'package:build_daemon/data/serializers.dart' as build_daemon;
1010
import 'package:logging/logging.dart';
1111
import 'package:pedantic/pedantic.dart';
1212
import 'package:shelf/shelf.dart';
@@ -16,13 +16,13 @@ import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
1616
import '../../data/connect_request.dart';
1717
import '../../data/devtools_request.dart';
1818
import '../../data/isolate_events.dart';
19-
import '../../data/run_request.dart';
20-
import '../../data/serializers.dart' as dwds;
21-
import '../../service.dart';
22-
import '../app_debug_services.dart';
19+
import '../../data/serializers.dart';
20+
import '../connections/app_connection.dart';
2321
import '../devtools.dart';
2422
import '../dwds_vm_client.dart';
2523
import '../handlers/asset_handler.dart';
24+
import '../services/app_debug_services.dart';
25+
import '../services/debug_service.dart';
2626

2727
/// SSE handler to enable development features like hot reload and
2828
/// opening DevTools.
@@ -33,14 +33,14 @@ class DevHandler {
3333
final DevTools _devTools;
3434
final AssetHandler _assetHandler;
3535
final String _hostname;
36-
final _connectedApps = StreamController<DevConnection>.broadcast();
36+
final _connectedApps = StreamController<AppConnection>.broadcast();
3737
final _servicesByAppId = <String, Future<AppDebugServices>>{};
3838
final Stream<BuildResult> buildResults;
3939
final bool _verbose;
4040
final void Function(Level, String) _logWriter;
4141
final Future<ChromeConnection> Function() _chromeConnection;
4242

43-
Stream<DevConnection> get connectedApps => _connectedApps.stream;
43+
Stream<AppConnection> get connectedApps => _connectedApps.stream;
4444

4545
DevHandler(
4646
this._chromeConnection,
@@ -73,7 +73,8 @@ class DevHandler {
7373
void _emitBuildResults(BuildResult result) {
7474
if (result.status != BuildStatus.succeeded) return;
7575
for (var connection in _connections) {
76-
connection.sink.add(jsonEncode(serializers.serialize(result)));
76+
connection.sink
77+
.add(jsonEncode(build_daemon.serializers.serialize(result)));
7778
}
7879
}
7980

@@ -104,20 +105,21 @@ class DevHandler {
104105
_connections.add(connection);
105106
String appId;
106107
connection.stream.listen((data) async {
107-
var message = dwds.serializers.deserialize(jsonDecode(data));
108+
var message = serializers.deserialize(jsonDecode(data));
108109
if (message is DevToolsRequest) {
109110
if (_devTools == null) {
110-
connection.sink.add(jsonEncode(dwds.serializers.serialize(
111-
DevToolsResponse((b) => b
111+
connection.sink
112+
.add(jsonEncode(serializers.serialize(DevToolsResponse((b) => b
112113
..success = false
113-
..error =
114-
'Debugging is not enabled, please pass the --debug flag '
115-
'when starting webdev.'))));
114+
..error = 'Debugging is not enabled.\n\n'
115+
'If you are using webdev please pass the --debug flag.\n'
116+
'Otherwise check the docs for the tool you are using.'))));
116117
return;
117118
}
119+
118120
if (appId != message.appId) {
119-
connection.sink.add(jsonEncode(dwds.serializers.serialize(
120-
DevToolsResponse((b) => b
121+
connection.sink.add(jsonEncode(serializers.serialize(DevToolsResponse(
122+
(b) => b
121123
..success = false
122124
..error =
123125
'App ID has changed since the connection was established. '
@@ -131,22 +133,22 @@ class DevHandler {
131133
appServices =
132134
await loadAppServices(message.appId, message.instanceId);
133135
} catch (_) {
134-
connection.sink.add(
135-
jsonEncode(dwds.serializers.serialize(DevToolsResponse((b) => b
136+
connection.sink
137+
.add(jsonEncode(serializers.serialize(DevToolsResponse((b) => b
136138
..success = false
137-
..error = 'Webdev was unable to connect debug services to your '
139+
..error = 'Unable to connect debug services to your '
138140
'application. Most likely this means you are trying to '
139141
'load in a different Chrome window than was launched by '
140-
'webdev.'))));
142+
'your development tool.'))));
141143
return;
142144
}
143145

144146
// Check if we are already running debug services for a different
145147
// instance of this app.
146148
if (appServices.connectedInstanceId != null &&
147149
appServices.connectedInstanceId != message.instanceId) {
148-
connection.sink.add(jsonEncode(dwds.serializers.serialize(
149-
DevToolsResponse((b) => b
150+
connection.sink.add(jsonEncode(serializers.serialize(DevToolsResponse(
151+
(b) => b
150152
..success = false
151153
..error =
152154
'This app is already being debugged in a different tab. '
@@ -164,8 +166,8 @@ class DevHandler {
164166
await loadAppServices(message.appId, message.instanceId);
165167
}
166168

167-
connection.sink.add(jsonEncode(dwds.serializers
168-
.serialize(DevToolsResponse((b) => b..success = true))));
169+
connection.sink.add(jsonEncode(
170+
serializers.serialize(DevToolsResponse((b) => b..success = true))));
169171

170172
appServices.connectedInstanceId = message.instanceId;
171173
await appServices.chromeProxyService.tabConnection
@@ -195,7 +197,7 @@ class DevHandler {
195197
}
196198
}
197199

198-
_connectedApps.add(DevConnection(message, connection));
200+
_connectedApps.add(AppConnection(message, connection));
199201
} else if (message is IsolateExit) {
200202
(await loadAppServices(message.appId, message.instanceId))
201203
?.chromeProxyService
@@ -257,18 +259,3 @@ Future<bool> _isCorrectTab(
257259
await tabConnection.runtime.evaluate(r'window["$dartAppInstanceId"];');
258260
return result.value == instanceId;
259261
}
260-
261-
class DevConnection {
262-
final ConnectRequest request;
263-
final SseConnection _connection;
264-
var _isStarted = false;
265-
DevConnection(this.request, this._connection);
266-
267-
void runMain() {
268-
if (!_isStarted) {
269-
_connection.sink
270-
.add(jsonEncode(dwds.serializers.serialize(RunRequest())));
271-
}
272-
_isStarted = true;
273-
}
274-
}

dwds/lib/src/handlers/injected_handler.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'dart:isolate';
99
import 'package:crypto/crypto.dart';
1010
import 'package:shelf/shelf.dart';
1111

12-
import '../injected/configuration.dart';
12+
import '../../dwds.dart';
1313

1414
/// File extension that build_web_compilers will place the
1515
/// [entrypointExtensionMarker] in.

0 commit comments

Comments
 (0)