Skip to content
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
Expand Up @@ -3,6 +3,7 @@
- Bump `build_web_compilers` to ^4.4.1.
- Remove unused `clientFuture` arg from `DwdsVmClient` methods.
- Fix pausing starting of `main` after the hot restart.
- Updating bootstrapper for DDC library bundler module format + Frontend Server.

## 26.2.2

Expand Down
10 changes: 7 additions & 3 deletions dwds/lib/src/loaders/ddc_library_bundle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,19 @@ class DdcLibraryBundleStrategy extends LoadStrategy {
modulePaths.forEach((name, path) {
scripts.add(<String, String>{'src': '$path.js', 'id': name});
});
return '''
$baseUrlScript
// canary-mode uses the Frontend Server, which begins script loads via a
// separate pathway.
final scriptLoader = buildSettings.canaryFeatures
? '''
var scripts = ${const JsonEncoder.withIndent(" ").convert(scripts)};
window.\$dartLoader.loadConfig.loadScriptFn = function(loader) {
loader.addScriptsToQueue(scripts, null);
loader.loadEnqueuedModules();
};
window.\$dartLoader.loader.nextAttempt();
''';
'''
: '';
return '$baseUrlScript\n$scriptLoader';
}

@override
Expand Down
4 changes: 4 additions & 0 deletions webdev/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.8.1-wip

- Adding initial DDC library bundler support behind the `canary` flag.

## 3.8.0-wip

- Bump minimum SDK constraint to 3.10.0
Expand Down
3 changes: 2 additions & 1 deletion webdev/lib/src/command/build_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ class BuildCommand extends Command<int> {
DefaultBuildTarget(
(b) => b
..target = configuration.outputInput
..outputLocation = outputLocation?.toBuilder(),
..outputLocation = outputLocation?.toBuilder()
..reportChangedAssets = true,
),
);
client.startBuild();
Expand Down
18 changes: 18 additions & 0 deletions webdev/lib/src/command/shared.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,24 @@ List<String> buildRunnerArgs(Configuration configuration) {
arguments.add('--enable-experiment=$experiment');
}

if (configuration.canaryFeatures) {
arguments
..add('--define')
..add('build_web_compilers|sdk_js=web-hot-reload=true');
arguments
..add('--define')
..add('build_web_compilers|entrypoint=web-hot-reload=true');
arguments
..add('--define')
..add('build_web_compilers|entrypoint_marker=web-hot-reload=true');
arguments
..add('--define')
..add('build_web_compilers|ddc=web-hot-reload=true');
arguments
..add('--define')
..add('build_web_compilers|ddc_modules=web-hot-reload=true');
}

return arguments;
}

Expand Down
6 changes: 4 additions & 2 deletions webdev/lib/src/serve/dev_workflow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ void _registerBuildTargets(
DefaultBuildTarget(
(b) => b
..target = target
..outputLocation = outputLocation?.toBuilder(),
..outputLocation = outputLocation?.toBuilder()
..reportChangedAssets = true,
),
);
}
Expand All @@ -161,7 +162,8 @@ void _registerBuildTargets(
DefaultBuildTarget(
(b) => b
..target = ''
..outputLocation = outputLocation.toBuilder(),
..outputLocation = outputLocation.toBuilder()
..reportChangedAssets = true,
),
);
}
Expand Down
24 changes: 24 additions & 0 deletions webdev/lib/src/serve/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,27 @@ String? findPackageConfigFilePath() {
candidateDir = parentDir;
}
}

