/// <summary> /// Removes column information from a source map /// This can significantly reduce the size of source maps /// If there is a tie between mapping entries, the first generated line takes priority /// <returns>A new source map</returns> /// </summary> public static SourceMap Flatten(SourceMap sourceMap) { SourceMap newMap = new SourceMap { File = sourceMap.File, Version = sourceMap.Version, Mappings = sourceMap.Mappings, Sources = sourceMap.Sources == null ? null : new List <string>(sourceMap.Sources), Names = sourceMap.Names == null ? null : new List <string>(sourceMap.Names), ParsedMappings = new List <MappingEntry>() }; HashSet <int> visitedLines = new HashSet <int>(); foreach (MappingEntry mapping in sourceMap.ParsedMappings) { int generatedLine = mapping.GeneratedSourcePosition.ZeroBasedLineNumber; if (!visitedLines.Contains(generatedLine)) { visitedLines.Add(generatedLine); var newMapping = mapping.Clone(); newMapping.GeneratedSourcePosition.ZeroBasedColumnNumber = 0; newMapping.OriginalSourcePosition.ZeroBasedColumnNumber = 0; newMap.ParsedMappings.Add(newMapping); } } return(newMap); }
/// <summary> /// Removes column information from a source map /// This can significantly reduce the size of source maps /// If there is a tie between mapping entries, the first generated line takes priority /// <returns>A new source map</returns> /// </summary> public static SourceMap Flatten(SourceMap sourceMap) { HashSet <int> visitedLines = new HashSet <int>(); List <MappingEntry> parsedMappings = new List <MappingEntry>(sourceMap.ParsedMappings.Count); // assume each line will not have been visited before foreach (MappingEntry mapping in sourceMap.ParsedMappings) { int generatedLine = mapping.GeneratedSourcePosition.ZeroBasedLineNumber; if (visitedLines.Add(generatedLine)) { MappingEntry newMapping = mapping.CloneWithResetColumnNumber(); parsedMappings.Add(newMapping); } } // Free-up any unneeded space. This no-ops if we're already the right size. parsedMappings.Capacity = parsedMappings.Count; SourceMap newMap = new SourceMap( version: sourceMap.Version, file: sourceMap.File, mappings: sourceMap.Mappings, sources: sourceMap.Sources, names: sourceMap.Names, parsedMappings: parsedMappings, sourcesContent: sourceMap.SourcesContent); return(newMap); }
/// <summary> /// Parses a stream representing a source map into a SourceMap object. /// </summary> public SourceMap ParseSourceMap(StreamReader sourceMapStream) { if (sourceMapStream == null) { return(null); } using (JsonTextReader jsonTextReader = new JsonTextReader(sourceMapStream)) { JsonSerializer serializer = new JsonSerializer(); SourceMap result = serializer.Deserialize <SourceMap>(jsonTextReader); // Since SourceMap is immutable we need to allocate a new one and copy over all the information List <MappingEntry> parsedMappings = _mappingsListParser.ParseMappings(result.Mappings, result.Names, result.Sources); // Resize to free unused memory parsedMappings.Capacity = parsedMappings.Count; result = new SourceMap( version: result.Version, file: result.File, mappings: result.Mappings, sources: result.Sources, names: result.Names, parsedMappings: parsedMappings, sourcesContent: result.SourcesContent); sourceMapStream.Close(); return(result); } }
/// <summary> /// Serialize SourceMap object to json string with given serialize settings /// </summary> public string SerializeMapping(SourceMap sourceMap, JsonSerializerSettings jsonSerializerSettings = null) { if (sourceMap == null) { throw new ArgumentNullException(nameof(sourceMap)); } string mappings = null; if (sourceMap.ParsedMappings != null && sourceMap.ParsedMappings.Count > 0) { MappingGenerateState state = new MappingGenerateState(sourceMap.Names, sourceMap.Sources); StringBuilder output = new StringBuilder(); foreach (MappingEntry entry in sourceMap.ParsedMappings) { SerializeMappingEntry(entry, state, output); } output.Append(';'); mappings = output.ToString(); } SourceMap mapToSerialize = new SourceMap( version: sourceMap.Version, file: sourceMap.File, mappings: mappings, sources: sourceMap.Sources, names: sourceMap.Names, parsedMappings: default,
/// <summary> /// Convenience wrapper around SerializeMapping, but returns a base 64 encoded string instead /// </summary> public string GenerateSourceMapInlineComment(SourceMap sourceMap, JsonSerializerSettings jsonSerializerSettings = null) { string mappings = SerializeMapping(sourceMap, jsonSerializerSettings); byte[] bytes = System.Text.Encoding.UTF8.GetBytes(mappings); var encoded = Convert.ToBase64String(bytes); return(@"//# sourceMappingURL=data:application/json;base64," + encoded); }
string ProcessWithSourceMaps(IEnumerable <AssetContent> assets) { var results = assets.Select(a => new { asset = a, result = this.reactEnvironment.ExecuteWithBabel <JavaScriptWithSourceMap>("ReactNET_transform_sourcemap", a.Contents, babelConfig, a.Path) }).ToArray(); var offset = 0; var map = new SourcemapToolkit.SourcemapParser.SourceMap(); var sourceMapParser = new SourceMapParser(); var outputBuilder = new StringBuilder(); foreach (var result in results) { var json = result.result.SourceMap.ToJson(); using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json))) { using (var streamReader = new StreamReader(memoryStream)) { var sourceMap = sourceMapParser.ParseSourceMap(streamReader); foreach (var name in sourceMap.Names) { map.Names.Add(name); } map.Sources.Add(result.asset.Path.ToLower()); foreach (var mappingEntry in sourceMap.ParsedMappings) { if (mappingEntry.OriginalSourcePosition == null) { continue; } map.ParsedMappings.Add(mappingEntry); mappingEntry.OriginalFileName = result.asset.Path.ToLower(); mappingEntry.GeneratedSourcePosition.ZeroBasedLineNumber += offset; } offset += result.result.Code.Split('\n').Length; } } outputBuilder.AppendLine(result.result.Code); } var sourceMapGenerator = new SourceMapGenerator(); var generateSourceMapInlineComment = sourceMapGenerator.GenerateSourceMapInlineComment(map); outputBuilder.AppendLine(generateSourceMapInlineComment); return(outputBuilder.ToString()); }
/// <summary> /// Parses a stream representing a source map into a SourceMap object. /// </summary> public SourceMap ParseSourceMap(StreamReader sourceMapStream) { if (sourceMapStream == null) { return(null); } using (JsonTextReader jsonTextReader = new JsonTextReader(sourceMapStream)) { JsonSerializer serializer = new JsonSerializer(); SourceMap result = serializer.Deserialize <SourceMap>(jsonTextReader); result.ParsedMappings = _mappingsListParser.ParseMappings(result.Mappings, result.Names, result.Sources); sourceMapStream.Close(); return(result); } }
/// <summary> /// Serialize SourceMap object to json string with given serialize settings /// </summary> public string SerializeMapping(SourceMap sourceMap, JsonSerializerSettings jsonSerializerSettings = null) { if (sourceMap == null) { throw new ArgumentNullException(nameof(sourceMap)); } SourceMap mapToSerialize = new SourceMap() { File = sourceMap.File, Names = sourceMap.Names, Sources = sourceMap.Sources, Version = sourceMap.Version, SourcesContent = sourceMap.SourcesContent }; if (sourceMap.ParsedMappings != null && sourceMap.ParsedMappings.Count > 0) { MappingGenerateState state = new MappingGenerateState(sourceMap.Names, sourceMap.Sources); StringBuilder output = new StringBuilder(); foreach (MappingEntry entry in sourceMap.ParsedMappings) { SerializeMappingEntry(entry, state, output); } output.Append(';'); mapToSerialize.Mappings = output.ToString(); } return(JsonConvert.SerializeObject(mapToSerialize, jsonSerializerSettings ?? new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, })); }
/// <summary> /// Applies the mappings of a sub source map to the current source map /// Each mapping to the supplied source file is rewritten using the supplied source map /// This is useful in situations where we have a to b to c, with mappings ba.map and cb.map /// Calling cb.ApplySourceMap(ba) will return mappings from c to a (ca) /// <param name="submap">The submap to apply</param> /// <param name="sourceFile">The filename of the source file. If not specified, submap's File property will be used</param> /// <returns>A new source map</returns> /// </summary> public SourceMap ApplySourceMap(SourceMap submap, string sourceFile = null) { if (submap == null) { throw new ArgumentNullException(nameof(submap)); } if (sourceFile == null) { if (submap.File == null) { throw new Exception("ApplySourceMap expects either the explicit source file to the map, or submap's 'file' property"); } sourceFile = submap.File; } SourceMap newSourceMap = new SourceMap { File = this.File, Version = this.Version, Sources = new List <string>(), Names = new List <string>(), SourcesContent = new List <string>(), ParsedMappings = new List <MappingEntry>() }; // transform mappings in this source map foreach (MappingEntry mappingEntry in this.ParsedMappings) { MappingEntry newMappingEntry = mappingEntry.Clone(); if (mappingEntry.OriginalFileName == sourceFile && mappingEntry.OriginalSourcePosition != null) { MappingEntry correspondingSubMapMappingEntry = submap.GetMappingEntryForGeneratedSourcePosition(mappingEntry.OriginalSourcePosition); if (correspondingSubMapMappingEntry != null) { // Copy the mapping newMappingEntry = new MappingEntry { GeneratedSourcePosition = mappingEntry.GeneratedSourcePosition.Clone(), OriginalSourcePosition = correspondingSubMapMappingEntry.OriginalSourcePosition.Clone(), OriginalName = correspondingSubMapMappingEntry.OriginalName ?? mappingEntry.OriginalName, OriginalFileName = correspondingSubMapMappingEntry.OriginalFileName ?? mappingEntry.OriginalFileName }; } } // Copy into "Sources" and "Names" string originalFileName = newMappingEntry.OriginalFileName; string originalName = newMappingEntry.OriginalName; if (originalFileName != null && !newSourceMap.Sources.Contains(originalFileName)) { newSourceMap.Sources.Add(originalFileName); } if (originalName != null && !newSourceMap.Names.Contains(originalName)) { newSourceMap.Names.Add(originalName); } newSourceMap.ParsedMappings.Add(newMappingEntry); } ; return(newSourceMap); }
/// <summary> /// Applies the mappings of a sub source map to the current source map /// Each mapping to the supplied source file is rewritten using the supplied source map /// This is useful in situations where we have a to b to c, with mappings ba.map and cb.map /// Calling cb.ApplySourceMap(ba) will return mappings from c to a (ca) /// <param name="submap">The submap to apply</param> /// <param name="sourceFile">The filename of the source file. If not specified, submap's File property will be used</param> /// <returns>A new source map</returns> /// </summary> public SourceMap ApplySourceMap(SourceMap submap, string sourceFile = null) { if (submap == null) { throw new ArgumentNullException(nameof(submap)); } if (sourceFile == null) { if (submap.File == null) { throw new Exception($"{nameof(ApplySourceMap)} expects either the explicit source file to the map, or submap's 'file' property"); } sourceFile = submap.File; } HashSet <string> sources = new HashSet <string>(StringComparer.Ordinal); HashSet <string> names = new HashSet <string>(StringComparer.Ordinal); List <MappingEntry> parsedMappings = new List <MappingEntry>(this.ParsedMappings.Count); // transform mappings in this source map foreach (MappingEntry mappingEntry in this.ParsedMappings) { MappingEntry newMappingEntry = mappingEntry; if (mappingEntry.OriginalFileName == sourceFile && mappingEntry.OriginalSourcePosition != SourcePosition.NotFound) { MappingEntry?correspondingSubMapMappingEntry = submap.GetMappingEntryForGeneratedSourcePosition(mappingEntry.OriginalSourcePosition); if (correspondingSubMapMappingEntry != null) { // Copy the mapping newMappingEntry = new MappingEntry( generatedSourcePosition: mappingEntry.GeneratedSourcePosition, originalSourcePosition: correspondingSubMapMappingEntry.Value.OriginalSourcePosition, originalName: correspondingSubMapMappingEntry.Value.OriginalName ?? mappingEntry.OriginalName, originalFileName: correspondingSubMapMappingEntry.Value.OriginalFileName ?? mappingEntry.OriginalFileName); } } // Copy into "Sources" and "Names" string originalFileName = newMappingEntry.OriginalFileName; string originalName = newMappingEntry.OriginalName; if (originalFileName != null) { sources.Add(originalFileName); } if (originalName != null) { names.Add(originalName); } parsedMappings.Add(newMappingEntry); } SourceMap newSourceMap = new SourceMap( version: Version, file: File, mappings: default,