Skip to content
Open
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
2 changes: 1 addition & 1 deletion frontend_server_client/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include: package:analysis_config/analysis_options.yaml
include: package:dart_flutter_team_lints/analysis_options.yaml
2 changes: 1 addition & 1 deletion frontend_server_client/example/app/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Future<void> main() async {
print(message);
while (!message.contains('goodbye')) {
print('waiting for hot reload to change message');
await Future.delayed(const Duration(seconds: 1));
await Future<void>.delayed(const Duration(seconds: 1));
}
print(message);
}
Expand Down
16 changes: 13 additions & 3 deletions frontend_server_client/example/vm_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,26 @@ import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';

void main(List<String> args) async {
// Change to package root so relative paths work in CI
final scriptDir = p.dirname(p.fromUri(Platform.script));
final packageRoot = p.dirname(scriptDir);
Directory.current = packageRoot;

try {
watch.start();
if (args.isNotEmpty) {
throw ArgumentError('No command line args are supported');
}

final packagesPath = findNearestPackageConfigPath();
final client = await FrontendServerClient.start(
'org-dartlang-root:///$app',
outputDill,
p.join(sdkDir, 'lib', '_internal', 'vm_platform_strong.dill'),
packagesJson: packagesPath ?? '.dart_tool/package_config.json',
target: 'vm',
fileSystemRoots: [p.url.current],
// Use an absolute filesystem root so org-dartlang-root:/// URIs resolve reliably in CI
fileSystemRoots: [Directory.current.path],
fileSystemScheme: 'org-dartlang-root',
verbose: true,
);
Expand All @@ -38,7 +46,7 @@ void main(List<String> args) async {
'--enable-vm-service=0',
result.dillOutput!,
]);
final sawHelloWorld = Completer();
final sawHelloWorld = Completer<void>();
appProcess.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
Expand All @@ -52,7 +60,9 @@ void main(List<String> args) async {
)) {
final observatoryUri =
'${line.split(' ').last.replaceFirst('http', 'ws')}ws';
vmServiceCompleter.complete(vmServiceConnectUri(observatoryUri));
if (!vmServiceCompleter.isCompleted) {
vmServiceCompleter.complete(vmServiceConnectUri(observatoryUri));
}
}
});
appProcess.stderr
Expand Down
12 changes: 10 additions & 2 deletions frontend_server_client/example/web_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import 'package:shelf_packages_handler/shelf_packages_handler.dart';
import 'package:shelf_static/shelf_static.dart';

