Add support for the sourcesContent field (dart-lang/source_maps#28)
diff --git a/pkgs/source_maps/CHANGELOG.md b/pkgs/source_maps/CHANGELOG.md index a7c1b06..8ef25be 100644 --- a/pkgs/source_maps/CHANGELOG.md +++ b/pkgs/source_maps/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/pkgs/source_maps/lib/parser.dart b/pkgs/source_maps/lib/parser.dart index 3b65e89..e46b783 100644 --- a/pkgs/source_maps/lib/parser.dart +++ b/pkgs/source_maps/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/pkgs/source_maps/pubspec.yaml b/pkgs/source_maps/pubspec.yaml index ecf277b..7bf7bcd 100644 --- a/pkgs/source_maps/pubspec.yaml +++ b/pkgs/source_maps/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/pkgs/source_maps/test/parser_test.dart b/pkgs/source_maps/test/parser_test.dart index 8c98c81..7896341 100644 --- a/pkgs/source_maps/test/parser_test.dart +++ b/pkgs/source_maps/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!")); + }); + }); + }); }