| // Copyright 2020 The Dart Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:async/async.dart'; |
| import 'package:frontend_server_client/frontend_server_client.dart'; |
| import 'package:path/path.dart' as p; |
| import 'package:shelf/shelf.dart'; |
| import 'package:shelf/shelf_io.dart' as shelf_io; |
| import 'package:shelf_packages_handler/shelf_packages_handler.dart'; |
| import 'package:shelf_static/shelf_static.dart'; |
| |
| void main(List<String> args) async { |
| try { |
| watch.start(); |
| if (args.isNotEmpty) { |
| throw ArgumentError('No command line args are supported'); |
| } |
| |
| _print('compiling the dart sdk'); |
| var sdkCompileResult = await Process.run(Platform.resolvedExecutable, [ |
| p.join(sdkDir, 'bin', 'snapshots', 'dartdevc.dart.snapshot'), |
| '--multi-root-scheme=org-dartlang-sdk', |
| '--modules=amd', |
| '--module-name=dart_sdk', |
| '--sound-null-safety', |
| '-o', |
| dartSdkJs, |
| p.url.join(sdkDir, sdkKernelPath), |
| ]); |
| if (sdkCompileResult.exitCode != 0) { |
| _print('Failed to compile the dart sdk to JS:\n' |
| '${sdkCompileResult.stdout}\n' |
| '${sdkCompileResult.stderr}'); |
| exit(sdkCompileResult.exitCode); |
| } |
| |
| _print('starting frontend server'); |
| var client = await DartDevcFrontendServerClient.start( |
| 'org-dartlang-root:///$app', outputDill, |
| fileSystemRoots: [p.current], |
| fileSystemScheme: 'org-dartlang-root', |
| platformKernel: p.toUri(sdkKernelPath).toString(), |
| verbose: true); |
| |
| _print('compiling $app'); |
| await client.compile([]); |
| client.accept(); |
| _print('done compiling $app'); |
| |
| _print('starting shelf server'); |
| var cascade = Cascade() |
| .add(_clientHandler(client)) |
| .add(createStaticHandler(p.current)) |
| .add(createFileHandler(dartSdkJs, url: 'example/app/dart_sdk.js')) |
| .add(createFileHandler( |
| p.join(sdkDir, 'lib', 'dev_compiler', 'web', |
| 'dart_stack_trace_mapper.js'), |
| url: 'example/app/dart_stack_trace_mapper.js')) |
| .add(createFileHandler( |
| p.join(sdkDir, 'lib', 'dev_compiler', 'amd', 'require.js'), |
| url: 'example/app/require.js')) |
| .add(packagesDirHandler()); |
| final server = await shelf_io.serve(cascade.handler, 'localhost', 8080); |
| _print('server ready'); |
| |
| // The file we will be editing in the repl |
| var appFile = File(app); |
| var originalContent = await appFile.readAsString(); |
| var appLines = const LineSplitter().convert(originalContent); |
| var getterText = 'String get message =>'; |
| var messageLine = |
| appLines.indexWhere((line) => line.startsWith(getterText)); |
| |
| var stdinQueue = StreamQueue( |
| stdin.transform(utf8.decoder).transform(const LineSplitter())); |
| _prompt(); |
| while (await stdinQueue.hasNext) { |
| var newMessage = await stdinQueue.next; |
| if (newMessage == 'quit') { |
| await server.close(); |
| await stdinQueue.cancel(); |
| break; |
| } else if (newMessage == 'reset') { |
| print('resetting'); |
| client.reset(); |
| _print('restoring $app'); |
| await appFile.writeAsString(originalContent); |
| } else { |
| _print('editing $app'); |
| appLines[messageLine] = '$getterText "$newMessage";'; |
| var newContent = appLines.join('\n'); |
| await appFile.writeAsString(newContent); |
| |
| _print('recompiling $app with edits'); |
| var result = |
| await client.compile([Uri.parse('org-dartlang-root:///$app')]); |
| if (result.errorCount > 0) { |
| print('Compile errors: \n${result.compilerOutputLines.join('\n')}'); |
| await client.reject(); |
| } else { |
| _print('Recompile succeeded for $app'); |
| client.accept(); |
| // TODO: support hot restart |
| print('reload app to see the new message'); |
| } |
| } |
| |
| _prompt(); |
| } |
| |
| _print('restoring $app'); |
| await appFile.writeAsString(originalContent); |
| _print('exiting'); |
| await client.shutdown(); |
| } finally { |
| Directory(p.join('.dart_tool', 'out')).deleteSync(recursive: true); |
| } |
| } |
| |
| Handler _clientHandler(DartDevcFrontendServerClient client) { |
| return (Request request) { |
| var path = request.url.path; |
| var packagesIndex = path.indexOf('/packages/'); |
| if (packagesIndex > 0) { |
| path = request.url.path.substring(packagesIndex); |
| } else { |
| path = request.url.path; |
| } |
| if (!path.startsWith('/')) path = '/$path'; |
| if (path.endsWith('.dart.js') && path != '/example/app/main.dart.js') { |
| path = path.replaceFirst('.dart.js', '.dart.lib.js', path.length - 8); |
| } |
| var assetBytes = client.assetBytes(path); |
| if (assetBytes == null) return Response.notFound('path not found'); |
| return Response.ok(assetBytes, |
| headers: {HttpHeaders.contentTypeHeader: 'application/javascript'}); |
| }; |
| } |
| |
| void _print(String message) { |
| print('${watch.elapsed}: $message'); |
| } |
| |
| void _prompt() => stdout.write( |
| 'Enter a new message to print and recompile, or type `quit` to exit:'); |
| |
| final app = 'example/app/main.dart'; |
| final dartSdkJs = p.join('.dart_tool', 'out', 'dart_sdk.js'); |
| final outputDill = p.join('.dart_tool', 'out', 'example_app.dill'); |
| final sdkDir = p.dirname(p.dirname(Platform.resolvedExecutable)); |
| final sdkKernelPath = p.join(sdkDir, 'lib', '_internal', 'ddc_platform.dill'); |
| final watch = Stopwatch(); |