Skip to content

Commit

Permalink
Keep the original source map's sourcesContent from the input source map.
Browse files Browse the repository at this point in the history
This allows the users to see the original file before an external compilation
step, typically keep the TypeScript source before it was translated to JS.

When a closure compiler is run with apply_input_source_maps and
source_map_input_content, it now preserves the original sourcesContent for the
files from the input source maps in the output source map.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=196936917
  • Loading branch information
josef-jelinek authored and blickly committed May 17, 2018
1 parent 37de22f commit 2008247
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 21 deletions.
6 changes: 6 additions & 0 deletions src/com/google/debugging/sourcemap/SourceMapConsumerV3.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public final class SourceMapConsumerV3 implements SourceMapConsumer,
static final int UNMAPPED = -1;

private String[] sources;
private String[] sourcesContent;
private String[] names;
private int lineCount;
// Slots in the lines list will be null if the line does not have any entries.
Expand Down Expand Up @@ -92,6 +93,7 @@ void parse(SourceMapObject sourceMapObject, SourceMapSupplier sectionSupplier)
lineCount = sourceMapObject.getLineCount();
sourceRoot = sourceMapObject.getSourceRoot();
sources = sourceMapObject.getSources();
sourcesContent = sourceMapObject.getSourcesContent();
names = sourceMapObject.getNames();

if (lineCount >= 0) {
Expand Down Expand Up @@ -186,6 +188,10 @@ public Collection<String> getOriginalSources() {
return Arrays.asList(sources);
}

public Collection<String> getOriginalSourcesContent() {
return sourcesContent == null ? null : Arrays.asList(sourcesContent);
}

@Override
public Collection<OriginalMapping> getReverseMapping(String originalFile,
int line, int column) {
Expand Down
16 changes: 9 additions & 7 deletions src/com/google/debugging/sourcemap/SourceMapGeneratorV3.java
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,11 @@ public void mergeMapSection(int line, int column, String mapSectionContents,
* 4. lineCount: 2,
* 5. sourceRoot: "",
* 6. sources: ["foo.js", "bar.js"],
* 7. names: ["src", "maps", "are", "fun"],
* 8. mappings: "a;;abcde,abcd,a;"
* 9. x_org_extension: value
* 10. }
* 7. sourcesContent: ["var foo", "var bar"],
* 8. names: ["src", "maps", "are", "fun"],
* 9. mappings: "a;;abcde,abcd,a;"
* 10. x_org_extension: value
* 11. }
*
* Line 1: The entire file is a single JSON object
* Line 2: File revision (always the first entry in the object)
Expand All @@ -355,10 +356,11 @@ public void mergeMapSection(int line, int column, String mapSectionContents,
* server or removing repeated prefix values in the "sources" entry.
* Line 6: A list of sources used by the "mappings" entry relative to the
* sourceRoot.
* Line 7: A list of symbol names used by the "mapping" entry. This list
* Line 7: An optional list of the full content of the source files.
* Line 8: A list of symbol names used by the "mapping" entry. This list
* may be incomplete.
* Line 8: The mappings field.
* Line 9: Any custom field (extension).
* Line 9: The mappings field.
* Line 10: Any custom field (extension).
*/
@Override
public void appendTo(Appendable out, String name) throws IOException {
Expand Down
24 changes: 23 additions & 1 deletion src/com/google/debugging/sourcemap/SourceMapObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class SourceMapObject {
private final String file;
private final String mappings;
private final String[] sources;
private final String[] sourcesContent;
private final String[] names;
private final List<SourceMapSection> sections;
private final Map<String, Object> extensions;
Expand All @@ -38,6 +39,7 @@ private SourceMapObject(
String file,
String mappings,
String[] sources,
String[] sourcesContent,
String[] names,
List<SourceMapSection> sections,
Map<String, Object> extensions) {
Expand All @@ -47,6 +49,7 @@ private SourceMapObject(
this.file = file;
this.mappings = mappings;
this.sources = sources;
this.sourcesContent = sourcesContent;
this.names = names;
this.sections = sections;
this.extensions = extensions;
Expand Down Expand Up @@ -76,6 +79,10 @@ public String[] getSources() {
return sources;
}

public String[] getSourcesContent() {
return sourcesContent;
}

public String[] getNames() {
return names;
}
Expand All @@ -99,6 +106,7 @@ static class Builder {
private String file;
private String mappings;
private String[] sources;
private String[] sourcesContent;
private String[] names;
private List<SourceMapSection> sections;
private Map<String, Object> extensions;
Expand Down Expand Up @@ -133,6 +141,11 @@ public Builder setSources(String[] sources) {
return this;
}

public Builder setSourcesContent(String[] sourcesContent) {
this.sourcesContent = sourcesContent;
return this;
}

public Builder setNames(String[] names) {
this.names = names;
return this;
Expand All @@ -150,7 +163,16 @@ public Builder setExtensions(Map<String, Object> extensions) {

public SourceMapObject build() {
return new SourceMapObject(
version, lineCount, sourceRoot, file, mappings, sources, names, sections, extensions);
version,
lineCount,
sourceRoot,
file,
mappings,
sources,
sourcesContent,
names,
sections,
extensions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static SourceMapObject parse(String contents) throws SourceMapParseExcept
}

builder.setSources(getJavaStringArray(sourceMapRoot.get("sources")));
builder.setSourcesContent(getJavaStringArray(sourceMapRoot.get("sourcesContent")));
builder.setNames(getJavaStringArray(sourceMapRoot.get("names")));

Map<String, Object> extensions = new LinkedHashMap<>();
Expand Down
40 changes: 39 additions & 1 deletion src/com/google/javascript/jscomp/Compiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -527,6 +528,11 @@ public void initBasedOnOptions() {
sourceMap.setPrefixMappings(options.sourceMapLocationMappings);
if (options.applyInputSourceMaps) {
sourceMap.setSourceFileMapping(this);
if (options.sourceMapIncludeSourcesContent) {
for (SourceMapInput inputSourceMap : inputSourceMaps.values()) {
addSourceMapSourceFiles(inputSourceMap);
}
}
}
}
}
Expand Down Expand Up @@ -2870,6 +2876,34 @@ public CharSequence getSourceFileContentByName(String sourceName) {
@Override
public void addInputSourceMap(String sourceFileName, SourceMapInput inputSourceMap) {
inputSourceMaps.put(sourceFileName, inputSourceMap);
if (options.sourceMapIncludeSourcesContent) {
addSourceMapSourceFiles(inputSourceMap);
}
}

/**
* Adds file name to content mappings for all sources found in a source map.
* This is used to populate sourcesContent array in the output source map
* even for sources embedded in the input source map.
*/
private void addSourceMapSourceFiles(SourceMapInput inputSourceMap) {
SourceMapConsumerV3 consumer = inputSourceMap.getSourceMap(errorManager);
if (consumer == null) {
return;
}
Collection<String> sourcesContent = consumer.getOriginalSourcesContent();
if (sourcesContent == null) {
return;
}
Iterator<String> content = sourcesContent.iterator();
Iterator<String> sources = consumer.getOriginalSources().iterator();
while (sources.hasNext() && content.hasNext()) {
sourceMap.addSourceFile(sources.next(), content.next());
}
if (sources.hasNext() || content.hasNext()) {
throw new RuntimeException(
"Source map's \"sources\" and \"sourcesContent\" lengths do not match.");
}
}

@Override
Expand Down Expand Up @@ -3442,7 +3476,11 @@ ModuleLoader getModuleLoader() {
private void addFilesToSourceMap(Iterable<? extends SourceFile> files) {
if (getOptions().sourceMapIncludeSourcesContent && getSourceMap() != null) {
for (SourceFile file : files) {
getSourceMap().addSourceFile(file);
try {
getSourceMap().addSourceFile(file.getName(), file.getCode());
} catch (IOException e) {
throw new RuntimeException("Cannot read code of a source map's source file.", e);
}
}
}
}
Expand Down
14 changes: 2 additions & 12 deletions src/com/google/javascript/jscomp/SourceMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/**
Expand All @@ -44,10 +42,6 @@
* @author johnlenz@google.com (John Lenz)
*/
public final class SourceMap {

private static final Logger logger =
Logger.getLogger("com.google.javascript.jscomp");

/**
* An enumeration of available source map formats
*/
Expand Down Expand Up @@ -202,12 +196,8 @@ public void addMapping(
outputStartPosition, outputEndPosition);
}

public void addSourceFile(SourceFile sourceFile) {
try {
generator.addSourcesContent(fixupSourceLocation(sourceFile.getName()), sourceFile.getCode());
} catch (IOException e) {
logger.log(Level.WARNING, "Exception while adding source content to source map.", e);
}
public void addSourceFile(String name, String code) {
generator.addSourcesContent(fixupSourceLocation(name), code);
}

/**
Expand Down
54 changes: 54 additions & 0 deletions test/com/google/javascript/jscomp/CompilerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,37 @@ public void testInputSourceMapInline() throws Exception {
SourceMapInput inputSourceMap = compiler.inputSourceMaps.get("tmp");
SourceMapConsumerV3 sourceMap = inputSourceMap.getSourceMap(null);
assertThat(sourceMap.getOriginalSources()).containsExactly("foo.ts");
assertNull(sourceMap.getOriginalSourcesContent());
}

private static final String SOURCE_MAP_TEST_CONTENT =
Joiner.on("\n")
.join(
"var A = (function () {",
" function A(input) {",
" this.a = input;",
" }",
" return A;",
"}());",
"console.log(new A(1));");

// Similar to BASE64_ENCODED_SOURCE_MAP; contains encoded SOURCE_MAP but with
// SOURCE_MAP_TEST_CONTENT as the only item of sourcesContent corresponding
// to "foo.ts" sources item.
private static final String BASE64_ENCODED_SOURCE_MAP_WITH_CONTENT =
"data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9vLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZm9vLnRzIl0sInNvdXJjZXNDb250ZW50IjpbInZhciBBID0gKGZ1bmN0aW9uICgpIHtcbiAgICBmdW5jdGlvbiBBKGlucHV0KSB7XG4gICAgICAgIHRoaXMuYSA9IGlucHV0O1xuICAgIH1cbiAgICByZXR1cm4gQTtcbn0oKSk7XG5jb25zb2xlLmxvZyhuZXcgQSgxKSk7Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0lBR0UsV0FBWSxLQUFhO1FBQ3ZCLElBQUksQ0FBQyxDQUFDLEdBQUcsS0FBSyxDQUFDO0lBQ2pCLENBQUM7SUFDSCxRQUFDO0FBQUQsQ0FBQyxBQU5ELElBTUM7QUFFRCxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMifQ==";

public void testInputSourceMapInlineContent() throws Exception {
Compiler compiler = new Compiler();
compiler.initCompilerOptionsIfTesting();
String code =
SOURCE_MAP_TEST_CODE + "\n//# sourceMappingURL=" + BASE64_ENCODED_SOURCE_MAP_WITH_CONTENT;
CompilerInput input = new CompilerInput(SourceFile.fromCode("tmp", code));
input.getAstRoot(compiler);
SourceMapInput inputSourceMap = compiler.inputSourceMaps.get("tmp");
SourceMapConsumerV3 sourceMap = inputSourceMap.getSourceMap(null);
assertThat(sourceMap.getOriginalSources()).containsExactly("foo.ts");
assertThat(sourceMap.getOriginalSourcesContent()).containsExactly(SOURCE_MAP_TEST_CONTENT);
}

public void testResolveRelativeSourceMap() throws Exception {
Expand Down Expand Up @@ -326,8 +357,31 @@ public void testApplyInputSourceMaps() throws Exception {
assertThat(mapping.getLineNumber()).isEqualTo(18);
assertThat(mapping.getColumnPosition()).isEqualTo(26);
assertThat(mapping.getIdentifier()).isEqualTo("testSymbolName");
assertThat(consumer.getOriginalSources()).containsExactly("input.js", "input.ts");
assertNull(consumer.getOriginalSourcesContent());
}

public void testKeepInputSourceMapsSourcesContent() throws Exception {
CompilerOptions options = new CompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT3);
options.sourceMapOutputPath = "fake/source_map_path.js.map";
options.applyInputSourceMaps = true;
options.sourceMapIncludeSourcesContent = true;
String code = SOURCE_MAP_TEST_CODE + "\n//# sourceMappingURL="
+ BASE64_ENCODED_SOURCE_MAP_WITH_CONTENT;
Compiler compiler = new Compiler();
SourceFile sourceFile = SourceFile.fromCode("input.js", code);
compiler.compile(EMPTY_EXTERNS.get(0), sourceFile, options);
assertThat(compiler.toSource()).isEqualTo(
"var X=function(){function X(input){this.y=input}return X}();console.log(new X(1));");
SourceMap sourceMap = compiler.getSourceMap();
StringWriter out = new StringWriter();
sourceMap.appendTo(out, "source.js.map");
SourceMapConsumerV3 consumer = new SourceMapConsumerV3();
consumer.parse(out.toString());
assertThat(consumer.getOriginalSources()).containsExactly("foo.ts");
assertThat(consumer.getOriginalSourcesContent()).containsExactly(SOURCE_MAP_TEST_CONTENT);
}

private static final ImmutableList<SourceFile> EMPTY_EXTERNS =
ImmutableList.of(SourceFile.fromCode("externs", ""));
Expand Down

0 comments on commit 2008247

Please sign in to comment.