Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for using the Frontend Server with the Legacy/DDC modu… #2285

Merged
merged 1 commit into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dwds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 23.0.0-wip
- Restructure `LoadStrategy` to provide build settings. - [#2270](https://github.com/dart-lang/webdev/pull/2270)
- Add `FrontendServerLegacyStrategyProvider` and update bootstrap generation logic for `LegacyStrategy` - [#2285](https://github.com/dart-lang/webdev/pull/2285)

## 22.1.0
- Update `package:vm_service` constraint to `^13.0.0`. - [#2265](https://github.com/dart-lang/webdev/pull/2265)
Expand Down
8 changes: 7 additions & 1 deletion dwds/lib/dwds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export 'src/handlers/dev_handler.dart' show AppConnectionException;
export 'src/handlers/socket_connections.dart';
export 'src/loaders/build_runner_require.dart'
show BuildRunnerRequireStrategyProvider;
export 'src/loaders/frontend_server_legacy.dart'
show FrontendServerLegacyStrategyProvider;
export 'src/loaders/frontend_server_require.dart'
show FrontendServerRequireStrategyProvider;
export 'src/loaders/legacy.dart' show LegacyStrategy;
Expand All @@ -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'
Expand Down
116 changes: 116 additions & 0 deletions dwds/lib/src/loaders/frontend_server_legacy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2023 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<Map<String, String>> 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<Map<String, String>> _moduleProvider(
MetadataProvider metadataProvider,
) async =>
(await metadataProvider.moduleToModulePath).map(
(key, value) =>
MapEntry(key, stripLeadingSlashes(removeJsExtension(value))),
);

Future<String?> _moduleForServerPath(
MetadataProvider metadataProvider,
String serverPath,
) async {
final modulePathToModule = await metadataProvider.modulePathToModule;
final relativeServerPath = _removeBasePath(serverPath);
return modulePathToModule[relativeServerPath];
}

Future<String> _serverPathForModule(
MetadataProvider metadataProvider,
String module,
) async =>
_addBasePath((await metadataProvider.moduleToModulePath)[module] ?? '');

Future<String> _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<Map<String, ModuleInfo>> _moduleInfoForProvider(
MetadataProvider metadataProvider,
) async {
final modules = await metadataProvider.moduleToModulePath;
final result = <String, ModuleInfo>{};
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;
}
}
80 changes: 78 additions & 2 deletions dwds/lib/src/loaders/legacy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 <base href> 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<Map<String, String>> 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<Map<String, String>> Function(MetadataProvider metadataProvider)
_digestsProvider;

/// Returns the module for the corresponding server path.
///
/// For example:
Expand Down Expand Up @@ -75,6 +130,8 @@ class LegacyStrategy extends LoadStrategy {

LegacyStrategy(
this.reloadConfiguration,
this._moduleProvider,
this._digestsProvider,
this._moduleForServerPath,
this._serverPathForModule,
this._sourceMapPathForModule,
Expand All @@ -87,7 +144,11 @@ class LegacyStrategy extends LoadStrategy {
) : super(assetReader, packageConfigPath: packageConfigPath);

@override
Handler get handler => (request) => Response.notFound(request.url.toString());
Handler get handler => (request) async {
// TODO(markzipan): Implement a hot restarter that uses digests for
// the DDC module system.
return Response.notFound(request.url.toString());
};

@override
String get id => 'legacy';
Expand All @@ -102,12 +163,27 @@ class LegacyStrategy extends LoadStrategy {
String get loadModuleSnippet => 'dart_library.import';

@override
Future<String> bootstrapFor(String entrypoint) async => '';
Future<String> bootstrapFor(String entrypoint) async =>
await _legacyLoaderSetup(entrypoint);

@override
String loadClientSnippet(String clientScript) =>
'window.\$dartLoader.forceLoadModule("$clientScript");\n';

Future<String> _legacyLoaderSetup(String entrypoint) async {
final metadataProvider = metadataProviderFor(entrypoint);
final modulePaths = await _moduleProvider(metadataProvider);
final scripts = <Map<String, String?>>[];
modulePaths.forEach((name, path) {
scripts.add(<String, String>{'src': '$path.js', 'id': name});
});
return '''
$_baseUrlScript
var scripts = ${const JsonEncoder.withIndent(" ").convert(scripts)};
window.\$dartLoader.loadScripts(scripts);
''';
}

@override
Future<String?> moduleForServerPath(String entrypoint, String serverPath) =>
_moduleForServerPath(metadataProviderFor(entrypoint), serverPath);
Expand Down