Skip to content

Adding support for Build Daemon + Frontend Server with expression eval. - #2836

Open
Markzipan wants to merge 90 commits into
dart-lang:mainfrom
Markzipan:expression_eval_dwds
Open

Adding support for Build Daemon + Frontend Server with expression eval.#2836
Markzipan wants to merge 90 commits into
dart-lang:mainfrom
Markzipan:expression_eval_dwds

Conversation

@Markzipan

@Markzipan Markzipan commented May 14, 2026

Copy link
Copy Markdown
Contributor

Changes at a glance

DWDS

  • asset_scheme.dart: Added AssetScheme to handle platform-specific file extensions (e.g., .ddc.js vs .dart.lib.js, .ddc.dill vs .dart.lib.dill) with BuildRunnerAssetScheme and FrontendServerAssetScheme default impls.
  • asset_reader.dart: Removed PackageUriMapper in favor of PathResolver interface with default impls: FrontendServerPathResolver, BuildRunnerPathResolver, and FlutterPathResolver.
  • Created web_path_translator.dart to hold asset translation rules (lib/ segments, asset-scheme-specific renaming, etc.).
  • loader.dart: Added MetadataLoader (with default impl MergedMetadataLoader) to allow platform-specific reading/parsing of metadata files.
  • strategy.dart: LoadStrategy now requires an AssetScheme and accepts a MetadataLoader. BuildSettings now accepts useDebuggerModuleNames.
  • dwds.dart: Now exports: AssetScheme, MetadataLoader, and related impls.
  • frontend_server_strategy_provider.dart: Added FrontendServerBuildDaemonStrategyProvider to support Frontend Server + Build Daemon config with hot reload.
  • daemon_expression_compiler.dart: Added DaemonExpressionCompiler to forward expression evaluation requests directly to the instantiated Frontend Server worker via socket.
  • location.dart: Updated sourcemap parsing to account for FES-specific URIs.

Webdev and Serving

  • dev_workflow.dart: Spawns a background build_frontend_server:fes_manager subprocess when webHotReload is enabled. This creates a persistent shared Frontend Server (with a persistent scratch space) that can receive expression eval, hot reload, and hot restart requests while communicating with the build daemon.
  • webdev_server.dart: Uses FrontendServerBuildDaemonStrategyProvider when webHotReload is enabled. Writes fes_manager_config to signal to build runner that a shared FES should be used. Now passes PathResolver and BuildSettings.
  • app_domain.dart: Now supports hot reload requests (instead of rejecting them).
  • server_manager.dart: Passes BuildDaemonClient to WebDevServer for build results to be accessible.

Tests

  • context.dart: Test fixtures now specify a PathResolver. Rewrote _createBuildRunnerDdcLibraryBundleAssetHandler to serve compiled outputs directly from the build cache or scratch space.
  • Now serves .ddc_merged_metadata via the fes_manager.
  • Now sends a BuildStatus.succeeded message early to avoid blocking downstream completers and locking.
  • Migrated several integration tests to support/run under the DDC Library Bundle + Frontend Server config (split between AMD and DDC Library Bundle).
  • Updated hot reload tests to signal reload failure by a crash when reading reloaded_sources.json. This is unlike a real app but works well for the test fixtures.
  • Bumped build_web_compilers to 4.8.7 in test fixtures.

@Markzipan
Markzipan force-pushed the expression_eval_dwds branch from b86e719 to 79b7fa2 Compare June 16, 2026 08:43

@nshahan nshahan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this PR is touching a lot of stuff, I added @srujzs to get another set of eyes on it just in case.

Comment thread dwds/lib/src/debugging/location.dart
Comment thread webdev/lib/src/serve/webdev_server.dart
Comment thread webdev/lib/src/serve/webdev_server.dart Outdated
Comment thread webdev/lib/src/serve/webdev_server.dart Outdated

@nshahan nshahan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you are doing some manual testing, but are those at a point that you can link some CLs/PRs so we can track? I want to avoid landing this too early if we still have work to do before rolling into the Dart SDK, Flutter, and google3.

/// A reader for Dart sources and related source maps provided by the Frontend
/// Server.
class FrontendServerAssetReader implements AssetReader {
class FrontendServerAssetReader extends AssetReader {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the implements -> extends change needed? Looks like there is no implementation to inherit in AssetReader?


/// A reader for resources provided by a proxy server.
class ProxyServerAssetReader implements AssetReader {
class ProxyServerAssetReader extends AssetReader {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

return null;
}

@override

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this diff?

import 'package:shelf/shelf.dart' as shelf;

class TestAssetServer implements AssetReader {
class TestAssetServer extends AssetReader {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}

class FakeAssetReader implements AssetReader {
class FakeAssetReader extends AssetReader {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@srujzs srujzs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of my comments are centered around some of the classes we're adding here.

final PackageConfig packageConfig;
abstract class PathResolver {
/// Computes the server path for a given application URL.
String? appUriToServerPath(String appUrl, {bool? useDebuggerModuleNames});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits: Might be worth detailing when we should expect nulls as returns here and below.

class PackageUriMapper {
final _logger = Logger('PackageUriMapper');
final PackageConfig packageConfig;
abstract class PathResolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we actually enumerate the derived classes ourselves, can we consider making these and the subclasses final or some other class modifier? Or do we expect other PathResolvers?

bool get useDebuggerModuleNames;
}

abstract class PathResolverBase implements PathResolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might have missed another implementation but consider flatting the hierarchy here so PathResolverBase and PathResolver are one class.

/// build daemon.
///
/// We assume the build daemon already has a Frontend Server intialized.
class DaemonExpressionCompiler implements ExpressionCompiler {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also consider using a class modifier here to seal the hierarchy.

// BSD-style license that can be found in the LICENSE file.

/// Encapsulates asset file naming conventions and schemes.
abstract class AssetScheme {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider class modifiers here and derived classes as well.

Comment on lines +136 to +137
Future<JSArray<JSObject>> hotRestartBegin(String? reloadedSourcesPath) async {
assert(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm totally fine handling this in here, but I'm guessing we're handling it in here to check that this path exists for hot restart without breaking the AMD and DDC module formats?

// BSD-style license that can be found in the LICENSE file.

/// Encapsulates asset file naming conventions and schemes.
abstract class AssetScheme {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can AssetScheme live as a getter within PathResolver? Mostly wondering if that's something a user needs to configure different from PathResolver.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants