Skip to content
3 changes: 3 additions & 0 deletions frontend_server_common/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.2.0
- Migrate to null safety

## 0.1.1
- Remove dead code

Expand Down
89 changes: 46 additions & 43 deletions frontend_server_common/lib/src/asset_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.9

// Note: this is a copy from flutter tools, updated to work with dwds tests

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

import 'package:dwds/dwds.dart';
import 'package:dwds/asset_reader.dart';
import 'package:file/file.dart';
import 'package:logging/logging.dart';
import 'package:mime/mime.dart' as mime;
Expand All @@ -32,10 +30,10 @@ class TestAssetServer implements AssetReader {
static const String _defaultMimeType = 'application/octet-stream';
final FileSystem _fileSystem;
final HttpServer _httpServer;
final Map<String, Uint8List> _files = <String, Uint8List>{};
final Map<String, Uint8List> _sourcemaps = <String, Uint8List>{};
final Map<String, Uint8List> _metadata = <String, Uint8List>{};
String _mergedMetadata;
final Map<String, Uint8List> _files = {};
final Map<String, Uint8List> _sourceMaps = {};
final Map<String, Uint8List> _metadata = {};
late String _mergedMetadata;
final PackageConfig _packageConfig;
final InternetAddress internetAddress;

Expand All @@ -47,6 +45,15 @@ class TestAssetServer implements AssetReader {
this._fileSystem,
) : basePath = _parseBasePathFromIndexHtml(index);

bool hasFile(String path) => _files.containsKey(path);
Uint8List getFile(String path) => _files[path]!;

bool hasSourceMap(String path) => _sourceMaps.containsKey(path);
Uint8List getSourceMap(String path) => _sourceMaps[path]!;

bool hasMetadata(String path) => _metadata.containsKey(path);
Uint8List getMetadata(String path) => _metadata[path]!;

/// Start the web asset server on a [hostname] and [port].
///
/// Unhandled exceptions will throw a exception with the error and stack
Expand All @@ -66,10 +73,6 @@ class TestAssetServer implements AssetReader {
return server;
}

Uint8List getFile(String path) => _files[path];

Uint8List getSourceMap(String path) => _sourcemaps[path];

// handle requests for JavaScript source, dart sources maps, or asset files.
Future<shelf.Response> handleRequest(shelf.Request request) async {
var headers = <String, String>{};
Expand All @@ -94,21 +97,18 @@ class TestAssetServer implements AssetReader {
requestPath = _stripBasePath(requestPath, basePath) ?? requestPath;

requestPath = requestPath.startsWith('/') ? requestPath : '/$requestPath';
if (requestPath == null) {
return shelf.Response.notFound('');
}

// If this is a JavaScript file, it must be in the in-memory cache.
// Attempt to look up the file by URI.
if (_files.containsKey(requestPath)) {
if (hasFile(requestPath)) {
final List<int> bytes = getFile(requestPath);
headers[HttpHeaders.contentLengthHeader] = bytes.length.toString();
headers[HttpHeaders.contentTypeHeader] = 'application/javascript';
return shelf.Response.ok(bytes, headers: headers);
}
// If this is a sourcemap file, then it might be in the in-memory cache.
// Attempt to lookup the file by URI.
if (_sourcemaps.containsKey(requestPath)) {
if (hasSourceMap(requestPath)) {
final List<int> bytes = getSourceMap(requestPath);
headers[HttpHeaders.contentLengthHeader] = bytes.length.toString();
headers[HttpHeaders.contentTypeHeader] = 'application/json';
Expand All @@ -124,7 +124,7 @@ class TestAssetServer implements AssetReader {
// Attempt to determine the file's mime type. if this is not provided some
// browsers will refuse to render images/show video et cetera. If the tool
// cannot determine a mime type, fall back to application/octet-stream.
String mimeType;
String? mimeType;
if (length >= 12) {
mimeType = mime.lookupMimeType(
file.path,
Expand Down Expand Up @@ -160,10 +160,6 @@ class TestAssetServer implements AssetReader {
var manifest =
_castStringKeyedMap(json.decode(manifestFile.readAsStringSync()));
for (var filePath in manifest.keys) {
if (filePath == null) {
_logger.severe('Invalid manfiest file: $filePath');
continue;
}
var offsets = _castStringKeyedMap(manifest[filePath]);
var codeOffsets = (offsets['code'] as List<dynamic>).cast<int>();
var sourcemapOffsets =
Expand Down Expand Up @@ -200,7 +196,7 @@ class TestAssetServer implements AssetReader {
sourcemapStart,
sourcemapEnd - sourcemapStart,
);
_sourcemaps['$filePath.map'] = sourcemapView;
_sourceMaps['$filePath.map'] = sourcemapView;

var metadataStart = metadataOffsets[0];
var metadataEnd = metadataOffsets[1];
Expand Down Expand Up @@ -259,36 +255,42 @@ class TestAssetServer implements AssetReader {
}

@override
Future<String> dartSourceContents(String serverPath) {
serverPath = _stripBasePath(serverPath, basePath);
var result = _resolveDartFile(serverPath);
if (result.existsSync()) {
return result.readAsString();
Future<String?> dartSourceContents(String serverPath) async {
final stripped = _stripBasePath(serverPath, basePath);
if (stripped != null) {
var result = _resolveDartFile(stripped);
if (result.existsSync()) {
return result.readAsString();
}
}
_logger.severe('Source not found: $serverPath');
return null;
}

@override
Future<String> sourceMapContents(String serverPath) async {
serverPath = _stripBasePath(serverPath, basePath);
var path = '/$serverPath';
if (_sourcemaps.containsKey(path)) {
return utf8.decode(_sourcemaps[path]);
Future<String?> sourceMapContents(String serverPath) async {
final stripped = _stripBasePath(serverPath, basePath);
if (stripped != null) {
var path = '/$stripped';
if (hasSourceMap(path)) {
return utf8.decode(getSourceMap(path));
}
}
_logger.severe('Source map not found: $serverPath');
return null;
}

@override
Future<String> metadataContents(String serverPath) async {
serverPath = _stripBasePath(serverPath, basePath);
if (serverPath.endsWith('.ddc_merged_metadata')) {
return _mergedMetadata;
}
var path = '/$serverPath';
if (_metadata.containsKey(path)) {
return utf8.decode(_metadata[path]);
Future<String?> metadataContents(String serverPath) async {
final stripped = _stripBasePath(serverPath, basePath);
if (stripped != null) {
if (stripped.endsWith('.ddc_merged_metadata')) {
return _mergedMetadata;
}
var path = '/$stripped';
if (hasMetadata(path)) {
return utf8.decode(getMetadata(path));
}
}
_logger.severe('Metadata not found: $serverPath');
return null;
Expand All @@ -299,7 +301,7 @@ class TestAssetServer implements AssetReader {
/// the same structure (`Map<String, dynamic>`) with the correct runtime types.
Map<String, dynamic> _castStringKeyedMap(dynamic untyped) {
var map = untyped as Map<dynamic, dynamic>;
return map?.cast<String, dynamic>();
return map.cast<String, dynamic>();
}

String _stripLeadingSlashes(String path) {
Expand All @@ -309,7 +311,7 @@ String _stripLeadingSlashes(String path) {
return path;
}

String _stripBasePath(String path, String basePath) {
String? _stripBasePath(String path, String basePath) {
path = _stripLeadingSlashes(path);
if (path.startsWith(basePath)) {
path = path.substring(basePath.length);
Expand All @@ -327,5 +329,6 @@ String _parseBasePathFromIndexHtml(String index) {
}
final contents = file.readAsStringSync();
final matches = RegExp(r'<base href="/([^>]*)/">').allMatches(contents);
return matches.isEmpty ? '' : matches.first.group(1);
if (matches.isEmpty) return '';
return matches.first.group(1) ?? '';
}
12 changes: 4 additions & 8 deletions frontend_server_common/lib/src/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.9

// Note: this is a copy from flutter tools, updated to work with dwds tests

import 'package:meta/meta.dart';

/// The JavaScript bootstrap script to support in-browser hot restart.
///
/// The [requireUrl] loads our cached RequireJS script file. The [mapperUrl]
Expand All @@ -18,9 +14,9 @@ import 'package:meta/meta.dart';
/// and is responsible for bootstrapping the RequireJS modules and attaching
/// the hot reload hooks.
String generateBootstrapScript({
@required String requireUrl,
@required String mapperUrl,
@required String entrypoint,
required String requireUrl,
required String mapperUrl,
required String entrypoint,
}) {
return '''
"use strict";
Expand Down Expand Up @@ -53,7 +49,7 @@ document.head.appendChild(requireEl);
/// 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 generateMainModule({@required String entrypoint}) {
String generateMainModule({required String entrypoint}) {
return '''/* ENTRYPOINT_EXTENTION_MARKER */

// Create the main module loaded below.
Expand Down
43 changes: 18 additions & 25 deletions frontend_server_common/lib/src/devfs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.9

// Note: this is a copy from flutter tools, updated to work with dwds tests

import 'dart:io';

import 'package:dwds/dwds.dart';
import 'package:dwds/asset_reader.dart';
import 'package:file/file.dart';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'package:path/path.dart' as p;

Expand All @@ -23,28 +20,27 @@ final String dartWebSdkPath = p.join(dartSdkPath, 'lib', 'dev_compiler');

class WebDevFS {
WebDevFS({
this.fileSystem,
this.hostname,
this.port,
this.projectDirectory,
this.packageConfigFile,
this.index,
this.urlTunneller,
this.soundNullSafety,
required this.fileSystem,
required this.hostname,
required this.port,
required this.projectDirectory,
required this.packageConfigFile,
required this.index,
required this.urlTunneller,
required this.soundNullSafety,
});

final FileSystem fileSystem;
TestAssetServer assetServer;
late final TestAssetServer assetServer;
final String hostname;
final int port;
final Uri projectDirectory;
final Uri packageConfigFile;
final String index;
final UrlEncoder urlTunneller;
final bool soundNullSafety;
Directory _savedCurrentDirectory;
List<Uri> sources;
PackageConfig _packageConfig;
late final Directory _savedCurrentDirectory;
late final PackageConfig _packageConfig;

Future<Uri> create() async {
_savedCurrentDirectory = fileSystem.currentDirectory;
Expand All @@ -61,16 +57,15 @@ class WebDevFS {

Future<void> dispose() {
fileSystem.currentDirectory = _savedCurrentDirectory;
return assetServer?.close();
return assetServer.close();
}

Future<UpdateFSReport> update({
Uri mainUri,
String dillOutputPath,
@required ResidentCompiler generator,
List<Uri> invalidatedFiles,
required Uri mainUri,
required String dillOutputPath,
required ResidentCompiler generator,
required List<Uri> invalidatedFiles,
}) async {
assert(generator != null);
final mainPath = mainUri.toFilePath();
final outputDirectoryPath = fileSystem.file(mainPath).parent.path;
final entryPoint = mainUri.toString();
Expand Down Expand Up @@ -108,8 +103,6 @@ class WebDevFS {
return UpdateFSReport(success: false);
}

// list of sources that needs to be monitored are in [compilerOutput.sources]
sources = compilerOutput.sources;
File codeFile;
File manifestFile;
File sourcemapFile;
Expand Down Expand Up @@ -200,7 +193,7 @@ class UpdateFSReport {
/// mode.
///
/// Only used for JavaScript compilation.
List<String> invalidatedModules;
List<String>? invalidatedModules;
}

String _filePathToUriFragment(String path) {
Expand Down
Loading