Skip to content

Commit 1281e60

Browse files
authored
Add minimal bootstrapper for ddc_library_bundle format (#2523)
Contains the following changes: - Bootstrapping scripts to load the needed scripts (including the module loader, dart_sdk, source maps, etc.) and to call main. - DDC library bundle load strategy that's mostly the DDC load strategy currently. - Fixes tests not passing canary feature flag. - Changes to the injected client to handle the new library format and use a no-op restarter for now. - Fixes some small typos/formatting.
1 parent 18ab1d5 commit 1281e60

File tree

13 files changed

+1094
-750
lines changed

13 files changed

+1094
-750
lines changed

dwds/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Replace deprecated JS code `this.__proto__` with `Object.getPrototypeOf(this)`. - [#2500](https://github.com/dart-lang/webdev/pull/2500)
66
- Migrate injected client code to `package:web`. - [#2491](https://github.com/dart-lang/webdev/pull/2491)
77
- Deprecated MetadataProvider's, CompilerOptions', SdkConfiguration's & SdkLayout's soundNullSafety. - [#2427](https://github.com/dart-lang/webdev/issues/2427)
8+
- Add load strategy and an unimplemented hot restart strategy for DDC library bundle format.
89

910
## 24.1.0
1011

dwds/lib/src/injected/client.js

Lines changed: 708 additions & 720 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dwds/lib/src/loaders/ddc.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ String removeJsExtension(String path) =>
1717
String addJsExtension(String path) => '$path.js';
1818

1919
/// JavaScript snippet to determine the base URL of the current path.
20-
const _baseUrlScript = '''
20+
const baseUrlScript = '''
2121
var baseUrl = (function () {
2222
// Attempt to detect --precompiled mode for tests, and set the base url
2323
// appropriately, otherwise set it to '/'.
@@ -179,7 +179,7 @@ class DdcStrategy extends LoadStrategy {
179179
scripts.add(<String, String>{'src': '$path.js', 'id': name});
180180
});
181181
return '''
182-
$_baseUrlScript
182+
$baseUrlScript
183183
var scripts = ${const JsonEncoder.withIndent(" ").convert(scripts)};
184184
window.\$dartLoader.loadConfig.loadScriptFn = function(loader) {
185185
loader.addScriptsToQueue(scripts, null);
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
import 'package:dwds/src/debugging/metadata/provider.dart';
8+
import 'package:dwds/src/loaders/ddc.dart';
9+
import 'package:dwds/src/loaders/strategy.dart';
10+
import 'package:dwds/src/readers/asset_reader.dart';
11+
import 'package:dwds/src/services/expression_compiler.dart';
12+
import 'package:shelf/shelf.dart';
13+
14+
// TODO(srujzs): This is mostly a copy of `DdcStrategy`. Some of the
15+
// functionality in here may not make sense with the library bundle format yet.
16+
class DdcLibraryBundleStrategy extends LoadStrategy {
17+
@override
18+
final ReloadConfiguration reloadConfiguration;
19+
20+
/// Returns a map of module name to corresponding server path (excluding .js)
21+
/// for the provided Dart application entrypoint.
22+
///
23+
/// For example:
24+
///
25+
/// web/main -> main.ddc
26+
/// packages/path/path -> packages/path/path.ddc
27+
///
28+
final Future<Map<String, String>> Function(MetadataProvider metadataProvider)
29+
_moduleProvider;
30+
31+
/// Returns a map of module name to corresponding digest value.
32+
///
33+
/// For example:
34+
///
35+
/// web/main -> 8363b363f74b41cac955024ab8b94a3f
36+
/// packages/path/path -> d348c2a4647e998011fe305f74f22961
37+
///
38+
final Future<Map<String, String>> Function(MetadataProvider metadataProvider)
39+
// ignore: unused_field
40+
_digestsProvider;
41+
42+
/// Returns the module for the corresponding server path.
43+
///
44+
/// For example:
45+
///
46+
/// /packages/path/path.ddc.js -> packages/path/path
47+
///
48+
final Future<String?> Function(
49+
MetadataProvider metadataProvider,
50+
String sourcePath,
51+
) _moduleForServerPath;
52+
53+
/// Returns a map from module id to module info.
54+
///
55+
/// For example:
56+
///
57+
/// web/main -> {main.ddc.full.dill, main.ddc.dill}
58+
///
59+
final Future<Map<String, ModuleInfo>> Function(
60+
MetadataProvider metadataProvider,
61+
) _moduleInfoForProvider;
62+
63+
/// Returns the server path for the provided module.
64+
///
65+
/// For example:
66+
///
67+
/// web/main -> main.ddc.js
68+
///
69+
final Future<String?> Function(
70+
MetadataProvider metadataProvider,
71+
String module,
72+
) _serverPathForModule;
73+
74+
/// Returns the source map path for the provided module.
75+
///
76+
/// For example:
77+
///
78+
/// web/main -> main.ddc.js.map
79+
///
80+
final Future<String?> Function(
81+
MetadataProvider metadataProvider,
82+
String module,
83+
) _sourceMapPathForModule;
84+
85+
/// Returns the server path for the app uri.
86+
///
87+
/// For example:
88+
///
89+
/// org-dartlang-app://web/main.dart -> main.dart
90+
///
91+
/// Will return `null` if the provided uri is not
92+
/// an app URI.
93+
final String? Function(String appUri) _serverPathForAppUri;
94+
95+
/// Returns the relative path in google3, determined by the [absolutePath].
96+
///
97+
/// Returns `null` if not a google3 app.
98+
final String? Function(String absolutePath) _g3RelativePath;
99+
100+
final BuildSettings _buildSettings;
101+
102+
DdcLibraryBundleStrategy(
103+
this.reloadConfiguration,
104+
this._moduleProvider,
105+
this._digestsProvider,
106+
this._moduleForServerPath,
107+
this._serverPathForModule,
108+
this._sourceMapPathForModule,
109+
this._serverPathForAppUri,
110+
this._moduleInfoForProvider,
111+
AssetReader assetReader,
112+
this._buildSettings,
113+
this._g3RelativePath,
114+
String? packageConfigPath,
115+
) : super(assetReader, packageConfigPath: packageConfigPath);
116+
117+
@override
118+
Handler get handler => (request) async {
119+
// TODO(markzipan): Implement a hot restarter that uses digests for
120+
// the DDC module system.
121+
return Response.notFound(request.url.toString());
122+
};
123+
124+
@override
125+
String get id => 'ddc-library-bundle';
126+
127+
// DDC doesn't have a 'ddc-library-bundle' format flag. Instead, it treats the
128+
// combination of the DDC module format and canary mode as the DDC library
129+
// bundle format, so we just pass 'ddc' here.
130+
@override
131+
String get moduleFormat => 'ddc';
132+
133+
@override
134+
String get loadLibrariesModule => 'ddc_module_loader.ddk.js';
135+
136+
// TODO(srujzs): Refactor code that uses this to avoid loading individual
137+
// libraries, as that's no longer supported in the new module format.
138+
@override
139+
String get loadModuleSnippet =>
140+
"function() { throw new Error('LoadStrategy.loadModuleSnippet is used. "
141+
"This is currently unsupported in the DDC library bundle format.'); }";
142+
143+
@override
144+
Future<String> bootstrapFor(String entrypoint) async =>
145+
await _ddcLoaderSetup(entrypoint);
146+
147+
@override
148+
String loadClientSnippet(String clientScript) =>
149+
'window.\$dartLoader.forceLoadModule("$clientScript");\n';
150+
151+
Future<String> _ddcLoaderSetup(String entrypoint) async {
152+
final metadataProvider = metadataProviderFor(entrypoint);
153+
final modulePaths = await _moduleProvider(metadataProvider);
154+
final scripts = <Map<String, String?>>[];
155+
modulePaths.forEach((name, path) {
156+
scripts.add(<String, String>{'src': '$path.js', 'id': name});
157+
});
158+
return '''
159+
$baseUrlScript
160+
var scripts = ${const JsonEncoder.withIndent(" ").convert(scripts)};
161+
window.\$dartLoader.loadConfig.loadScriptFn = function(loader) {
162+
loader.addScriptsToQueue(scripts, null);
163+
loader.loadEnqueuedModules();
164+
};
165+
window.\$dartLoader.loader.nextAttempt();
166+
''';
167+
}
168+
169+
@override
170+
Future<String?> moduleForServerPath(String entrypoint, String serverPath) =>
171+
_moduleForServerPath(metadataProviderFor(entrypoint), serverPath);
172+
173+
@override
174+
Future<Map<String, ModuleInfo>> moduleInfoForEntrypoint(String entrypoint) =>
175+
_moduleInfoForProvider(metadataProviderFor(entrypoint));
176+
177+
@override
178+
Future<String?> serverPathForModule(String entrypoint, String module) =>
179+
_serverPathForModule(metadataProviderFor(entrypoint), module);
180+
181+
@override
182+
Future<String?> sourceMapPathForModule(String entrypoint, String module) =>
183+
_sourceMapPathForModule(metadataProviderFor(entrypoint), module);
184+
185+
@override
186+
String? serverPathForAppUri(String appUri) => _serverPathForAppUri(appUri);
187+
188+
@override
189+
BuildSettings get buildSettings => _buildSettings;
190+
191+
@override
192+
String? g3RelativePath(String absolutePath) => _g3RelativePath(absolutePath);
193+
}

dwds/lib/src/loaders/frontend_server_strategy_provider.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:dwds/src/debugging/metadata/provider.dart';
66
import 'package:dwds/src/loaders/ddc.dart';
7+
import 'package:dwds/src/loaders/ddc_library_bundle.dart';
78
import 'package:dwds/src/loaders/require.dart';
89
import 'package:dwds/src/loaders/strategy.dart';
910
import 'package:dwds/src/readers/asset_reader.dart';
@@ -132,6 +133,38 @@ class FrontendServerDdcStrategyProvider
132133
DdcStrategy get strategy => _ddcStrategy;
133134
}
134135

136+
/// Provides a [DdcLibraryBundleStrategy] suitable for use with the Frontend
137+
/// Server.
138+
class FrontendServerDdcLibraryBundleStrategyProvider
139+
extends FrontendServerStrategyProvider<DdcLibraryBundleStrategy> {
140+
late final DdcLibraryBundleStrategy _libraryBundleStrategy =
141+
DdcLibraryBundleStrategy(
142+
_configuration,
143+
_moduleProvider,
144+
(_) => _digestsProvider(),
145+
_moduleForServerPath,
146+
_serverPathForModule,
147+
_sourceMapPathForModule,
148+
_serverPathForAppUri,
149+
_moduleInfoForProvider,
150+
_assetReader,
151+
_buildSettings,
152+
(String _) => null,
153+
null,
154+
);
155+
156+
FrontendServerDdcLibraryBundleStrategyProvider(
157+
super._configuration,
158+
super._assetReader,
159+
super._packageUriMapper,
160+
super._digestsProvider,
161+
super._buildSettings,
162+
);
163+
164+
@override
165+
DdcLibraryBundleStrategy get strategy => _libraryBundleStrategy;
166+
}
167+
135168
/// Provides a [RequireStrategy] suitable for use with Frontend Server.
136169
class FrontendServerRequireStrategyProvider
137170
extends FrontendServerStrategyProvider<RequireStrategy> {

dwds/test/evaluate_common.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ void testAll({
7575
enableExpressionEvaluation: true,
7676
useDebuggerModuleNames: useDebuggerModuleNames,
7777
verboseCompiler: debug,
78+
canaryFeatures: provider.canaryFeatures,
7879
),
7980
);
8081
});
@@ -832,6 +833,7 @@ void testAll({
832833
moduleFormat: provider.ddcModuleFormat,
833834
enableExpressionEvaluation: false,
834835
verboseCompiler: debug,
836+
canaryFeatures: provider.canaryFeatures,
835837
),
836838
);
837839
});

dwds/test/fixtures/context.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,13 @@ class TestContext {
348348
buildSettings,
349349
).strategy,
350350
ModuleFormat.ddc => buildSettings.canaryFeatures
351-
? throw Exception(
352-
'''Unsupported DDC module format ${testSettings.moduleFormat.name}
353-
with canaryFeatures set to ${buildSettings.canaryFeatures}.''',
354-
)
351+
? FrontendServerDdcLibraryBundleStrategyProvider(
352+
testSettings.reloadConfiguration,
353+
assetReader,
354+
packageUriMapper,
355+
() async => {},
356+
buildSettings,
357+
).strategy
355358
: FrontendServerDdcStrategyProvider(
356359
testSettings.reloadConfiguration,
357360
assetReader,
@@ -370,8 +373,8 @@ class TestContext {
370373

371374
final debugPort = await findUnusedPort();
372375
if (testSettings.launchChrome) {
373-
// If the environment variable DWDS_DEBUG_CHROME is set to the string true
374-
// then Chrome will be launched with a UI rather than headless.
376+
// If the environment variable DWDS_DEBUG_CHROME is set to the string
377+
// true then Chrome will be launched with a UI rather than headless.
375378
// If the extension is enabled, then Chrome will be launched with a UI
376379
// since headless Chrome does not support extensions.
377380
final enableDebugExtension = debugSettings.enableDebugExtension;

dwds/web/client.dart

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import 'package:uuid/uuid.dart';
2424
import 'package:web/helpers.dart';
2525
import 'package:web_socket_channel/web_socket_channel.dart';
2626

27+
import 'reloader/ddc_library_bundle_restarter.dart';
2728
import 'reloader/ddc_restarter.dart';
2829
import 'reloader/manager.dart';
2930
import 'reloader/require_restarter.dart';
30-
import 'reloader/restarter.dart';
3131
import 'run_main.dart';
3232
import 'web_utils.dart';
3333

@@ -47,14 +47,12 @@ Future<void>? main() {
4747
? WebSocketClient(WebSocketChannel.connect(fixedUri))
4848
: SseSocketClient(SseClient(fixedPath, debugKey: 'InjectedClient'));
4949

50-
Restarter restarter;
51-
if (dartModuleStrategy == 'require-js') {
52-
restarter = await RequireRestarter.create();
53-
} else if (dartModuleStrategy == 'ddc' || dartModuleStrategy == 'legacy') {
54-
restarter = DdcRestarter();
55-
} else {
56-
throw StateError('Unknown module strategy: $dartModuleStrategy');
57-
}
50+
final restarter = switch (dartModuleStrategy) {
51+
'require-js' => await RequireRestarter.create(),
52+
'ddc-library-bundle' => DdcLibraryBundleRestarter(),
53+
'ddc' || 'legacy' => DdcRestarter(),
54+
_ => throw StateError('Unknown module strategy: $dartModuleStrategy')
55+
};
5856

5957
final manager = ReloadingManager(client, restarter);
6058

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'restarter.dart';
8+
9+
class DdcLibraryBundleRestarter implements Restarter {
10+
@override
11+
Future<bool> restart({String? runId, Future? readyToRunMain}) async {
12+
throw UnimplementedError(
13+
"Hot restart isn't supported for the DDC library bundle format yet.",
14+
);
15+
}
16+
}

frontend_server_common/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.3-wip
2+
3+
- Add bootstrapping code for DDC library bundle format.
4+
15
## 0.2.2
26

37
- Start the frontend server from the AOT snapshot shipped in the Dart SDK.

0 commit comments

Comments
 (0)