/// Returns the absolute file path of the `package_config.json` file in the `.dart_tool`
/// directory, searching recursively from the current directory hierarchy.
Uri? findPackageConfigUri() {
var candidateDir = Directory(p.current).absolute;

while (true) {
final candidatePackageConfigFile = File(
p.join(candidateDir.path, '.dart_tool', 'package_config.json'),
);

if (candidatePackageConfigFile.existsSync()) {
return candidatePackageConfigFile.uri;
}

final parentDir = candidateDir.parent;
if (parentDir.path == candidateDir.path) {
// We've reached the root directory
return null;
}

candidateDir = parentDir;
}
}
152 changes: 143 additions & 9 deletions webdev/lib/src/serve/webdev_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:build_daemon/data/build_status.dart' as daemon;
import 'package:dds/devtools_server.dart';
import 'package:dwds/data/build_result.dart';
import 'package:dwds/dwds.dart';
import 'package:dwds/sdk_configuration.dart';
import 'package:file/local.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
import 'package:http_multi_server/http_multi_server.dart';
Expand All @@ -21,10 +23,14 @@ import '../command/configuration.dart';
import '../util.dart';
import 'chrome.dart';
import 'handlers/favicon_handler.dart';
import 'utils.dart' show findPackageConfigFilePath;
import 'utils.dart' show findPackageConfigFilePath, findPackageConfigUri;

Logger _logger = Logger('WebDevServer');

const reloadedSourcesFileName = 'reloaded_sources.json';
const jsLibraryBundleExtension = '.ddc.js';
const multiRootScheme = 'org-dartlang-app';

