From 8490a07fce09396bc296ed33bafee7b496f4c69c Mon Sep 17 00:00:00 2001 From: MarkZ Date: Thu, 26 Oct 2023 13:31:11 -0700 Subject: [PATCH] Adding support for using the Frontend Server with the Legacy/DDC module system. --- dwds/lib/dwds.dart | 8 +- .../src/loaders/frontend_server_legacy.dart | 116 ++++++++++++++++++ dwds/lib/src/loaders/legacy.dart | 74 ++++++++++- 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 dwds/lib/src/loaders/frontend_server_legacy.dart diff --git a/dwds/lib/dwds.dart b/dwds/lib/dwds.dart index aea53f80e..9bbbb9747 100644 --- a/dwds/lib/dwds.dart +++ b/dwds/lib/dwds.dart @@ -21,6 +21,8 @@ export 'src/loaders/build_runner_require.dart' show BuildRunnerRequireStrategyProvider; export 'src/loaders/frontend_server_require.dart' show FrontendServerRequireStrategyProvider; +export 'src/loaders/frontend_server_legacy.dart' + show FrontendServerLegacyStrategyProvider; export 'src/loaders/legacy.dart' show LegacyStrategy; export 'src/loaders/require.dart' show RequireStrategy; export 'src/loaders/strategy.dart' @@ -32,7 +34,11 @@ export 'src/readers/proxy_server_asset_reader.dart' show ProxyServerAssetReader; export 'src/servers/devtools.dart'; export 'src/services/chrome_debug_exception.dart' show ChromeDebugException; export 'src/services/expression_compiler.dart' - show ExpressionCompilationResult, ExpressionCompiler, ModuleInfo; + show + ExpressionCompilationResult, + ExpressionCompiler, + ModuleInfo, + CompilerOptions; export 'src/services/expression_compiler_service.dart' show ExpressionCompilerService; export 'src/utilities/sdk_configuration.dart' diff --git a/dwds/lib/src/loaders/frontend_server_legacy.dart b/dwds/lib/src/loaders/frontend_server_legacy.dart new file mode 100644 index 000000000..4c0a14061 --- /dev/null +++ b/dwds/lib/src/loaders/frontend_server_legacy.dart @@ -0,0 +1,116 @@ +// 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 'package:dwds/src/debugging/metadata/provider.dart'; +import 'package:dwds/src/loaders/legacy.dart'; +import 'package:dwds/src/loaders/strategy.dart'; +import 'package:dwds/src/readers/asset_reader.dart'; +import 'package:dwds/src/services/expression_compiler.dart'; +import 'package:path/path.dart' as p; + +/// Provides a [LegacyStrategy] suitable for use with Frontend Server. +class FrontendServerLegacyStrategyProvider { + final ReloadConfiguration _configuration; + final AssetReader _assetReader; + final PackageUriMapper _packageUriMapper; + final Future> Function() _digestsProvider; + final String _basePath; + final BuildSettings _buildSettings; + + late final LegacyStrategy _legacyStrategy = LegacyStrategy( + _configuration, + _moduleProvider, + (_) => _digestsProvider(), + _moduleForServerPath, + _serverPathForModule, + _sourceMapPathForModule, + _serverPathForAppUri, + _moduleInfoForProvider, + _assetReader, + _buildSettings, + (String _) => null, + null, + ); + + FrontendServerLegacyStrategyProvider( + this._configuration, + this._assetReader, + this._packageUriMapper, + this._digestsProvider, + this._buildSettings, + ) : _basePath = _assetReader.basePath; + + LegacyStrategy get strategy => _legacyStrategy; + + String _removeBasePath(String path) { + if (_basePath.isEmpty) return path; + + final stripped = stripLeadingSlashes(path); + return stripLeadingSlashes(stripped.substring(_basePath.length)); + } + + String _addBasePath(String serverPath) => _basePath.isEmpty + ? stripLeadingSlashes(serverPath) + : '$_basePath/${stripLeadingSlashes(serverPath)}'; + + Future> _moduleProvider( + MetadataProvider metadataProvider, + ) async => + (await metadataProvider.moduleToModulePath).map( + (key, value) => + MapEntry(key, stripLeadingSlashes(removeJsExtension(value))), + ); + + Future _moduleForServerPath( + MetadataProvider metadataProvider, + String serverPath, + ) async { + final modulePathToModule = await metadataProvider.modulePathToModule; + final relativeServerPath = _removeBasePath(serverPath); + return modulePathToModule[relativeServerPath]; + } + + Future _serverPathForModule( + MetadataProvider metadataProvider, + String module, + ) async => + _addBasePath((await metadataProvider.moduleToModulePath)[module] ?? ''); + + Future _sourceMapPathForModule( + MetadataProvider metadataProvider, + String module, + ) async => + _addBasePath((await metadataProvider.moduleToSourceMap)[module] ?? ''); + + String? _serverPathForAppUri(String appUrl) { + final appUri = Uri.parse(appUrl); + if (appUri.isScheme('org-dartlang-app')) { + return _addBasePath(appUri.path); + } + if (appUri.isScheme('package')) { + final resolved = _packageUriMapper.packageUriToServerPath(appUri); + if (resolved != null) { + return resolved; + } + } + return null; + } + + Future> _moduleInfoForProvider( + MetadataProvider metadataProvider, + ) async { + final modules = await metadataProvider.moduleToModulePath; + final result = {}; + for (var module in modules.keys) { + final modulePath = modules[module]!; + result[module] = ModuleInfo( + // TODO: Save locations of full kernel files in ddc metadata. + // Issue: https://github.com/dart-lang/sdk/issues/43684 + p.setExtension(modulePath, '.full.dill'), + p.setExtension(modulePath, '.dill'), + ); + } + return result; + } +} diff --git a/dwds/lib/src/loaders/legacy.dart b/dwds/lib/src/loaders/legacy.dart index 71b9f313c..6af1fb281 100644 --- a/dwds/lib/src/loaders/legacy.dart +++ b/dwds/lib/src/loaders/legacy.dart @@ -2,17 +2,72 @@ // for details. 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 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/loaders/strategy.dart'; import 'package:dwds/src/readers/asset_reader.dart'; import 'package:dwds/src/services/expression_compiler.dart'; +import 'package:path/path.dart' as p; import 'package:shelf/shelf.dart'; +String removeJsExtension(String path) => + path.endsWith('.js') ? p.withoutExtension(path) : path; + +String addJsExtension(String path) => '$path.js'; + +/// JavaScript snippet to determine the base URL of the current path. +const _baseUrlScript = ''' +var baseUrl = (function () { + // Attempt to detect --precompiled mode for tests, and set the base url + // appropriately, otherwise set it to '/'. + var pathParts = location.pathname.split("/"); + if (pathParts[0] == "") { + pathParts.shift(); + } + if (pathParts.length > 1 && pathParts[1] == "test") { + return "/" + pathParts.slice(0, 2).join("/") + "/"; + } + // Attempt to detect base url using html tag + // base href should start and end with "/" + if (typeof document !== 'undefined') { + var el = document.getElementsByTagName('base'); + if (el && el[0] && el[0].getAttribute("href") && el[0].getAttribute + ("href").startsWith("/") && el[0].getAttribute("href").endsWith("/")){ + return el[0].getAttribute("href"); + } + } + // return default value + return "/"; +}()); +'''; + /// A load strategy for the legacy module system. class LegacyStrategy extends LoadStrategy { @override final ReloadConfiguration reloadConfiguration; + /// Returns a map of module name to corresponding server path (excluding .js) + /// for the provided Dart application entrypoint. + /// + /// For example: + /// + /// web/main -> main.ddc + /// packages/path/path -> packages/path/path.ddc + /// + final Future> Function(MetadataProvider metadataProvider) + _moduleProvider; + + /// Returns a map of module name to corresponding digest value. + /// + /// For example: + /// + /// web/main -> 8363b363f74b41cac955024ab8b94a3f + /// packages/path/path -> d348c2a4647e998011fe305f74f22961 + /// + final Future> Function(MetadataProvider metadataProvider) + _digestsProvider; + /// Returns the module for the corresponding server path. /// /// For example: @@ -75,6 +130,8 @@ class LegacyStrategy extends LoadStrategy { LegacyStrategy( this.reloadConfiguration, + this._moduleProvider, + this._digestsProvider, this._moduleForServerPath, this._serverPathForModule, this._sourceMapPathForModule, @@ -102,12 +159,27 @@ class LegacyStrategy extends LoadStrategy { String get loadModuleSnippet => 'dart_library.import'; @override - Future bootstrapFor(String entrypoint) async => ''; + Future bootstrapFor(String entrypoint) async => + await _legacyLoaderSetup(entrypoint); @override String loadClientSnippet(String clientScript) => 'window.\$dartLoader.forceLoadModule("$clientScript");\n'; + Future _legacyLoaderSetup(String entrypoint) async { + final metadataProvider = metadataProviderFor(entrypoint); + final modulePaths = await _moduleProvider(metadataProvider); + final scripts = >[]; + modulePaths.forEach((name, path) { + scripts.add({'src': '$path.js', 'id': name}); + }); + return ''' +$_baseUrlScript +var scripts = ${const JsonEncoder.withIndent(" ").convert(scripts)}; +window.\$dartLoader.loadScripts(scripts); +'''; + } + @override Future moduleForServerPath(String entrypoint, String serverPath) => _moduleForServerPath(metadataProviderFor(entrypoint), serverPath);