| // 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:meta/meta.dart'; |
| import 'package:package_config/package_config.dart'; |
| import 'package:path/path.dart' as p; |
| |
| import 'asset_server.dart'; |
| import 'bootstrap.dart'; |
| import 'frontend_server_client.dart'; |
| import 'utilities.dart'; |
| |
| final String dartWebSdkPath = p.join(dartSdkPath, 'lib', 'dev_compiler'); |
| |
| class WebDevFS { |
| WebDevFS({ |
| this.fileSystem, |
| this.hostname, |
| this.port, |
| this.packageConfigPath, |
| this.root, |
| this.urlTunneller, |
| this.soundNullSafety, |
| }); |
| |
| final FileSystem fileSystem; |
| TestAssetServer assetServer; |
| final String hostname; |
| final int port; |
| final String packageConfigPath; |
| final String root; |
| final UrlEncoder urlTunneller; |
| final bool soundNullSafety; |
| Directory _savedCurrentDirectory; |
| List<Uri> sources; |
| PackageConfig _packageConfig; |
| |
| Future<Uri> create() async { |
| _savedCurrentDirectory = fileSystem.currentDirectory; |
| // package_config.json is located in <project directory>/.dart_tool/package_config |
| var projectDirectory = p.dirname(p.dirname(packageConfigPath)); |
| |
| fileSystem.currentDirectory = projectDirectory; |
| |
| _packageConfig = await loadPackageConfigUri(Uri.file(packageConfigPath), |
| loader: (Uri uri) => fileSystem.file(uri).readAsBytes()); |
| |
| assetServer = await TestAssetServer.start( |
| fileSystem, root, hostname, port, urlTunneller, _packageConfig); |
| return Uri.parse('http://$hostname:$port'); |
| } |
| |
| Future<void> dispose() { |
| fileSystem.currentDirectory = _savedCurrentDirectory; |
| return assetServer?.close(); |
| } |
| |
| Future<UpdateFSReport> update({ |
| String mainPath, |
| 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', '{}'); |
| var sdk = soundNullSafety ? dartSdkSound : dartSdk; |
| var sdkSourceMap = |
| soundNullSafety ? dartSdkSourcemapSound : dartSdkSourcemap; |
| assetServer.writeFile('/dart_sdk.js', sdk.readAsStringSync()); |
| assetServer.writeFile('/dart_sdk.js.map', sdkSourceMap.readAsStringSync()); |
| |
| generator.reset(); |
| var compilerOutput = await generator.recompile( |
| Uri.parse('org-dartlang-app:///$mainPath'), invalidatedFiles, |
| outputPath: p.join(dillOutputPath, 'app.dill'), |
| packageConfig: _packageConfig); |
| 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 dartSdkSound => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'kernel', |
| 'amd', |
| 'dart_sdk_sound.js', |
| )); |
| |
| File get dartSdkSourcemap => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'kernel', |
| 'amd', |
| 'dart_sdk.js.map', |
| )); |
| |
| File get dartSdkSourcemapSound => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'kernel', |
| 'amd', |
| 'dart_sdk_sound.js.map', |
| )); |
| |
| File get stackTraceMapper => fileSystem.file(fileSystem.path.join( |
| dartWebSdkPath, |
| 'web', |
| 'dart_stack_trace_mapper.js', |
| )); |
| } |
| |
| class UpdateFSReport { |
| final bool _success; |
| final int _invalidatedSourcesCount; |
| final int _syncedBytes; |
| |
| UpdateFSReport({ |
| bool success = false, |
| int invalidatedSourcesCount = 0, |
| int syncedBytes = 0, |
| }) : _success = success, |
| _invalidatedSourcesCount = invalidatedSourcesCount, |
| _syncedBytes = syncedBytes; |
| |
| bool get success => _success; |
| int get invalidatedSourcesCount => _invalidatedSourcesCount; |
| int get syncedBytes => _syncedBytes; |
| |
| /// JavaScript modules produced by the incremental compiler in `dartdevc` |
| /// mode. |
| /// |
| /// Only used for JavaScript compilation. |
| List<String> invalidatedModules; |
| } |
| |
| 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; |
| } |