class ServerOptions {
final Configuration configuration;
final int port;
Expand Down Expand Up @@ -80,6 +86,7 @@ class WebDevServer {
ServerOptions options,
Stream<daemon.BuildResults> buildResults,
) async {
final basePath = 'http://localhost:${options.port}';
var pipeline = const Pipeline();

if (options.configuration.logRequests) {
Expand All @@ -88,8 +95,47 @@ class WebDevServer {

pipeline = pipeline.addMiddleware(interceptFavicon);

/// JSON-ifiable list of sources that were reloaded in this restart and
/// follows the following format:
///
/// `src`: A string that corresponds to the file path containing a DDC library
/// bundle. To support embedded libraries, the path should include the
/// `baseUri` of the web server.
/// `module`: The name of the library bundle in `src`.
/// `libraries`: An array of strings containing the libraries that were
/// compiled in `src`.
///
/// For example:
/// ```json
/// [
/// {
/// "src": "<baseUri>/<file_name>",
/// "module": "<module_name>",
/// "libraries": ["<lib1>", "<lib2>"],
/// },
/// ]
/// ```
///
/// The path of the output file should stay consistent across the lifetime of
/// the app.
final reloadedSources = <Map<String, dynamic>>[];

// Only provide relevant build results
final filteredBuildResults = buildResults.asyncMap<BuildResult>((results) {
if (options.configuration.canaryFeatures) {
// Clear reloaded sources for the new build results.
reloadedSources.clear();
results.changedAssets?.forEach((uri) {
if (uri.path.endsWith(jsLibraryBundleExtension)) {
final reloadedSource = {
'src': ddcUriToSourceUrl(basePath, options.target, uri),
'module': ddcUriToLibraryId(uri),
'libraries': [ddcUriToLibraryId(uri)],
};
reloadedSources.add(reloadedSource);
}
});
}
final result = results.results.firstWhere(
(result) => result.target == options.target,
);
Expand Down Expand Up @@ -132,20 +178,39 @@ class WebDevServer {
// the load strategy?
final buildSettings = BuildSettings(
appEntrypoint: Uri.parse(
'org-dartlang-app:///${options.target}/main.dart',
'$multiRootScheme:///${options.target}/main.dart',
),
canaryFeatures: options.configuration.canaryFeatures,
isFlutterApp: false,
experiments: options.configuration.experiments,
);

final loadStrategy = BuildRunnerRequireStrategyProvider(
assetHandler,
options.configuration.reload,
assetReader,
buildSettings,
packageConfigPath: findPackageConfigFilePath(),
).strategy;
final LoadStrategy loadStrategy;
if (options.configuration.canaryFeatures) {
final frontendServerFileSystem = LocalFileSystem();
final packageUriMapper = await PackageUriMapper.create(
frontendServerFileSystem,
findPackageConfigUri()!,
useDebuggerModuleNames: false,
);
loadStrategy = FrontendServerDdcLibraryBundleStrategyProvider(
options.configuration.reload,
assetReader,
packageUriMapper,
() async => {},
buildSettings,
packageConfigPath: findPackageConfigFilePath(),
reloadedSourcesUri: Uri.parse('$basePath/$reloadedSourcesFileName'),
).strategy;
} else {
loadStrategy = BuildRunnerRequireStrategyProvider(
assetHandler,
options.configuration.reload,
assetReader,
buildSettings,
packageConfigPath: findPackageConfigFilePath(),
).strategy;
}

if (options.configuration.enableExpressionEvaluation) {
ddcService = ExpressionCompilerService(
Expand All @@ -161,8 +226,10 @@ class WebDevServer {
final debugSettings = DebugSettings(
enableDebugExtension: options.configuration.debugExtension,
enableDebugging: options.configuration.debug,
// ignore: deprecated_member_use
spawnDds: !options.configuration.disableDds,
expressionCompiler: ddcService,
// ignore: deprecated_member_use
devToolsLauncher: shouldServeDevTools
? (String hostname) async {
final server = await DevToolsServer().serveDevTools(
Expand Down Expand Up @@ -191,6 +258,18 @@ class WebDevServer {
);
pipeline = pipeline.addMiddleware(dwds.middleware);
cascade = cascade.add(dwds.handler);
if (options.configuration.canaryFeatures) {
// Add a handler to serve reloaded sources.
cascade = cascade.add((Request request) {
if (request.url.path == reloadedSourcesFileName) {
return Response.ok(
jsonEncode(reloadedSources),
headers: {'Content-Type': 'application/json'},
);
}
return Response.notFound('');
});
}
cascade = cascade.add(assetHandler);
} else {
cascade = cascade.add(assetHandler);
Expand Down Expand Up @@ -233,3 +312,58 @@ class WebDevServer {
);
}
}

/// Transforms a package:build JS asset id [uri] into a source url compatible
/// with DDC's bootstrapper.
///
/// [basePath] is the path from which JS files as served up to but not
/// including the path.
/// [target] is the path whose files will be served from [basePath].
/// [uri] is the asset id's uri being transformed.
///
/// Example:
/// basePath: http://localhost:8080
/// target: web
///
/// uri: asset:some_package/web/main.ddc.js
/// returns http://localhost:8080/main.ddc.js
///
/// uri: package:some_package/src/sub_dir/file.ddc.js
/// returns http://localhost:8080/some_package/src/sub_dir/file.ddc.js
String ddcUriToSourceUrl(String basePath, String target, Uri uri) {
String jsPath;
if (uri.isScheme('asset')) {
// This indicates that this asset is the 'main' web asset. We directly
// serve all files under the package's [target] directory.
var pathParts = uri.pathSegments.skip(1);
if (pathParts.first == target) {
pathParts = pathParts.skip(1);
}
jsPath = pathParts.join('/');
} else if (uri.isScheme('package')) {
jsPath = 'packages/${uri.path}';
} else {
jsPath = uri.path;
}
return '$basePath/$jsPath';
}

/// Transforms a package:build JS asset id [uri] into a library id compatible
/// with DDC's bootstrapper.
///
/// Example:
/// uri: asset:some_package/web/main.ddc.js
/// returns org-dartlang-app:///web/main.dart
///
/// uri: package:some_package/src/sub_dir/file.ddc.js
/// returns package:some_package/src/sub_dir/file.dart
String ddcUriToLibraryId(Uri uri) {
final jsPath = uri.isScheme('package')
? 'package:${uri.path}'
: '$multiRootScheme:///${uri.path}';
final prefix = jsPath.substring(
0,
jsPath.length - jsLibraryBundleExtension.length,
);
return '$prefix.dart';
}
2 changes: 1 addition & 1 deletion webdev/lib/src/version.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions webdev/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: webdev
# Every time this changes you need to run `dart run build_runner build`.
version: 3.8.0-wip
version: 3.8.1-wip
# We should not depend on a dev SDK before publishing.
# publish_to: none
description: >-
Expand All @@ -19,7 +19,8 @@ dependencies:
crypto: ^3.0.2
dds: ^4.1.0
# Pin DWDS to avoid dependency conflicts with vm_service:
dwds: 24.3.11
dwds: ^25.0.0
file: ^7.0.1
http: ^1.0.0
http_multi_server: ^3.2.0
io: ^1.0.3
Expand Down