OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// Contains the top-level function to parse source maps version 3. | 5 /// Contains the top-level function to parse source maps version 3. |
6 library source_maps.parser; | 6 library source_maps.parser; |
7 | 7 |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 import 'dart:convert'; | 9 import 'dart:convert'; |
10 | 10 |
11 import 'package:path/path.dart' as path; | |
12 import 'package:source_span/source_span.dart'; | 11 import 'package:source_span/source_span.dart'; |
13 | 12 |
14 import 'builder.dart' as builder; | 13 import 'builder.dart' as builder; |
15 import 'src/source_map_span.dart'; | 14 import 'src/source_map_span.dart'; |
16 import 'src/utils.dart'; | 15 import 'src/utils.dart'; |
17 import 'src/vlq.dart'; | 16 import 'src/vlq.dart'; |
18 | 17 |
19 /// Parses a source map directly from a json string. | 18 /// Parses a source map directly from a json string. |
20 /// | 19 /// |
21 /// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of | 20 /// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 } | 177 } |
179 } | 178 } |
180 | 179 |
181 class MappingBundle extends Mapping { | 180 class MappingBundle extends Mapping { |
182 Map<String, SingleMapping> _mappings = {}; | 181 Map<String, SingleMapping> _mappings = {}; |
183 | 182 |
184 MappingBundle.fromJson(List json, {String mapUrl}) { | 183 MappingBundle.fromJson(List json, {String mapUrl}) { |
185 for (var map in json) { | 184 for (var map in json) { |
186 var mapping = parseJson(map, mapUrl: mapUrl) as SingleMapping; | 185 var mapping = parseJson(map, mapUrl: mapUrl) as SingleMapping; |
187 var targetUrl = mapping.targetUrl; | 186 var targetUrl = mapping.targetUrl; |
| 187 // TODO(jacobr): verify that targetUrl is valid uri instead of a windows |
| 188 // path. |
188 _mappings[targetUrl] = mapping; | 189 _mappings[targetUrl] = mapping; |
189 } | 190 } |
190 } | 191 } |
191 | 192 |
192 /// Encodes the Mapping mappings as a json map. | 193 /// Encodes the Mapping mappings as a json map. |
193 List toJson() => _mappings.values.map((v) => v.toJson()).toList(); | 194 List toJson() => _mappings.values.map((v) => v.toJson()).toList(); |
194 | 195 |
195 String toString() { | 196 String toString() { |
196 var buff = new StringBuffer(); | 197 var buff = new StringBuffer(); |
197 for (var map in _mappings.values) { | 198 for (var map in _mappings.values) { |
198 buff.write(map.toString()); | 199 buff.write(map.toString()); |
199 } | 200 } |
200 return buff.toString(); | 201 return buff.toString(); |
201 } | 202 } |
202 | 203 |
203 SourceMapSpan spanFor(int line, int column, | 204 SourceMapSpan spanFor(int line, int column, |
204 {Map<String, SourceFile> files, String uri}) { | 205 {Map<String, SourceFile> files, String uri}) { |
205 if (uri == null) { | 206 if (uri == null) { |
206 throw new ArgumentError.notNull('uri'); | 207 throw new ArgumentError.notNull('uri'); |
207 } | 208 } |
208 if (_mappings.containsKey(uri)) { | 209 |
209 return _mappings[uri].spanFor(line, column, files: files, uri: uri); | 210 // Find the longest suffix of the uri that matches the sourcemap |
210 } | 211 // where the suffix starts after a path segment boundary. |
211 // Fall back to looking up the source map on just the basename. | 212 // We consider ":" and "/" as path segment boundaries so that |
212 var name = path.basename(uri.toString()); | 213 // "package:" uris can be handled with minimal special casing. Having a |
213 if (_mappings.containsKey(name)) { | 214 // few false positive path segment boundaries is not a significant issue |
214 return _mappings[name].spanFor(line, column, files: files, uri: name); | 215 // as we prefer the longest matching prefix. |
| 216 // Using package:path `path.split` to find path segment boundaries would |
| 217 // not generate all of the path segment boundaries we want for "package:" |
| 218 // urls as "package:package_name" would be one path segment when we want |
| 219 // "package" and "package_name" to be sepearate path segments. |
| 220 |
| 221 bool onBoundary = true; |
| 222 var separatorCodeUnits = ['/'.codeUnitAt(0), ':'.codeUnitAt(0)]; |
| 223 for (var i = 0; i < uri.length; ++i) { |
| 224 if (onBoundary) { |
| 225 var candidate = uri.substring(i); |
| 226 if (_mappings.containsKey(candidate)) { |
| 227 return _mappings[candidate] |
| 228 .spanFor(line, column, files: files, uri: candidate); |
| 229 } |
| 230 } |
| 231 onBoundary = separatorCodeUnits.contains(uri.codeUnitAt(i)); |
215 } | 232 } |
216 | 233 |
217 // Note: when there is no source map for an uri, this behaves like an | 234 // Note: when there is no source map for an uri, this behaves like an |
218 // identity function, returning the requested location as the result. | 235 // identity function, returning the requested location as the result. |
219 | 236 |
220 // Create a mock offset for the output location. We compute it in terms | 237 // Create a mock offset for the output location. We compute it in terms |
221 // of the input line and column to minimize the chances that two different | 238 // of the input line and column to minimize the chances that two different |
222 // line and column locations are mapped to the same offset. | 239 // line and column locations are mapped to the same offset. |
223 var offset = line * 1000000 + column; | 240 var offset = line * 1000000 + column; |
224 var location = new SourceLocation(offset, | 241 var location = new SourceLocation(offset, |
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
611 static const _TokenKind EOF = const _TokenKind(isEof: true); | 628 static const _TokenKind EOF = const _TokenKind(isEof: true); |
612 static const _TokenKind VALUE = const _TokenKind(); | 629 static const _TokenKind VALUE = const _TokenKind(); |
613 final bool isNewLine; | 630 final bool isNewLine; |
614 final bool isNewSegment; | 631 final bool isNewSegment; |
615 final bool isEof; | 632 final bool isEof; |
616 bool get isValue => !isNewLine && !isNewSegment && !isEof; | 633 bool get isValue => !isNewLine && !isNewSegment && !isEof; |
617 | 634 |
618 const _TokenKind( | 635 const _TokenKind( |
619 {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); | 636 {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); |
620 } | 637 } |
OLD | NEW |