| // 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. |
| |
| // @dart = 2.9 |
| |
| // Note: this is a copy from flutter tools, updated to work with dwds tests |
| |
| import 'dart:io'; |
| |
| import 'package:dwds/dwds.dart'; |
| import 'package:file/file.dart'; |
| import 'package:logging/logging.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:path/path.dart' as p; |
| |
| import 'asset.dart'; |
| import 'asset_server.dart'; |
| import 'bootstrap.dart'; |
| import 'devfs_content.dart'; |
| import 'frontend_server_client.dart'; |
| import 'utilities.dart'; |
| |
| final String dartWebSdkPath = p.join(dartSdkPath, 'lib', 'dev_compiler'); |
| |
| Logger _logger = Logger('WebDevFs'); |
| |
| class WebDevFS { |
| WebDevFS({ |
| this.fileSystem, |
| this.hostname, |
| this.port, |
| this.packagesFilePath, |
| this.packagesPath, |
| this.root, |
| this.urlTunneller, |
| }); |
| |
| final FileSystem fileSystem; |
| TestAssetServer assetServer; |
| final String hostname; |
| final int port; |
| final String packagesFilePath; |
| final String packagesPath; |
| final String root; |
| final UrlEncoder urlTunneller; |
| Directory _savedCurrentDirectory; |
| List<Uri> sources; |
| |
| Future<Uri> create() async { |
| _savedCurrentDirectory = fileSystem.currentDirectory; |
| fileSystem.currentDirectory = packagesPath; |
| assetServer = await TestAssetServer.start( |
| fileSystem, root, hostname, port, urlTunneller); |
| return Uri.parse('http://$hostname:$port'); |
| } |
| |
| Future<void> dispose() { |
| fileSystem.currentDirectory = _savedCurrentDirectory; |
| return assetServer.close(); |
| } |
| |
| Future<UpdateFSReport> update({ |
| String mainPath, |
| AssetBundle bundle, |
| String dillOutputPath, |
| @required ResidentCompiler generator, |
| List<Uri> invalidatedFiles, |
| }) async { |
| assert(generator != null); |
| var outputDirectoryPath = fileSystem.file(mainPath).parent.path; |
| var entryPoint = mainPath; |
| |
| assetServer.writeFile( |
| '/main.dart.js', |
| generateBootstrapScript( |
| requireUrl: _filePathToUriFragment(requireJS.path), |
| mapperUrl: _filePathToUriFragment(stackTraceMapper.path), |
| entrypoint: entryPoint, |
| ), |
| ); |
| assetServer.writeFile( |
| '/main_module.bootstrap.js', |
| generateMainModule( |
| entrypoint: entryPoint, |
| ), |
| ); |
| |
| assetServer.writeFile('/main_module.digests', '{}'); |
| assetServer.writeFile('/dart_sdk.js', dartSdk.readAsStringSync()); |
| assetServer.writeFile( |
| '/dart_sdk.js.map', dartSdkSourcemap.readAsStringSync()); |
| // TODO(jonahwilliams): refactor the asset code in this and the regular devfs to |
| // be shared. |
| if (bundle != null) { |
| await writeBundle( |
| fileSystem.directory(p.joinAll(['build', 'assets'])), |
| bundle.entries, |
| ); |
| } |
| |
| generator.reset(); |
| |
| var compilerOutput = await generator.recompile( |
| 'org-dartlang-app:///$mainPath', |
| invalidatedFiles, |
| outputPath: p.join(dillOutputPath, 'app.dill'), |
| packagesFilePath: packagesFilePath, |
| ); |
| if (compilerOutput == null || compilerOutput.errorCount > 0) { |
| return UpdateFSReport(success: false); |
| } |
| |
| // list of sources that needs to be monitored are in [compilerOutput.sources] |
| sources = compilerOutput.sources; |
| File codeFile; |
| File manifestFile; |
| File sourcemapFile; |
| File metadataFile; |
| List<String> modules; |
| try { |
| var parentDirectory = fileSystem.directory(outputDirectoryPath); |
| codeFile = |
| parentDirectory.childFile('${compilerOutput.outputFilename}.sources'); |
| manifestFile = |
| parentDirectory.childFile('${compilerOutput.outputFilename}.json'); |
| sourcemapFile = |
| parentDirectory.childFile('${compilerOutput.outputFilename}.map'); |
| metadataFile = parentDirectory |
| .childFile('${compilerOutput.outputFilename}.metadata'); |
| modules = assetServer.write( |
| codeFile, manifestFile, sourcemapFile, metadataFile); |
| } on FileSystemException catch (err) { |
| throw Exception('Failed to load recompiled sources:\n$err'); |
| } |
| return UpdateFSReport( |
| success: true, |
| syncedBytes: codeFile.lengthSync(), |
| invalidatedSourcesCount: invalidatedFiles.length, |
| )..invalidatedModules = modules; |
| } |
| |
| File get requireJS => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'kernel', |
| 'amd', |
| 'require.js', |
| )); |
| |
| File get dartSdk => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'kernel', |
| 'amd', |
| 'dart_sdk.js', |
| )); |
| |
| File get dartSdkSourcemap => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'kernel', |
| 'amd', |
| 'dart_sdk.js.map', |
| )); |
| |
| File get stackTraceMapper => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'web', |
| 'dart_stack_trace_mapper.js', |
| )); |
| } |
| |
| String _filePathToUriFragment(String path) { |
| if (Platform.isWindows) { |
| var startWithSlash = path.startsWith('/'); |
| var partial = |
| fileSystem.path.split(path).skip(startWithSlash ? 2 : 1).join('/'); |
| if (partial.startsWith('/')) { |
| return partial; |
| } |
| return '/$partial'; |
| } |
| return path; |
| } |
| |
| Future<void> writeBundle( |
| Directory bundleDir, Map<String, DevFSContent> assetEntries) async { |
| if (bundleDir.existsSync()) { |
| try { |
| bundleDir.deleteSync(recursive: true); |
| } on FileSystemException catch (e, s) { |
| _logger.warning( |
| 'Failed to clean up asset directory ${bundleDir.path}.\n' |
| 'To clean build artifacts, use the command "flutter clean".', |
| e, |
| s); |
| } |
| } |
| bundleDir.createSync(recursive: true); |
| |
| await Future.wait<void>(assetEntries.entries |
| .map<Future<void>>((MapEntry<String, DevFSContent> entry) async { |
| var file = fileSystem.file(fileSystem.path.join(bundleDir.path, entry.key)); |
| file.parent.createSync(recursive: true); |
| await file.writeAsBytes(await entry.value.contentsAsBytes()); |
| })); |
| } |