Add support for the sourcesContent field (#28)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7c1b06..8ef25be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.10.5
+
+* Add a `SingleMapping.files` field which provides access to `SourceFile`s
+ representing the `"sourcesContent"` fields in the source map.
+
+* Add an `includeSourceContents` flag to `SingleMapping.toJson()` which
+ indicates whether to include source file contents in the source map.
+
## 0.10.4
* Implement `highlight` in `SourceMapFileSpan`.
* Require version `^1.3.0` of `source_span`.
diff --git a/lib/parser.dart b/lib/parser.dart
index 3b65e89..e46b783 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -5,7 +5,6 @@
/// Contains the top-level function to parse source maps version 3.
library source_maps.parser;
-import 'dart:collection';
import 'dart:convert';
import 'package:source_span/source_span.dart';
@@ -258,6 +257,16 @@
/// Source names used in the mapping, indexed by id.
final List<String> names;
+ /// The [SourceFile]s to which the entries in [lines] refer.
+ ///
+ /// This is in the same order as [urls]. If this was constructed using
+ /// [fromEntries], this contains files from any [FileLocation]s used to build
+ /// the mapping. If it was parsed from JSON, it contains files for any sources
+ /// whose contents were provided via the `"sourcesContent"` field.
+ ///
+ /// Files whose contents aren't available are `null`.
+ final List<SourceFile> files;
+
/// Entries indicating the beginning of each span.
final List<TargetLineEntry> lines;
@@ -269,7 +278,7 @@
final Uri _mapUrl;
- SingleMapping._(this.targetUrl, this.urls, this.names, this.lines)
+ SingleMapping._(this.targetUrl, this.files, this.urls, this.names, this.lines)
: _mapUrl = null;
factory SingleMapping.fromEntries(Iterable<builder.Entry> entries,
@@ -279,12 +288,15 @@
var lines = <TargetLineEntry>[];
// Indices associated with file urls that will be part of the source map. We
- // use a linked hash-map so that `_urls.keys[_urls[u]] == u`
- var urls = new LinkedHashMap<String, int>();
+ // rely on map order so that `urls.keys[urls[u]] == u`
+ var urls = <String, int>{};
// Indices associated with identifiers that will be part of the source map.
- // We use a linked hash-map so that `_names.keys[_names[n]] == n`
- var names = new LinkedHashMap<String, int>();
+ // We rely on map order so that `names.keys[names[n]] == n`
+ var names = <String, int>{};
+
+ /// The file for each URL, indexed by [urls]' values.
+ var files = <int, SourceFile>{};
var lineNum;
List<TargetEntry> targetEntries;
@@ -301,6 +313,12 @@
var sourceUrl = sourceEntry.source.sourceUrl;
var urlId = urls.putIfAbsent(
sourceUrl == null ? '' : sourceUrl.toString(), () => urls.length);
+
+ if (sourceEntry.source is FileLocation) {
+ files.putIfAbsent(
+ urlId, () => (sourceEntry.source as FileLocation).file);
+ }
+
var srcNameId = sourceEntry.identifierName == null
? null
: names.putIfAbsent(sourceEntry.identifierName, () => names.length);
@@ -309,16 +327,30 @@
}
}
return new SingleMapping._(
- fileUrl, urls.keys.toList(), names.keys.toList(), lines);
+ fileUrl,
+ urls.values.map((i) => files[i]).toList(),
+ urls.keys.toList(),
+ names.keys.toList(),
+ lines);
}
SingleMapping.fromJson(Map map, {mapUrl})
: targetUrl = map['file'],
urls = new List<String>.from(map['sources']),
names = new List<String>.from(map['names']),
+ files = new List(map['sources'].length),
sourceRoot = map['sourceRoot'],
lines = <TargetLineEntry>[],
_mapUrl = mapUrl is String ? Uri.parse(mapUrl) : mapUrl {
+ var sourcesContent = map['sourcesContent'] == null
+ ? const []
+ : new List<String>.from(map['sourcesContent']);
+ for (var i = 0; i < urls.length && i < sourcesContent.length; i++) {
+ var source = sourcesContent[i];
+ if (source == null) continue;
+ files[i] = new SourceFile.fromString(source, url: urls[i]);
+ }
+
int line = 0;
int column = 0;
int srcUrlId = 0;
@@ -385,7 +417,10 @@
}
/// Encodes the Mapping mappings as a json map.
- Map toJson() {
+ ///
+ /// If [sourcesContent] is `true`, this includes the source file contents from
+ /// [files] in the map if possible.
+ Map toJson({bool includeSourceContents: false}) {
var buff = new StringBuffer();
var line = 0;
var column = 0;
@@ -431,9 +466,12 @@
'names': names,
'mappings': buff.toString()
};
- if (targetUrl != null) {
- result['file'] = targetUrl;
+ if (targetUrl != null) result['file'] = targetUrl;
+
+ if (includeSourceContents) {
+ result['sourcesContent'] = files.map((file) => file?.getText(0)).toList();
}
+
return result;
}
diff --git a/pubspec.yaml b/pubspec.yaml
index ecf277b..7bf7bcd 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: source_maps
-version: 0.10.5-dev
+version: 0.10.5
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 8c98c81..7896341 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -341,4 +341,55 @@
var mapping = parseJsonExtended(SOURCE_MAP_BUNDLE) as MappingBundle;
expect(mapping.toJson(), equals(SOURCE_MAP_BUNDLE));
});
+
+ group("source files", () {
+ group("from fromEntries()", () {
+ test("are null for non-FileLocations", () {
+ var mapping = new SingleMapping.fromEntries([
+ new Entry(new SourceLocation(10, line: 1, column: 8),
+ outputVar1.start, null)
+ ]);
+ expect(mapping.files, equals([null]));
+ });
+
+ test("use a file location's file", () {
+ var mapping = new SingleMapping.fromEntries(
+ [new Entry(inputVar1.start, outputVar1.start, null)]);
+ expect(mapping.files, equals([input]));
+ });
+ });
+
+ group("from parse()", () {
+ group("are null", () {
+ test("with no sourcesContent field", () {
+ var mapping = parseJson(EXPECTED_MAP) as SingleMapping;
+ expect(mapping.files, equals([null]));
+ });
+
+ test("with null sourcesContent values", () {
+ var map = new Map.from(EXPECTED_MAP);
+ map["sourcesContent"] = [null];
+ var mapping = parseJson(map) as SingleMapping;
+ expect(mapping.files, equals([null]));
+ });
+
+ test("with a too-short sourcesContent", () {
+ var map = new Map.from(EXPECTED_MAP);
+ map["sourcesContent"] = [];
+ var mapping = parseJson(map) as SingleMapping;
+ expect(mapping.files, equals([null]));
+ });
+ });
+
+ test("are parsed from sourcesContent", () {
+ var map = new Map.from(EXPECTED_MAP);
+ map["sourcesContent"] = ["hello, world!"];
+ var mapping = parseJson(map) as SingleMapping;
+
+ var file = mapping.files[0];
+ expect(file.url, equals(Uri.parse("input.dart")));
+ expect(file.getText(0), equals("hello, world!"));
+ });
+ });
+ });
}