| // 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. |
| |
| // @dart = 2.7 |
| |
| /// 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 && new Directory(args[0]).existsSync()) { |
| humanReadableSourceMapFile = new File('${args[0]}/out.js.map2'); |
| sourceMapFile = new File('${args[0]}/out.js.map'); |
| } else { |
| humanReadableSourceMapFile = new File(args[0]); |
| if (args.length > 1) { |
| sourceMapFile = new 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 = new List<String>.filled(urls.length, null); |
| urls.forEach((Object index, Object 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 lineNo; |
| int columnStart; |
| int columnEnd; |
| if (targetString != null) { |
| int commaPos = targetString.indexOf(','); |
| lineNo = int.parse(targetString.substring(0, commaPos)) - 1; |
| if (lineNo < 0) { |
| throw new ArgumentError('target line must be > 0: $lineNo'); |
| } |
| targetString = targetString.substring(commaPos + 1); |
| int dashPos = targetString.indexOf('-'); |
| columnStart = int.parse(targetString.substring(0, dashPos)) - 1; |
| if (columnStart < 0) { |
| throw new 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 new 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 new ArgumentError('source url id end must be > 0: $sourceUrlId'); |
| } else if (sourceUrlId >= sources.length) { |
| throw new 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 new ArgumentError('source line end must be > 0: $sourceLine'); |
| } |
| sourceString = sourceString.substring(commaPos + 1); |
| sourceColumn = int.parse(sourceString) - 1; |
| if (sourceColumn < 0) { |
| throw new ArgumentError('source column must be > 0: $sourceColumn'); |
| } |
| } |
| |
| TargetLineEntry lineEntry = |
| lineEntryMap.putIfAbsent(lineNo, () => new TargetLineEntry(lineNo, [])); |
| lineEntry.entries.add(new TargetEntry( |
| columnStart, sourceUrlId, sourceLine, sourceColumn, nameIndex)); |
| if (columnEnd != null) { |
| lineEntry.entries.add(new TargetEntry(columnEnd)); |
| } |
| } |
| |
| List<TargetLineEntry> lineEntries = lineEntryMap.values.toList(); |
| |
| Map outputMap = { |
| 'version': 3, |
| 'sourceRoot': inputMap['sourceRoot'], |
| 'file': inputMap['file'], |
| 'sources': sources, |
| 'names': names.keys.toList(), |
| 'mappings': '', |
| }; |
| |
| SingleMapping mapping = new SingleMapping.fromJson(outputMap); |
| mapping.lines.addAll(lineEntries); |
| return mapping; |
| } |