| // Copyright (c) 2016, 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. |
| |
| /// Loads a human-readable source map and outputs the source map file for it. |
| |
| library save; |
| |
| import 'dart:convert'; |
| import 'dart:io'; |
| import 'package:source_maps/source_maps.dart'; |
| import '../helpers/lax_json.dart' as lazon; |
| |
| void main(List<String> args) { |
| if (args.isEmpty) { |
| print(''' |
| Usage: save <dir-containing 'out.js.map2'> |
| or: save <human-readable-source-map-file> [<source-map-file>]'''); |
| exit(1); |
| } |
| |
| File humanReadableSourceMapFile; |
| File? sourceMapFile; |
| if (args.length == 1 && Directory(args[0]).existsSync()) { |
| humanReadableSourceMapFile = File('${args[0]}/out.js.map2'); |
| sourceMapFile = File('${args[0]}/out.js.map'); |
| } else { |
| humanReadableSourceMapFile = File(args[0]); |
| if (args.length > 1) { |
| sourceMapFile = File(args[1]); |
| } |
| } |
| |
| String humanReadableSourceMap = humanReadableSourceMapFile.readAsStringSync(); |
| SingleMapping mapping = convertFromHumanReadableSourceMap( |
| humanReadableSourceMap, |
| ); |
| |
| if (sourceMapFile != null) { |
| sourceMapFile.writeAsStringSync(json.encoder.convert(mapping.toJson())); |
| } else { |
| print(new JsonEncoder.withIndent(' ').convert(mapping.toJson())); |
| } |
| } |
| |
| SingleMapping convertFromHumanReadableSourceMap(String json) { |
| Map inputMap = lazon.decode(json); |
| Map urls = inputMap['sources']; |
| List<String?> sources = List.filled(urls.length, null); |
| urls.forEach((dynamic index, dynamic url) { |
| int i = int.parse(index); |
| assert(sources[i] == null); |
| sources[i] = url; |
| }); |
| List lines = inputMap['lines']; |
| Map<String, int> names = <String, int>{}; |
| Map<int, TargetLineEntry> lineEntryMap = {}; |
| |
| for (Map line in lines) { |
| String targetString = line['target']!; |
| String? sourceString = line['source']; |
| String? name = line['name']; |
| int? nameIndex; |
| if (name != null) { |
| nameIndex = names.putIfAbsent(name, () => names.length); |
| } |
| |
| int? columnEnd; |
| int commaPos = targetString.indexOf(','); |
| final lineNo = int.parse(targetString.substring(0, commaPos)) - 1; |
| if (lineNo < 0) { |
| throw ArgumentError('target line must be > 0: $lineNo'); |
| } |
| targetString = targetString.substring(commaPos + 1); |
| int dashPos = targetString.indexOf('-'); |
| final columnStart = int.parse(targetString.substring(0, dashPos)) - 1; |
| if (columnStart < 0) { |
| throw ArgumentError('target column start must be > 0: $columnStart'); |
| } |
| targetString = targetString.substring(dashPos + 1); |
| if (!targetString.isEmpty) { |
| columnEnd = int.parse(targetString) - 1; |
| if (columnEnd < 0) { |
| throw ArgumentError('target column end must be > 0: $columnEnd'); |
| } |
| } |
| |
| int? sourceUrlId; |
| int? sourceLine; |
| int? sourceColumn; |
| if (sourceString != null) { |
| int colonPos = sourceString.indexOf(':'); |
| sourceUrlId = int.parse(sourceString.substring(0, colonPos)); |
| if (sourceUrlId < 0) { |
| throw ArgumentError('source url id end must be > 0: $sourceUrlId'); |
| } else if (sourceUrlId >= sources.length) { |
| throw ArgumentError( |
| 'source url id end must be < ${sources.length}: $sourceUrlId', |
| ); |
| } |
| |
| sourceString = sourceString.substring(colonPos + 1); |
| int commaPos = sourceString.indexOf(','); |
| sourceLine = int.parse(sourceString.substring(0, commaPos)) - 1; |
| if (sourceLine < 0) { |
| throw ArgumentError('source line end must be > 0: $sourceLine'); |
| } |
| sourceString = sourceString.substring(commaPos + 1); |
| sourceColumn = int.parse(sourceString) - 1; |
| if (sourceColumn < 0) { |
| throw ArgumentError('source column must be > 0: $sourceColumn'); |
| } |
| } |
| |
| TargetLineEntry lineEntry = lineEntryMap.putIfAbsent( |
| lineNo, |
| () => TargetLineEntry(lineNo, []), |
| ); |
| lineEntry.entries.add( |
| TargetEntry( |
| columnStart, |
| sourceUrlId, |
| sourceLine, |
| sourceColumn, |
| nameIndex, |
| ), |
| ); |
| if (columnEnd != null) { |
| lineEntry.entries.add(TargetEntry(columnEnd)); |
| } |
| } |
| |
| List<TargetLineEntry> lineEntries = lineEntryMap.values.toList(); |
| |
| var outputMap = <String, dynamic>{ |
| 'version': 3, |
| 'sourceRoot': inputMap['sourceRoot'], |
| 'file': inputMap['file'], |
| 'sources': sources, |
| 'names': names.keys.toList(), |
| 'mappings': '', |
| }; |
| |
| SingleMapping mapping = SingleMapping.fromJson(outputMap); |
| mapping.lines.addAll(lineEntries); |
| return mapping; |
| } |