Rev package version as the extended source map format is a new feature.
Polish `MappingBundle.spanFor` handling of uris that have a suffix that exactly match a source map in the MappingBundle.
R=nweiz@google.com, sigmund@google.com
Review URL: https://codereview.chromium.org//2574593004 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 425e7ae..d2bed8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.10.2
+ * Support for extended source map format.
+ * Polish `MappingBundle.spanFor` handling of URIs that have a suffix that
+ exactly match a source map in the MappingBundle.
+
## 0.10.1+5
* Fix strong mode warning in test.
diff --git a/lib/parser.dart b/lib/parser.dart
index 492e6cf..1c23187 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -8,7 +8,6 @@
import 'dart:collection';
import 'dart:convert';
-import 'package:path/path.dart' as path;
import 'package:source_span/source_span.dart';
import 'builder.dart' as builder;
@@ -185,6 +184,8 @@
for (var map in json) {
var mapping = parseJson(map, mapUrl: mapUrl) as SingleMapping;
var targetUrl = mapping.targetUrl;
+ // TODO(jacobr): verify that targetUrl is valid uri instead of a windows
+ // path.
_mappings[targetUrl] = mapping;
}
}
@@ -205,13 +206,29 @@
if (uri == null) {
throw new ArgumentError.notNull('uri');
}
- if (_mappings.containsKey(uri)) {
- return _mappings[uri].spanFor(line, column, files: files, uri: uri);
- }
- // Fall back to looking up the source map on just the basename.
- var name = path.basename(uri.toString());
- if (_mappings.containsKey(name)) {
- return _mappings[name].spanFor(line, column, files: files, uri: name);
+
+ // Find the longest suffix of the uri that matches the sourcemap
+ // where the suffix starts after a path segment boundary.
+ // We consider ":" and "/" as path segment boundaries so that
+ // "package:" uris can be handled with minimal special casing. Having a
+ // few false positive path segment boundaries is not a significant issue
+ // as we prefer the longest matching prefix.
+ // Using package:path `path.split` to find path segment boundaries would
+ // not generate all of the path segment boundaries we want for "package:"
+ // urls as "package:package_name" would be one path segment when we want
+ // "package" and "package_name" to be sepearate path segments.
+
+ bool onBoundary = true;
+ var separatorCodeUnits = ['/'.codeUnitAt(0), ':'.codeUnitAt(0)];
+ for (var i = 0; i < uri.length; ++i) {
+ if (onBoundary) {
+ var candidate = uri.substring(i);
+ if (_mappings.containsKey(candidate)) {
+ return _mappings[candidate]
+ .spanFor(line, column, files: files, uri: candidate);
+ }
+ }
+ onBoundary = separatorCodeUnits.contains(uri.codeUnitAt(i));
}
// Note: when there is no source map for an uri, this behaves like an
diff --git a/pubspec.yaml b/pubspec.yaml
index 3beb3ea..64c0e9c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: source_maps
-version: 0.10.1+5
+version: 0.10.2
author: Dart Team <misc@dartlang.org>
description: Library to programmatically manipulate source map files.
homepage: http://github.com/dart-lang/source_maps
diff --git a/test/parser_test.dart b/test/parser_test.dart
index 4b2d947..3cccf44 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -43,7 +43,7 @@
'sources': const ['input1.dart'],
'names': const ['var1'],
'mappings': 'AAAAA',
- 'file': 'output1.dart'
+ 'file': 'output.dart'
};
const Map<String, dynamic> MAP_WITH_SOURCE_LOCATION_AND_NAME_2 = const {
@@ -55,9 +55,19 @@
'file': 'output2.dart'
};
+const Map<String, dynamic> MAP_WITH_SOURCE_LOCATION_AND_NAME_3 = const {
+ 'version': 3,
+ 'sourceRoot': 'pkg/',
+ 'sources': const ['input3.dart'],
+ 'names': const ['var3'],
+ 'mappings': 'AAAAA',
+ 'file': '3/output.dart'
+};
+
const List SOURCE_MAP_BUNDLE = const [
MAP_WITH_SOURCE_LOCATION_AND_NAME_1,
- MAP_WITH_SOURCE_LOCATION_AND_NAME_2
+ MAP_WITH_SOURCE_LOCATION_AND_NAME_2,
+ MAP_WITH_SOURCE_LOCATION_AND_NAME_3,
];
main() {
@@ -155,11 +165,12 @@
group('parse with bundle', () {
var mapping =
parseJsonExtended(SOURCE_MAP_BUNDLE, mapUrl: "file:///path/to/map");
+
test('simple', () {
expect(
mapping
.spanForLocation(new SourceLocation(0,
- sourceUrl: new Uri.file('/path/to/output1.dart')))
+ sourceUrl: new Uri.file('/path/to/output.dart')))
.sourceUrl,
Uri.parse("file:///path/to/pkg/input1.dart"));
expect(
@@ -168,13 +179,50 @@
sourceUrl: new Uri.file('/path/to/output2.dart')))
.sourceUrl,
Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(
+ mapping
+ .spanForLocation(new SourceLocation(0,
+ sourceUrl: new Uri.file('/path/to/3/output.dart')))
+ .sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
expect(
- mapping.spanFor(0, 0, uri: "file:///path/to/output1.dart").sourceUrl,
+ mapping.spanFor(0, 0, uri: "file:///path/to/output.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input1.dart"));
expect(
mapping.spanFor(0, 0, uri: "file:///path/to/output2.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(
+ mapping.spanFor(0, 0, uri: "file:///path/to/3/output.dart").sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
+ });
+
+ test('package uris', () {
+ expect(
+ mapping
+ .spanForLocation(new SourceLocation(0,
+ sourceUrl: Uri.parse('package:1/output.dart')))
+ .sourceUrl,
+ Uri.parse("file:///path/to/pkg/input1.dart"));
+ expect(
+ mapping
+ .spanForLocation(new SourceLocation(0,
+ sourceUrl: Uri.parse('package:2/output2.dart')))
+ .sourceUrl,
+ Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(
+ mapping
+ .spanForLocation(new SourceLocation(0,
+ sourceUrl: Uri.parse('package:3/output.dart')))
+ .sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
+
+ expect(mapping.spanFor(0, 0, uri: "package:1/output.dart").sourceUrl,
+ Uri.parse("file:///path/to/pkg/input1.dart"));
+ expect(mapping.spanFor(0, 0, uri: "package:2/output2.dart").sourceUrl,
+ Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(mapping.spanFor(0, 0, uri: "package:3/output.dart").sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
});
test('unmapped path', () {
@@ -194,20 +242,24 @@
});
test('incomplete paths', () {
- expect(mapping.spanFor(0, 0, uri: "output1.dart").sourceUrl,
+ expect(mapping.spanFor(0, 0, uri: "output.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input1.dart"));
expect(mapping.spanFor(0, 0, uri: "output2.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(mapping.spanFor(0, 0, uri: "3/output.dart").sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
});
test('parseExtended', () {
var mapping = parseExtended(JSON.encode(SOURCE_MAP_BUNDLE),
mapUrl: "file:///path/to/map");
- expect(mapping.spanFor(0, 0, uri: "output1.dart").sourceUrl,
+ expect(mapping.spanFor(0, 0, uri: "output.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input1.dart"));
expect(mapping.spanFor(0, 0, uri: "output2.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(mapping.spanFor(0, 0, uri: "3/output.dart").sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
});
// Test that the source map can handle cases where the uri passed in is
@@ -217,7 +269,7 @@
expect(
mapping
.spanForLocation(new SourceLocation(0,
- sourceUrl: Uri.parse('http://localhost/output1.dart')))
+ sourceUrl: Uri.parse('http://localhost/output.dart')))
.sourceUrl,
Uri.parse("file:///path/to/pkg/input1.dart"));
expect(
@@ -226,13 +278,24 @@
sourceUrl: Uri.parse('http://localhost/output2.dart')))
.sourceUrl,
Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(
+ mapping
+ .spanForLocation(new SourceLocation(0,
+ sourceUrl: Uri.parse('http://localhost/3/output.dart')))
+ .sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
expect(
- mapping.spanFor(0, 0, uri: "http://localhost/output1.dart").sourceUrl,
+ mapping.spanFor(0, 0, uri: "http://localhost/output.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input1.dart"));
expect(
mapping.spanFor(0, 0, uri: "http://localhost/output2.dart").sourceUrl,
Uri.parse("file:///path/to/pkg/input2.dart"));
+ expect(
+ mapping
+ .spanFor(0, 0, uri: "http://localhost/3/output.dart")
+ .sourceUrl,
+ Uri.parse("file:///path/to/pkg/input3.dart"));
});
});