Generate relative paths in JS source maps
Also, add CLI options to manipulate prefixes of path See KT-4078
This commit is contained in:
@@ -29,6 +29,12 @@ public class JSConfigurationKeys {
|
||||
public static final CompilerConfigurationKey<Boolean> SOURCE_MAP =
|
||||
CompilerConfigurationKey.create("generate source map");
|
||||
|
||||
public static final CompilerConfigurationKey<String> SOURCE_MAP_PREFIX =
|
||||
CompilerConfigurationKey.create("prefix to add to paths in source map");
|
||||
|
||||
public static final CompilerConfigurationKey<List<String>> SOURCE_MAP_SOURCE_ROOTS =
|
||||
CompilerConfigurationKey.create("base directories used to calculate relative paths for source map");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> META_INFO =
|
||||
CompilerConfigurationKey.create("generate .meta.js and .kjsm files");
|
||||
|
||||
|
||||
@@ -103,6 +103,16 @@ public class JsConfig {
|
||||
return getConfiguration().getList(JSConfigurationKeys.LIBRARIES);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSourceMapPrefix() {
|
||||
return configuration.get(JSConfigurationKeys.SOURCE_MAP_PREFIX, "");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getSourceMapRoots() {
|
||||
return configuration.get(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, Collections.singletonList("."));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getFriends() {
|
||||
if (getConfiguration().getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return Collections.emptyList();
|
||||
|
||||
@@ -22,11 +22,12 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.*
|
||||
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
|
||||
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.BinaryOperation.Type.*
|
||||
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.UnaryOperation.Type.*
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineStrategy as KotlinInlineStrategy
|
||||
|
||||
class JsAstSerializer {
|
||||
class JsAstSerializer(private val pathResolver: (File) -> String) {
|
||||
private val nameTableBuilder = NameTable.newBuilder()
|
||||
private val stringTableBuilder = StringTable.newBuilder()
|
||||
private val nameMap = mutableMapOf<JsName, Int>()
|
||||
@@ -567,9 +568,9 @@ class JsAstSerializer {
|
||||
if (location != null) {
|
||||
val lastFile = fileStack.peek()
|
||||
val newFile = location.file
|
||||
fileChanged = lastFile != newFile && newFile != null
|
||||
fileChanged = lastFile != newFile
|
||||
if (fileChanged) {
|
||||
fileConsumer(serialize(newFile!!))
|
||||
fileConsumer(serialize(newFile))
|
||||
fileStack.push(location.file)
|
||||
}
|
||||
val locationBuilder = Location.newBuilder()
|
||||
@@ -598,7 +599,7 @@ class JsAstSerializer {
|
||||
val file = element.containingFile
|
||||
val document = file.viewProvider.document!!
|
||||
|
||||
val path = file.viewProvider.virtualFile.path
|
||||
val path = pathResolver(File(file.viewProvider.virtualFile.path))
|
||||
|
||||
val startOffset = element.node.startOffset
|
||||
val startLine = document.getLineNumber(startOffset)
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.js.facade.*
|
||||
import org.jetbrains.kotlin.js.parser.parse
|
||||
import org.jetbrains.kotlin.js.parser.sourcemaps.*
|
||||
import org.jetbrains.kotlin.js.sourceMap.JsSourceGenerationVisitor
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
||||
import org.jetbrains.kotlin.js.test.utils.*
|
||||
import org.jetbrains.kotlin.js.util.TextOutputImpl
|
||||
@@ -409,7 +410,8 @@ abstract class BasicBoxTest(
|
||||
generatedProgram.accept(AmbiguousAstSourcePropagation())
|
||||
|
||||
val output = TextOutputImpl()
|
||||
val sourceMapBuilder = SourceMap3Builder(outputFile, output, SourceMapBuilderConsumer())
|
||||
val pathResolver = SourceFilePathResolver(mutableListOf(File(".")))
|
||||
val sourceMapBuilder = SourceMap3Builder(outputFile, output, "", SourceMapBuilderConsumer(pathResolver))
|
||||
generatedProgram.accept(JsSourceGenerationVisitor(output, sourceMapBuilder))
|
||||
val code = output.toString()
|
||||
val generatedSourceMap = sourceMapBuilder.build()
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.js.inline.JsInliner;
|
||||
import org.jetbrains.kotlin.js.inline.clean.LabeledBlockToDoWhileTransformation;
|
||||
import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt;
|
||||
import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt;
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
|
||||
import org.jetbrains.kotlin.js.translate.general.AstGenerationResult;
|
||||
import org.jetbrains.kotlin.js.translate.general.FileTranslationResult;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
@@ -45,10 +46,13 @@ import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil;
|
||||
import org.jetbrains.kotlin.serialization.js.ast.JsAstSerializer;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError;
|
||||
|
||||
@@ -144,8 +148,18 @@ public final class K2JSTranslator {
|
||||
ExpandIsCallsKt.expandIsCalls(newFragments);
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
|
||||
List<File> sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList());
|
||||
SourceFilePathResolver pathResolver = new SourceFilePathResolver(sourceRoots);
|
||||
|
||||
Map<KtFile, FileTranslationResult> fileMap = new HashMap<>();
|
||||
JsAstSerializer serializer = new JsAstSerializer();
|
||||
JsAstSerializer serializer = new JsAstSerializer(file -> {
|
||||
try {
|
||||
return pathResolver.getPathRelativeToSourceRoots(file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("IO error occurred resolving path to source file", e);
|
||||
}
|
||||
});
|
||||
byte[] metadataHeader = null;
|
||||
boolean serializeFragments = config.getConfiguration().get(JSConfigurationKeys.SERIALIZE_FRAGMENTS, false);
|
||||
for (KtFile file : files) {
|
||||
|
||||
@@ -20,21 +20,40 @@ import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.PairConsumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsLocation;
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SourceMapBuilderConsumer implements PairConsumer<SourceMapBuilder, Object> {
|
||||
@NotNull
|
||||
private final SourceFilePathResolver pathResolver;
|
||||
|
||||
public SourceMapBuilderConsumer(@NotNull SourceFilePathResolver pathResolver) {
|
||||
this.pathResolver = pathResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(SourceMapBuilder builder, Object sourceInfo) {
|
||||
if (sourceInfo instanceof PsiElement) {
|
||||
PsiElement element = (PsiElement) sourceInfo;
|
||||
PsiFile file = element.getContainingFile();
|
||||
PsiFile psiFile = element.getContainingFile();
|
||||
int offset = element.getNode().getStartOffset();
|
||||
Document document = file.getViewProvider().getDocument();
|
||||
Document document = psiFile.getViewProvider().getDocument();
|
||||
assert document != null;
|
||||
int line = document.getLineNumber(offset);
|
||||
int column = offset - document.getLineStartOffset(line);
|
||||
builder.addMapping(file.getViewProvider().getVirtualFile().getPath(), line, column);
|
||||
|
||||
File file = new File(psiFile.getViewProvider().getVirtualFile().getPath());
|
||||
try {
|
||||
builder.addMapping(pathResolver.getPathRelativeToSourceRoots(file), line, column);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("IO error occurred generating source maps", e);
|
||||
}
|
||||
}
|
||||
else if (sourceInfo instanceof JsLocation) {
|
||||
JsLocation location = (JsLocation) sourceInfo;
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsProgram
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.js.sourceMap.JsSourceGenerationVisitor
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilder
|
||||
import org.jetbrains.kotlin.js.translate.general.FileTranslationResult
|
||||
@@ -58,9 +59,14 @@ abstract class TranslationResult protected constructor(val diagnostics: Diagnost
|
||||
fun getOutputFiles(outputFile: File, outputPrefixFile: File?, outputPostfixFile: File?): OutputFileCollection {
|
||||
val output = TextOutputImpl()
|
||||
val sourceMapBuilder =
|
||||
if (config.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP))
|
||||
SourceMap3Builder(outputFile, output, SourceMapBuilderConsumer())
|
||||
else null
|
||||
if (config.configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)) {
|
||||
val sourceRoots = config.sourceMapRoots.map { File(it) }
|
||||
val pathResolver = SourceFilePathResolver(sourceRoots)
|
||||
SourceMap3Builder(outputFile, output, config.sourceMapPrefix, SourceMapBuilderConsumer(pathResolver))
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
|
||||
val code = getCode(output, sourceMapBuilder)
|
||||
val prefix = outputPrefixFile?.readText() ?: ""
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.js.sourceMap;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class SourceFilePathResolver {
|
||||
@NotNull
|
||||
private final Set<File> sourceRoots;
|
||||
|
||||
@NotNull
|
||||
private final Map<File, String> cache = new HashMap<>();
|
||||
|
||||
public SourceFilePathResolver(@NotNull List<File> sourceRoots) {
|
||||
this.sourceRoots = new HashSet<>();
|
||||
for (File sourceRoot : sourceRoots) {
|
||||
this.sourceRoots.add(sourceRoot.getAbsoluteFile());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getPathRelativeToSourceRoots(@NotNull File file) throws IOException {
|
||||
String path = cache.get(file);
|
||||
if (path == null) {
|
||||
path = calculatePathRelativeToSourceRoots(file);
|
||||
cache.put(file, path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private String calculatePathRelativeToSourceRoots(@NotNull File file) throws IOException {
|
||||
List<String> parts = new ArrayList<>();
|
||||
File currentFile = file.getCanonicalFile();
|
||||
|
||||
while (currentFile != null) {
|
||||
if (sourceRoots.contains(currentFile)) {
|
||||
if (parts.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
Collections.reverse(parts);
|
||||
return StringUtil.join(parts, File.separator);
|
||||
}
|
||||
parts.add(currentFile.getName());
|
||||
currentFile = currentFile.getParentFile();
|
||||
}
|
||||
return file.getName();
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ public class SourceMap3Builder implements SourceMapBuilder {
|
||||
private final StringBuilder out = new StringBuilder(8192);
|
||||
private final File generatedFile;
|
||||
private final TextOutput textOutput;
|
||||
private final String pathPrefix;
|
||||
private final PairConsumer<SourceMapBuilder, Object> sourceInfoConsumer;
|
||||
|
||||
private String lastSource;
|
||||
@@ -50,9 +51,11 @@ public class SourceMap3Builder implements SourceMapBuilder {
|
||||
private int previousSourceLine;
|
||||
private int previousSourceColumn;
|
||||
|
||||
public SourceMap3Builder(File generatedFile, TextOutput textOutput, PairConsumer<SourceMapBuilder, Object> sourceInfoConsumer) {
|
||||
public SourceMap3Builder(File generatedFile, TextOutput textOutput, String pathPrefix,
|
||||
PairConsumer<SourceMapBuilder, Object> sourceInfoConsumer) {
|
||||
this.generatedFile = generatedFile;
|
||||
this.textOutput = textOutput;
|
||||
this.pathPrefix = pathPrefix;
|
||||
this.sourceInfoConsumer = sourceInfoConsumer;
|
||||
}
|
||||
|
||||
@@ -83,7 +86,7 @@ public class SourceMap3Builder implements SourceMapBuilder {
|
||||
else {
|
||||
isNotFirst = true;
|
||||
}
|
||||
sb.append('"').append("file://").append(source).append('"');
|
||||
sb.append('"').append(pathPrefix).append(source).append('"');
|
||||
}
|
||||
sb.append(']');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user