| Index: mojo/public/dart/third_party/package_config/lib/packages_file.dart
|
| diff --git a/mojo/public/dart/third_party/package_config/lib/packages_file.dart b/mojo/public/dart/third_party/package_config/lib/packages_file.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f30781dc3fb3b0a076e2990451e48e41114fa8e9
|
| --- /dev/null
|
| +++ b/mojo/public/dart/third_party/package_config/lib/packages_file.dart
|
| @@ -0,0 +1,190 @@
|
| +// Copyright (c) 2015, 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.
|
| +
|
| +library package_config.packages_file;
|
| +
|
| +import "package:charcode/ascii.dart";
|
| +import "src/util.dart" show isValidPackageName;
|
| +
|
| +/// Parses a `.packages` file into a map from package name to base URI.
|
| +///
|
| +/// The [source] is the byte content of a `.packages` file, assumed to be
|
| +/// UTF-8 encoded. In practice, all significant parts of the file must be ASCII,
|
| +/// so Latin-1 or Windows-1252 encoding will also work fine.
|
| +///
|
| +/// If the file content is available as a string, its [String.codeUnits] can
|
| +/// be used as the `source` argument of this function.
|
| +///
|
| +/// The [baseLocation] is used as a base URI to resolve all relative
|
| +/// URI references against.
|
| +/// If the content was read from a file, `baseLocation` should be the
|
| +/// location of that file.
|
| +///
|
| +/// Returns a simple mapping from package name to package location.
|
| +Map<String, Uri> parse(List<int> source, Uri baseLocation) {
|
| + int index = 0;
|
| + Map<String, Uri> result = <String, Uri>{};
|
| + while (index < source.length) {
|
| + bool isComment = false;
|
| + int start = index;
|
| + int separatorIndex = -1;
|
| + int end = source.length;
|
| + int char = source[index++];
|
| + if (char == $cr || char == $lf) {
|
| + continue;
|
| + }
|
| + if (char == $colon) {
|
| + throw new FormatException("Missing package name", source, index - 1);
|
| + }
|
| + isComment = char == $hash;
|
| + while (index < source.length) {
|
| + char = source[index++];
|
| + if (char == $colon && separatorIndex < 0) {
|
| + separatorIndex = index - 1;
|
| + } else if (char == $cr || char == $lf) {
|
| + end = index - 1;
|
| + break;
|
| + }
|
| + }
|
| + if (isComment) continue;
|
| + if (separatorIndex < 0) {
|
| + throw new FormatException("No ':' on line", source, index - 1);
|
| + }
|
| + var packageName = new String.fromCharCodes(source, start, separatorIndex);
|
| + if (!isValidPackageName(packageName)) {
|
| + throw new FormatException("Not a valid package name", packageName, 0);
|
| + }
|
| + var packageUri = new String.fromCharCodes(source, separatorIndex + 1, end);
|
| + var packageLocation = Uri.parse(packageUri);
|
| + packageLocation = baseLocation.resolveUri(packageLocation);
|
| + if (!packageLocation.path.endsWith('/')) {
|
| + packageLocation =
|
| + packageLocation.replace(path: packageLocation.path + "/");
|
| + }
|
| + if (result.containsKey(packageName)) {
|
| + throw new FormatException(
|
| + "Same package name occured twice.", source, start);
|
| + }
|
| + result[packageName] = packageLocation;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +/// Writes the mapping to a [StringSink].
|
| +///
|
| +/// If [comment] is provided, the output will contain this comment
|
| +/// with `# ` in front of each line.
|
| +/// Lines are defined as ending in line feed (`'\n'`). If the final
|
| +/// line of the comment doesn't end in a line feed, one will be added.
|
| +///
|
| +/// If [baseUri] is provided, package locations will be made relative
|
| +/// to the base URI, if possible, before writing.
|
| +///
|
| +/// All the keys of [packageMapping] must be valid package names,
|
| +/// and the values must be URIs that do not have the `package:` scheme.
|
| +void write(StringSink output, Map<String, Uri> packageMapping,
|
| + {Uri baseUri, String comment}) {
|
| + if (baseUri != null && !baseUri.isAbsolute) {
|
| + throw new ArgumentError.value(baseUri, "baseUri", "Must be absolute");
|
| + }
|
| +
|
| + if (comment != null) {
|
| + var lines = comment.split('\n');
|
| + if (lines.last.isEmpty) lines.removeLast();
|
| + for (var commentLine in lines) {
|
| + output.write('# ');
|
| + output.writeln(commentLine);
|
| + }
|
| + } else {
|
| + output.write("# generated by package:package_config at ");
|
| + output.write(new DateTime.now());
|
| + output.writeln();
|
| + }
|
| +
|
| + packageMapping.forEach((String packageName, Uri uri) {
|
| + // Validate packageName.
|
| + if (!isValidPackageName(packageName)) {
|
| + throw new ArgumentError('"$packageName" is not a valid package name');
|
| + }
|
| + if (uri.scheme == "package") {
|
| + throw new ArgumentError.value(
|
| + "Package location must not be a package: URI", uri.toString());
|
| + }
|
| + output.write(packageName);
|
| + output.write(':');
|
| + // If baseUri provided, make uri relative.
|
| + if (baseUri != null) {
|
| + uri = _relativize(uri, baseUri);
|
| + }
|
| + output.write(uri);
|
| + if (!uri.path.endsWith('/')) {
|
| + output.write('/');
|
| + }
|
| + output.writeln();
|
| + });
|
| +}
|
| +
|
| +/// Attempts to return a relative URI for [uri].
|
| +///
|
| +/// The result URI satisfies `baseUri.resolveUri(result) == uri`,
|
| +/// but may be relative.
|
| +/// The `baseUri` must be absolute.
|
| +Uri _relativize(Uri uri, Uri baseUri) {
|
| + assert(baseUri.isAbsolute);
|
| + if (uri.hasQuery || uri.hasFragment) {
|
| + uri = new Uri(
|
| + scheme: uri.scheme,
|
| + userInfo: uri.hasAuthority ? uri.userInfo : null,
|
| + host: uri.hasAuthority ? uri.host : null,
|
| + port: uri.hasAuthority ? uri.port : null,
|
| + path: uri.path);
|
| + }
|
| +
|
| + // Already relative. We assume the caller knows what they are doing.
|
| + if (!uri.isAbsolute) return uri;
|
| +
|
| + if (baseUri.scheme != uri.scheme) {
|
| + return uri;
|
| + }
|
| +
|
| + // If authority differs, we could remove the scheme, but it's not worth it.
|
| + if (uri.hasAuthority != baseUri.hasAuthority) return uri;
|
| + if (uri.hasAuthority) {
|
| + if (uri.userInfo != baseUri.userInfo ||
|
| + uri.host.toLowerCase() != baseUri.host.toLowerCase() ||
|
| + uri.port != baseUri.port) {
|
| + return uri;
|
| + }
|
| + }
|
| +
|
| + baseUri = _normalizePath(baseUri);
|
| + List<String> base = baseUri.pathSegments.toList();
|
| + if (base.isNotEmpty) {
|
| + base = new List<String>.from(base)..removeLast();
|
| + }
|
| + uri = _normalizePath(uri);
|
| + List<String> target = uri.pathSegments.toList();
|
| + if (target.isNotEmpty && target.last.isEmpty) target.removeLast();
|
| + int index = 0;
|
| + while (index < base.length && index < target.length) {
|
| + if (base[index] != target[index]) {
|
| + break;
|
| + }
|
| + index++;
|
| + }
|
| + if (index == base.length) {
|
| + if (index == target.length) {
|
| + return new Uri(path: "./");
|
| + }
|
| + return new Uri(path: target.skip(index).join('/'));
|
| + } else if (index > 0) {
|
| + return new Uri(
|
| + path: '../' * (base.length - index) + target.skip(index).join('/'));
|
| + } else {
|
| + return uri;
|
| + }
|
| +}
|
| +
|
| +// TODO: inline to uri.normalizePath() when we move to 1.11
|
| +Uri _normalizePath(Uri uri) => new Uri().resolveUri(uri);
|
|
|