// 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 args) { if (args.isEmpty) { print(''' Usage: save or: save []'''); 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 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 names = {}; Map 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 lineEntries = lineEntryMap.values.toList(); var outputMap = { '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; }