blob: b3210da6c77a55da601e9f0f08fbf10a3cec6ded [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:collection/collection.dart';
import 'package:file/file.dart';
import 'package:logging/logging.dart';
import 'package:package_config/package_config.dart';
/// A reader for Dart sources and related source maps.
abstract class AssetReader {
/// Base path of the application, for example, set up in the index file:
///
/// ```
/// <html>
/// <head>
/// <base href="/abc/">
/// <script src="main.dart.js"></script>
/// </head>
/// </html>
/// ```
String get basePath;
/// Returns the contents for a source map at the provided server path, or
/// null if the resource does not exist.
Future<String?> sourceMapContents(String serverPath);
/// Returns the contents for a dart source at the provided server path, or
/// null if the resource does not exist.
Future<String?> dartSourceContents(String serverPath);
/// Returns the contents for the merged metadata output at the provided path,
/// or null if the resource does not exist.
Future<String?> metadataContents(String serverPath);
/// Closes connections
Future<void> close();
}
class PackageUriMapper {
final _logger = Logger('PackageUriMapper');
final PackageConfig packageConfig;
final bool useDebuggerModuleNames;
static Future<PackageUriMapper> create(
FileSystem fileSystem,
Uri packageConfigFile, {
bool useDebuggerModuleNames = false,
}) async {
final packageConfig = await loadPackageConfig(
fileSystem.file(packageConfigFile),
);
return PackageUriMapper(
packageConfig,
useDebuggerModuleNames: useDebuggerModuleNames,
);
}
PackageUriMapper(this.packageConfig, {this.useDebuggerModuleNames = false});
/// Compute server path for package uri.
///
/// Note: needs to match `urlForComponentUri` in javascript_bundle.dart
/// in SDK code.
String? packageUriToServerPath(Uri packageUri) {
final defaultServerPath = '/packages/${packageUri.path}';
if (packageUri.isScheme('package')) {
if (!useDebuggerModuleNames) {
return defaultServerPath;
}
final resolvedUri = packageConfig.resolve(packageUri);
if (resolvedUri == null) {
_logger.severe('Cannot resolve package uri $packageUri');
return defaultServerPath;
}
final package = packageConfig.packageOf(resolvedUri);
if (package == null) {
_logger.severe('Cannot find package for package uri $packageUri');
return defaultServerPath;
}
final root = package.root;
final relativeUrl = resolvedUri.toString().replaceFirst('$root', '');
final relativeRoot = _getRelativeRoot(root);
final ret =
relativeRoot == null
? 'packages/$relativeUrl'
: 'packages/$relativeRoot/$relativeUrl';
return ret;
}
_logger.severe('Expected package uri, but found $packageUri');
return null;
}
/// Compute resolved file uri for a server path.
Uri? serverPathToResolvedUri(String serverPath) {
serverPath = stripLeadingSlashes(serverPath);
final segments = serverPath.split('/');
if (segments.first == 'packages') {
if (!useDebuggerModuleNames) {
return packageConfig.resolve(
Uri(scheme: 'package', pathSegments: segments.skip(1)),
);
}
final relativeRoot = segments.skip(1).first;
final relativeUrl = segments.skip(2).join('/');
final package = packageConfig.packages.firstWhere(
(Package p) => _getRelativeRoot(p.root) == relativeRoot,
);
final resolvedUri = package.root.resolve(relativeUrl);
return resolvedUri;
}
_logger.severe('Expected "packages/" path, but found $serverPath');
return null;
}
}
String stripLeadingSlashes(String path) {
while (path.startsWith('/')) {
path = path.substring(1);
}
return path;
}
String? _getRelativeRoot(Uri root) =>
root.pathSegments.lastWhereOrNull((segment) => segment.isNotEmpty);