Skip to content

Commit 0f154df

Browse files
authored
fix the example frontend server web app (#1694)
Fixes #1546 There are definitely some hacky-ish things here in terms of the routing, but it works and that isn't the focus of the example.
1 parent ef52eda commit 0f154df

File tree

9 files changed

+318
-214
lines changed

9 files changed

+318
-214
lines changed

.github/workflows/dart.yml

Lines changed: 78 additions & 77 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!DOCTYPE html>
22
<html>
33
<head>
4-
<script src="main.dart.js" type="application/javascript"></script>
4+
<script defer src="main.dart.js" type="application/javascript"></script>
55
</head>
6-
</html>
6+
</html>

frontend_server_client/example/vm_client.dart

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,68 +12,80 @@ import 'package:vm_service/vm_service.dart';
1212
import 'package:vm_service/vm_service_io.dart';
1313

1414
void main(List<String> args) async {
15-
watch.start();
16-
if (args.isNotEmpty) {
17-
throw ArgumentError('No command line args are supported');
18-
}
15+
try {
16+
watch.start();
17+
if (args.isNotEmpty) {
18+
throw ArgumentError('No command line args are supported');
19+
}
1920

20-
var client = await FrontendServerClient.start('org-dartlang-root:///$app',
21-
outputDill, p.join(sdkDir, 'lib', '_internal', 'vm_platform_strong.dill'),
22-
target: 'vm',
23-
fileSystemRoots: [p.current],
24-
fileSystemScheme: 'org-dartlang-root',
25-
verbose: true);
26-
_print('compiling $app');
27-
var result = await client.compile();
28-
client.accept();
29-
_print('done compiling $app');
21+
var client = await FrontendServerClient.start(
22+
'org-dartlang-root:///$app',
23+
outputDill,
24+
p.join(sdkDir, 'lib', '_internal', 'vm_platform_strong.dill'),
25+
target: 'vm',
26+
fileSystemRoots: [p.url.current],
27+
fileSystemScheme: 'org-dartlang-root',
28+
verbose: true);
29+
_print('compiling $app');
30+
var result = await client.compile();
31+
client.accept();
32+
_print('done compiling $app');
3033

31-
Process appProcess;
32-
final vmServiceCompleter = Completer<VmService>();
33-
appProcess = await Process.start(Platform.resolvedExecutable,
34-
['--observe', '--no-pause-isolates-on-exit', result.dillOutput!]);
35-
appProcess.stdout
36-
.transform(utf8.decoder)
37-
.transform(const LineSplitter())
38-
.listen((line) {
39-
stdout.writeln('APP -> $line');
40-
if (line.startsWith('Observatory listening on')) {
41-
var observatoryUri =
42-
'${line.split(' ').last.replaceFirst('http', 'ws')}ws';
43-
vmServiceCompleter.complete(vmServiceConnectUri(observatoryUri));
44-
}
45-
});
46-
appProcess.stderr
47-
.transform(utf8.decoder)
48-
.transform(const LineSplitter())
49-
.listen((line) {
50-
stderr.writeln('APP -> $line');
51-
});
34+
Process appProcess;
35+
final vmServiceCompleter = Completer<VmService>();
36+
appProcess = await Process.start(Platform.resolvedExecutable,
37+
['--enable-vm-service', result.dillOutput!]);
38+
final sawHelloWorld = Completer();
39+
appProcess.stdout
40+
.transform(utf8.decoder)
41+
.transform(const LineSplitter())
42+
.listen((line) {
43+
stdout.writeln('APP -> $line');
44+
if (line == 'hello/world') {
45+
sawHelloWorld.complete();
46+
}
47+
if (line.startsWith(
48+
'The Dart DevTools debugger and profiler is available at:')) {
49+
var observatoryUri =
50+
'${line.split(' ').last.replaceFirst('http', 'ws')}ws';
51+
vmServiceCompleter.complete(vmServiceConnectUri(observatoryUri));
52+
}
53+
});
54+
appProcess.stderr
55+
.transform(utf8.decoder)
56+
.transform(const LineSplitter())
57+
.listen((line) {
58+
stderr.writeln('APP -> $line');
59+
});
5260

53-
final vmService = await vmServiceCompleter.future;
61+
final vmService = await vmServiceCompleter.future;
62+
await sawHelloWorld.future;
5463

55-
_print('editing $app');
56-
var appFile = File(app);
57-
var originalContent = await appFile.readAsString();
58-
var newContent = originalContent.replaceFirst('hello', 'goodbye');
59-
await appFile.writeAsString(newContent);
64+
_print('editing $app');
65+
var appFile = File(app);
66+
var originalContent = await appFile.readAsString();
67+
var newContent = originalContent.replaceFirst('hello', 'goodbye');
68+
await appFile.writeAsString(newContent);
6069

61-
_print('recompiling $app with edits');
62-
result = await client.compile([Uri.parse('org-dartlang-root:///$app')]);
63-
client.accept();
64-
_print('done recompiling $app');
65-
_print('reloading $app');
66-
var vm = await vmService.getVM();
67-
await vmService.reloadSources(vm.isolates!.first.id!,
68-
rootLibUri: result.dillOutput!);
70+
_print('recompiling $app with edits');
71+
result = await client.compile([Uri.parse('org-dartlang-root:///$app')]);
72+
client.accept();
73+
_print('done recompiling $app');
74+
_print('reloading $app');
75+
var vm = await vmService.getVM();
76+
await vmService.reloadSources(vm.isolates!.first.id!,
77+
rootLibUri: result.dillOutput!);
6978

70-
_print('restoring $app to original contents');
71-
await appFile.writeAsString(originalContent);
72-
_print('exiting');
73-
await client.shutdown().timeout(const Duration(seconds: 1), onTimeout: () {
74-
client.kill();
75-
return 1;
76-
});
79+
_print('restoring $app to original contents');
80+
await appFile.writeAsString(originalContent);
81+
_print('exiting');
82+
await client.shutdown().timeout(const Duration(seconds: 1), onTimeout: () {
83+
client.kill();
84+
return 1;
85+
});
86+
} finally {
87+
Directory(p.join('.dart_tool', 'out')).deleteSync(recursive: true);
88+
}
7789
}
7890

7991
void _print(String message) {

frontend_server_client/example/web_client.dart

Lines changed: 115 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -14,93 +14,128 @@ import 'package:shelf_packages_handler/shelf_packages_handler.dart';
1414
import 'package:shelf_static/shelf_static.dart';
1515

1616
void main(List<String> args) async {
17-
watch.start();
18-
if (args.isNotEmpty) {
19-
throw ArgumentError('No command line args are supported');
20-
}
17+
try {
18+
watch.start();
19+
if (args.isNotEmpty) {
20+
throw ArgumentError('No command line args are supported');
21+
}
2122

22-
var client = await DartDevcFrontendServerClient.start(
23-
'org-dartlang-root:///$app', outputDill,
24-
fileSystemRoots: [p.current],
25-
fileSystemScheme: 'org-dartlang-root',
26-
verbose: true);
27-
28-
_print('compiling $app');
29-
await client.compile([]);
30-
client.accept();
31-
_print('done compiling $app');
32-
33-
_print('starting shelf server');
34-
var cascade = Cascade()
35-
.add(_clientHandler(client))
36-
.add(createStaticHandler(p.current))
37-
.add(createFileHandler(
38-
p.join(sdkDir, 'lib', 'dev_compiler', 'kernel', 'amd', 'dart_sdk.js'),
39-
url: 'example/app/dart_sdk.js'))
40-
.add(createFileHandler(
41-
p.join(sdkDir, 'lib', 'dev_compiler', 'web',
42-
'dart_stack_trace_mapper.js'),
43-
url: 'example/app/dart_stack_trace_mapper.js'))
44-
.add(createFileHandler(
45-
p.join(sdkDir, 'lib', 'dev_compiler', 'kernel', 'amd', 'require.js'),
46-
url: 'example/app/require.js'))
47-
.add(packagesDirHandler());
48-
final server = await shelf_io.serve(cascade.handler, 'localhost', 8080);
49-
_print('server ready');
50-
51-
// The file we will be editing in the repl
52-
var appFile = File(app);
53-
var originalContent = await appFile.readAsString();
54-
var appLines = const LineSplitter().convert(originalContent);
55-
var getterText = 'String get message =>';
56-
var messageLine = appLines.indexWhere((line) => line.startsWith(getterText));
57-
58-
var stdinQueue = StreamQueue(
59-
stdin.transform(utf8.decoder).transform(const LineSplitter()));
60-
_prompt();
61-
while (await stdinQueue.hasNext) {
62-
var newMessage = await stdinQueue.next;
63-
if (newMessage == 'quit') {
64-
await server.close();
65-
await stdinQueue.cancel();
66-
break;
67-
} else if (newMessage == 'reset') {
68-
print('resetting');
69-
client.reset();
70-
_print('restoring $app');
71-
await appFile.writeAsString(originalContent);
72-
} else {
73-
_print('editing $app');
74-
appLines[messageLine] = '$getterText "$newMessage";';
75-
var newContent = appLines.join('\n');
76-
await appFile.writeAsString(newContent);
77-
78-
_print('recompiling $app with edits');
79-
var result =
80-
await client.compile([Uri.parse('org-dartlang-root:///$app')]);
81-
if (result.errorCount > 0) {
82-
print('Compile errors: \n${result.compilerOutputLines.join('\n')}');
83-
await client.reject();
23+
_print('compiling the dart sdk');
24+
var sdkCompileResult = await Process.run(Platform.resolvedExecutable, [
25+
p.join(sdkDir, 'bin', 'snapshots', 'dartdevc.dart.snapshot'),
26+
'--multi-root-scheme=org-dartlang-sdk',
27+
'--modules=amd',
28+
'--module-name=dart_sdk',
29+
'--sound-null-safety',
30+
'-o',
31+
dartSdkJs,
32+
p.url.join(sdkDir, sdkKernelPath),
33+
]);
34+
if (sdkCompileResult.exitCode != 0) {
35+
_print('Failed to compile the dart sdk to JS:\n'
36+
'${sdkCompileResult.stdout}\n'
37+
'${sdkCompileResult.stderr}');
38+
exit(sdkCompileResult.exitCode);
39+
}
40+
41+
_print('starting frontend server');
42+
var client = await DartDevcFrontendServerClient.start(
43+
'org-dartlang-root:///$app', outputDill,
44+
fileSystemRoots: [p.current],
45+
fileSystemScheme: 'org-dartlang-root',
46+
platformKernel: p.toUri(sdkKernelPath).toString(),
47+
verbose: true);
48+
49+
_print('compiling $app');
50+
await client.compile([]);
51+
client.accept();
52+
_print('done compiling $app');
53+
54+
_print('starting shelf server');
55+
var cascade = Cascade()
56+
.add(_clientHandler(client))
57+
.add(createStaticHandler(p.current))
58+
.add(createFileHandler(dartSdkJs, url: 'example/app/dart_sdk.js'))
59+
.add(createFileHandler(
60+
p.join(sdkDir, 'lib', 'dev_compiler', 'web',
61+
'dart_stack_trace_mapper.js'),
62+
url: 'example/app/dart_stack_trace_mapper.js'))
63+
.add(createFileHandler(
64+
p.join(
65+
sdkDir, 'lib', 'dev_compiler', 'kernel', 'amd', 'require.js'),
66+
url: 'example/app/require.js'))
67+
.add(packagesDirHandler());
68+
final server = await shelf_io.serve(cascade.handler, 'localhost', 8080);
69+
_print('server ready');
70+
71+
// The file we will be editing in the repl
72+
var appFile = File(app);
73+
var originalContent = await appFile.readAsString();
74+
var appLines = const LineSplitter().convert(originalContent);
75+
var getterText = 'String get message =>';
76+
var messageLine =
77+
appLines.indexWhere((line) => line.startsWith(getterText));
78+
79+
var stdinQueue = StreamQueue(
80+
stdin.transform(utf8.decoder).transform(const LineSplitter()));
81+
_prompt();
82+
while (await stdinQueue.hasNext) {
83+
var newMessage = await stdinQueue.next;
84+
if (newMessage == 'quit') {
85+
await server.close();
86+
await stdinQueue.cancel();
87+
break;
88+
} else if (newMessage == 'reset') {
89+
print('resetting');
90+
client.reset();
91+
_print('restoring $app');
92+
await appFile.writeAsString(originalContent);
8493
} else {
85-
_print('Recompile succeeded for $app');
86-
client.accept();
87-
// TODO: support hot restart
88-
print('reload app to see the new message');
94+
_print('editing $app');
95+
appLines[messageLine] = '$getterText "$newMessage";';
96+
var newContent = appLines.join('\n');
97+
await appFile.writeAsString(newContent);
98+
99+
_print('recompiling $app with edits');
100+
var result =
101+
await client.compile([Uri.parse('org-dartlang-root:///$app')]);
102+
if (result.errorCount > 0) {
103+
print('Compile errors: \n${result.compilerOutputLines.join('\n')}');
104+
await client.reject();
105+
} else {
106+
_print('Recompile succeeded for $app');
107+
client.accept();
108+
// TODO: support hot restart
109+
print('reload app to see the new message');
110+
}
89111
}
112+
113+
_prompt();
90114
}
91115

92-
_prompt();
116+
_print('restoring $app');
117+
await appFile.writeAsString(originalContent);
118+
_print('exiting');
119+
await client.shutdown();
120+
} finally {
121+
Directory(p.join('.dart_tool', 'out')).deleteSync(recursive: true);
93122
}
94-
95-
_print('restoring $app');
96-
await appFile.writeAsString(originalContent);
97-
_print('exiting');
98-
await client.shutdown();
99123
}
100124

101125
Handler _clientHandler(DartDevcFrontendServerClient client) {
102126
return (Request request) {
103-
var assetBytes = client.assetBytes(request.requestedUri.path);
127+
var path = request.url.path;
128+
var packagesIndex = path.indexOf('/packages/');
129+
if (packagesIndex > 0) {
130+
path = request.url.path.substring(packagesIndex);
131+
} else {
132+
path = request.url.path;
133+
}
134+
if (!path.startsWith('/')) path = '/$path';
135+
if (path.endsWith('.dart.js') && path != '/example/app/main.dart.js') {
136+
path = path.replaceFirst('.dart.js', '.dart.lib.js', path.length - 8);
137+
}
138+
var assetBytes = client.assetBytes(path);
104139
if (assetBytes == null) return Response.notFound('path not found');
105140
return Response.ok(assetBytes,
106141
headers: {HttpHeaders.contentTypeHeader: 'application/javascript'});
@@ -115,6 +150,9 @@ void _prompt() => stdout.write(
115150
'Enter a new message to print and recompile, or type `quit` to exit:');
116151

117152
final app = 'example/app/main.dart';
153+
final dartSdkJs = p.join('.dart_tool', 'out', 'dart_sdk.js');
118154
final outputDill = p.join('.dart_tool', 'out', 'example_app.dill');
119155
final sdkDir = p.dirname(p.dirname(Platform.resolvedExecutable));
156+
final sdkKernelPath =
157+
p.join(sdkDir, 'lib', '_internal', 'ddc_platform_sound.dill');
120158
final watch = Stopwatch();

frontend_server_client/mono_pkg.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ stages:
66
- analyze: --fatal-infos .
77
sdk: dev
88
- unit_test:
9-
- test:
9+
- test: -j 1
1010
sdk:
1111
- stable
1212
- dev

frontend_server_client/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dev_dependencies:
1919
shelf_static: ^1.1.0
2020
test: ^1.16.0
2121
test_descriptor: ^2.0.0
22+
test_process: ^2.0.0
2223
vm_service: ^8.0.0
2324

2425
dependency_overrides:

0 commit comments

Comments
 (0)