void main(List<String> args) async {
// Change to package root so relative paths work in CI
final scriptDir = p.dirname(p.fromUri(Platform.script));
final packageRoot = p.dirname(scriptDir);
Directory.current = packageRoot;

try {
watch.start();
if (args.isNotEmpty) {
Expand Down Expand Up @@ -41,12 +46,15 @@ void main(List<String> args) async {
}

_print('starting frontend server');
final packagesPath = findNearestPackageConfigPath();
final client = await DartDevcFrontendServerClient.start(
'org-dartlang-root:///$app',
outputDill,
fileSystemRoots: [p.current],
// Use an absolute filesystem root so org-dartlang-root:/// URIs resolve reliably in CI
fileSystemRoots: [Directory.current.path],
fileSystemScheme: 'org-dartlang-root',
platformKernel: p.toUri(sdkKernelPath).toString(),
packagesJson: packagesPath ?? '.dart_tool/package_config.json',
verbose: true,
);

Expand All @@ -58,7 +66,7 @@ void main(List<String> args) async {
_print('starting shelf server');
final cascade = Cascade()
.add(_clientHandler(client))
.add(createStaticHandler(p.current))
.add(createStaticHandler(Directory.current.path))
.add(createFileHandler(dartSdkJs, url: 'example/app/dart_sdk.js'))
.add(
createFileHandler(
Expand Down
1 change: 1 addition & 0 deletions frontend_server_client/lib/frontend_server_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export 'src/dartdevc_frontend_server_client.dart'
show DartDevcFrontendServerClient;
export 'src/frontend_server_client.dart'
show CompileResult, FrontendServerClient;
export 'src/package_config_utils.dart' show findNearestPackageConfigPath;
12 changes: 6 additions & 6 deletions frontend_server_client/lib/src/dartdevc_bootstrap_amd.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ document.head.appendChild(requireEl);
/// method.
///
/// RE: Object.keys usage in app.main:
/// This attaches the main entrypoint and hot reload functionality to the window.
/// The app module will have a single property which contains the actual application
/// code. The property name is based off of the entrypoint that is generated, for example
/// the file `foo/bar/baz.dart` will generate a property named approximately
/// `foo__bar__baz`. Rather than attempt to guess, we assume the first property of
/// this object is the module.
/// This attaches the main entrypoint and hot reload functionality to the
/// window. The app module will have a single property which contains the
/// actual application code. The property name is based off of the entrypoint
/// that is generated, for example the file `foo/bar/baz.dart` will generate
/// a property named approximately `foo__bar__baz`. Rather than attempt to
/// guess, we assume the first property of this object is the module.
String generateAmdMainModule({required String entrypoint}) {
return '''/* ENTRYPOINT_EXTENTION_MARKER */
// Create the main module loaded below.
Expand Down
3 changes: 2 additions & 1 deletion frontend_server_client/lib/src/frontend_server_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ class FrontendServerClient {
removedSources.add(diffUri);
} else {
throw StateError(
'unrecognized diff line, should start with a + or - but got: $line',
'unrecognized diff line, should start with a + or - '
'but got: $line',
);
}
continue;
Expand Down
46 changes: 46 additions & 0 deletions frontend_server_client/lib/src/package_config_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Utility functions to locate a package_config.json for pub/workspace setups.

import 'dart:io';

import 'package:package_config/package_config.dart';
import 'package:path/path.dart' as p;

/// Walks up from [start] (or the current directory if omitted) to find the
/// nearest `.dart_tool/package_config.json`.
///
/// Returns the absolute file path, or `null` if none is found.
String? findNearestPackageConfigPath([Directory? start]) {
var dir = (start ?? Directory.current).absolute;
while (true) {
final file = File(p.join(dir.path, '.dart_tool', 'package_config.json'));
if (file.existsSync()) return file.path;
final parent = dir.parent;
if (parent.path == dir.path) return null;
dir = parent;
}
}

/// Returns an absolute path under the given [packageName]'s root directory,
/// resolving using the nearest workspace `.dart_tool/package_config.json`.
///
/// This is robust for pub workspace monorepos where the nearest package
/// config lives at the repo root and contains individual entries for each
/// package with its own root.
Future<String> pathFromNearestPackageConfig(
String relativePath, {
String packageName = 'frontend_server_client',
}) async {
final configPath = findNearestPackageConfigPath();
if (configPath == null) {
throw StateError('Could not locate .dart_tool/package_config.json');
}
final config = await loadPackageConfigUri(Uri.file(configPath));
final pkg = config.packages.firstWhere(
(p0) => p0.name == packageName,
orElse: () => throw StateError(
'Package $packageName not found in package config at $configPath',
),
);
final packageRootDir = p.fromUri(pkg.root);
return p.normalize(p.join(packageRootDir, relativePath));
}
5 changes: 2 additions & 3 deletions frontend_server_client/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ environment:

dependencies:
async: ^2.5.0
package_config: ^2.1.0
path: ^1.8.0

dev_dependencies:
analysis_config:
path: ../_analysis_config
package_config: ^2.0.0
dart_flutter_team_lints: ^3.5.2
shelf: ^1.0.0
shelf_packages_handler: ^3.0.0
shelf_static: ^1.1.0
Expand Down
7 changes: 6 additions & 1 deletion frontend_server_client/test/example/vm_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ library;

import 'dart:io';

import 'package:frontend_server_client/src/package_config_utils.dart';
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';

void main() {
test('vm client example can build and rebuild an app', () async {
// Resolve the example script path based on the package root.
final exampleFilePath = await pathFromNearestPackageConfig(
'example/vm_client.dart',
);
final process = await TestProcess.start(Platform.resolvedExecutable, [
'run',
'example/vm_client.dart',
exampleFilePath,
]);
await expectLater(
process.stdout,
Expand Down
7 changes: 6 additions & 1 deletion frontend_server_client/test/example/web_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ library;

import 'dart:io';

import 'package:frontend_server_client/src/package_config_utils.dart';
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';

void main() {
test('web client example can build and rebuild an app', () async {
// Resolve the example script path based on the package root.
final exampleFilePath = await pathFromNearestPackageConfig(
'example/web_client.dart',
);
final process = await TestProcess.start(Platform.resolvedExecutable, [
'run',
'example/web_client.dart',
exampleFilePath,
]);
await expectLater(
process.stdout,
Expand Down
Loading