Generate relative paths in JS source maps

Also, add CLI options to manipulate prefixes of path

See KT-4078
This commit is contained in:
Alexey Andreev
2017-06-09 14:52:04 +03:00
parent 44d3b8fb1a
commit 13ab63ae09
39 changed files with 399 additions and 18 deletions
@@ -41,6 +41,18 @@ public class K2JSCompilerArguments extends CommonCompilerArguments {
@Argument(value = "-source-map", description = "Generate source map")
public boolean sourceMap;
@GradleOption(DefaultValues.StringNullDefault.class)
@Argument(value = "-source-map-prefix", description = "Prefix for paths in a source map")
public String sourceMapPrefix;
@GradleOption(DefaultValues.StringNullDefault.class)
@Argument(
value = "-source-map-source-roots",
valueDescription = "<path>",
description = "Base directories which are used to calculate relative paths to source files in source map"
)
public String sourceMapSourceRoots;
@GradleOption(DefaultValues.BooleanTrueDefault.class)
@Argument(value = "-meta-info", description = "Generate .meta.js and .kjsm files with metadata. Use to create a library")
public boolean metaInfo;
@@ -19,7 +19,9 @@ package org.jetbrains.kotlin.cli.js;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
@@ -34,6 +36,7 @@ import org.jetbrains.kotlin.cli.common.ExitCode;
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants;
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity;
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsKt;
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
@@ -58,8 +61,8 @@ import org.jetbrains.kotlin.utils.PathUtil;
import org.jetbrains.kotlin.utils.StringsKt;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import java.util.*;
import static org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR;
import static org.jetbrains.kotlin.cli.common.ExitCode.OK;
@@ -261,6 +264,23 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
if (arguments.sourceMap) {
configuration.put(JSConfigurationKeys.SOURCE_MAP, true);
if (arguments.sourceMapPrefix != null) {
configuration.put(JSConfigurationKeys.SOURCE_MAP_PREFIX, arguments.sourceMapPrefix);
}
String sourceMapSourceRoots = arguments.sourceMapSourceRoots != null ?
arguments.sourceMapSourceRoots :
calculateSourceMapSourceRoot(messageCollector, arguments);
List<String> sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator);
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList);
}
else {
if (arguments.sourceMapPrefix != null) {
messageCollector.report(WARNING, "source-map-prefix argument has no effect without source map", null);
}
if (arguments.sourceMapSourceRoots != null) {
messageCollector.report(WARNING, "source-map-source-root argument has no effect without source map", null);
}
}
if (arguments.metaInfo) {
configuration.put(JSConfigurationKeys.META_INFO, true);
@@ -299,6 +319,58 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
configuration.put(JSConfigurationKeys.MODULE_KIND, moduleKind);
}
@NotNull
private static String calculateSourceMapSourceRoot(
@NotNull MessageCollector messageCollector,
@NotNull K2JSCompilerArguments arguments
) {
File commonPath = null;
List<File> pathToRoot = new ArrayList<>();
Map<File, Integer> pathToRootIndexes = new HashMap<>();
try {
for (String path : arguments.freeArgs) {
File file = new File(path).getCanonicalFile();
if (commonPath == null) {
commonPath = file;
while (file != null) {
pathToRoot.add(file);
file = file.getParentFile();
}
Collections.reverse(pathToRoot);
for (int i = 0; i < pathToRoot.size(); ++i) {
pathToRootIndexes.put(pathToRoot.get(i), i);
}
}
else {
while (file != null) {
Integer existingIndex = pathToRootIndexes.get(file);
if (existingIndex != null) {
existingIndex = Math.min(existingIndex, pathToRoot.size() - 1);
pathToRoot.subList(existingIndex + 1, pathToRoot.size()).clear();
commonPath = pathToRoot.get(pathToRoot.size() - 1);
break;
}
file = file.getParentFile();
}
if (file == null) {
break;
}
}
}
}
catch (IOException e) {
String text = ExceptionUtil.getThrowableText(e);
messageCollector.report(CompilerMessageSeverity.ERROR, "IO error occurred calculating source root:\n" + text, null);
return ".";
}
return commonPath != null ? commonPath.getPath() : ".";
}
private static MainCallParameters createMainCallParameters(String main) {
if (K2JsArgumentConstants.NO_CALL.equals(main)) {
return MainCallParameters.noCall();
+3
View File
@@ -4,6 +4,9 @@ where possible options include:
-no-stdlib Don't use bundled Kotlin stdlib
-libraries <path> Paths to Kotlin libraries with .meta.js and .kjsm files, separated by system path separator
-source-map Generate source map
-source-map-prefix Prefix for paths in a source map
-source-map-source-roots <path>
Base directories which are used to calculate relative paths to source files in source map
-meta-info Generate .meta.js and .kjsm files with metadata. Use to create a library
-target { v5 } Generate JS files for specific ECMA version
-module-kind { plain, amd, commonjs, umd }
+5
View File
@@ -0,0 +1,5 @@
$TESTDATA_DIR$/sourceMap.kt
-no-stdlib
-source-map
-output
$TEMP_DIR$/out.js
+5
View File
@@ -0,0 +1,5 @@
var log = ""
fun foo(x: String) {
log += "$x;"
}
+1
View File
@@ -0,0 +1 @@
OK
+2
View File
@@ -0,0 +1,2 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "sourceMap.kt"
+7
View File
@@ -0,0 +1,7 @@
$TESTDATA_DIR$/sourceMap.kt
-no-stdlib
-source-map
-source-map-prefix
http://localhost:8080/src/
-output
$TEMP_DIR$/out.js
+1
View File
@@ -0,0 +1 @@
OK
+2
View File
@@ -0,0 +1,2 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "http://localhost:8080/src/sourceMap.kt"
@@ -0,0 +1,3 @@
fun h(): Long {
return 42L
}
+3
View File
@@ -0,0 +1,3 @@
fun g(): Int {
return 23
}
@@ -0,0 +1,3 @@
fun j(): Float {
return 3.14159f
}
+3
View File
@@ -0,0 +1,3 @@
fun f(): String {
return "f()"
}
+6
View File
@@ -0,0 +1,6 @@
$TESTDATA_DIR$/sourceMapRoot/foo/file1.kt
$TESTDATA_DIR$/sourceMapRoot/bar/file2.kt
-no-stdlib
-source-map
-output
$TEMP_DIR$/out.js
+1
View File
@@ -0,0 +1 @@
OK
+3
View File
@@ -0,0 +1,3 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "foo/file1.kt"
// CONTAINS: out.js.map, "bar/file2.kt"
+8
View File
@@ -0,0 +1,8 @@
$TESTDATA_DIR$/sourceMapRoot/foo/file1.kt
$TESTDATA_DIR$/sourceMapRoot/bar/file2.kt
-no-stdlib
-source-map
-source-map-source-roots
$TESTDATA_DIR$/
-output
$TEMP_DIR$/out.js
+1
View File
@@ -0,0 +1 @@
OK
+3
View File
@@ -0,0 +1,3 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "sourceMapRoot/foo/file1.kt"
// CONTAINS: out.js.map, "sourceMapRoot/bar/file2.kt"
+10
View File
@@ -0,0 +1,10 @@
$TESTDATA_DIR$/sourceMapRoot/foo/file1.kt
$TESTDATA_DIR$/sourceMapRoot/bar/file2.kt
$TESTDATA_DIR$/sourceMapRoot/bar/baz/file3.kt
$TESTDATA_DIR$/sourceMapRoot/foo/baz/file4.kt
-no-stdlib
-source-map
-source-map-source-roots
$TESTDATA_DIR$/sourceMapRoot/foo
-output
$TEMP_DIR$/out.js
+1
View File
@@ -0,0 +1 @@
OK
+5
View File
@@ -0,0 +1,5 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "file1.kt"
// CONTAINS: out.js.map, "baz/file4.kt"
// CONTAINS: out.js.map, "file2.kt"
// CONTAINS: out.js.map, "file3.kt"
+8
View File
@@ -0,0 +1,8 @@
$TESTDATA_DIR$/sourceMapRoot/foo/file1.kt
$TESTDATA_DIR$/sourceMapRoot/bar/file2.kt
-no-stdlib
-source-map
-source-map-source-roots
$TESTDATA_DIR$/foo:$TESTDATA_DIR$/bar
-output
$TEMP_DIR$/out.js
+1
View File
@@ -0,0 +1 @@
OK
+3
View File
@@ -0,0 +1,3 @@
// EXISTS: out.js
// CONTAINS: out.js.map, "file1.kt"
// CONTAINS: out.js.map, "file2.kt"
@@ -24,7 +24,6 @@ import kotlin.collections.CollectionsKt;
import kotlin.io.FilesKt;
import kotlin.text.Charsets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cli.common.CLICompiler;
import org.jetbrains.kotlin.cli.common.CLITool;
import org.jetbrains.kotlin.cli.common.ExitCode;
import org.jetbrains.kotlin.cli.js.K2JSCompiler;
@@ -120,6 +119,26 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
}
}
List<String> containsTextList = InTextDirectivesUtils.findLinesWithPrefixesRemoved(content, "// CONTAINS: ");
for (String containsSpec : containsTextList) {
String[] parts = containsSpec.split(",", 2);
String fileName = parts[0].trim();
String contentToSearch = parts[1].trim();
File file = new File(tmpdir, fileName);
if (!file.exists()) {
diagnostics.add("File does not exist: " + fileName);
}
else if (file.isDirectory()) {
diagnostics.add("File is a directory: " + fileName);
}
else {
String text = FilesKt.readText(file, Charsets.UTF_8);
if (!text.contains(contentToSearch)) {
diagnostics.add("File " + fileName + " does not contain string: " + contentToSearch);
}
}
}
if (!diagnostics.isEmpty()) {
diagnostics.add(0, diagnostics.size() + " problem(s) found:");
Assert.fail(StringsKt.join(diagnostics, "\n"));
@@ -127,7 +146,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
}
@NotNull
static List<String> readArgs(@NotNull String argsFilePath, @NotNull String tempDir) throws IOException {
private static List<String> readArgs(@NotNull String argsFilePath, @NotNull String tempDir) throws IOException {
List<String> lines = FilesKt.readLines(new File(argsFilePath), Charsets.UTF_8);
return CollectionsKt.mapNotNull(lines, arg -> {
@@ -515,6 +515,42 @@ public class CliTestGenerated extends AbstractCliTest {
doJsTest(fileName);
}
@TestMetadata("sourceMap.args")
public void testSourceMap() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMap.args");
doJsTest(fileName);
}
@TestMetadata("sourceMapPrefix.args")
public void testSourceMapPrefix() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapPrefix.args");
doJsTest(fileName);
}
@TestMetadata("sourceMapRootAuto.args")
public void testSourceMapRootAuto() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapRootAuto.args");
doJsTest(fileName);
}
@TestMetadata("sourceMapRootManual.args")
public void testSourceMapRootManual() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapRootManual.args");
doJsTest(fileName);
}
@TestMetadata("sourceMapRootMissing.args")
public void testSourceMapRootMissing() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapRootMissing.args");
doJsTest(fileName);
}
@TestMetadata("sourceMapRootMultiple.args")
public void testSourceMapRootMultiple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/sourceMapRootMultiple.args");
doJsTest(fileName);
}
@TestMetadata("suppressAllWarningsJS.args")
public void testSuppressAllWarningsJS() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cli/js/suppressAllWarningsJS.args");
@@ -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(']');
}
@@ -48,6 +48,18 @@ interface KotlinJsOptions : org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
*/
var sourceMap: kotlin.Boolean
/**
* Prefix for paths in a source map
* Default value: null
*/
var sourceMapPrefix: kotlin.String?
/**
* Base directories which are used to calculate relative paths to source files in source map
* Default value: null
*/
var sourceMapSourceRoots: kotlin.String?
/**
* Generate JS files for specific ECMA version
* Possible values: "v5"
@@ -59,6 +59,16 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko
get() = sourceMapField ?: false
set(value) { sourceMapField = value }
private var sourceMapPrefixField: kotlin.String?? = null
override var sourceMapPrefix: kotlin.String?
get() = sourceMapPrefixField ?: null
set(value) { sourceMapPrefixField = value }
private var sourceMapSourceRootsField: kotlin.String?? = null
override var sourceMapSourceRoots: kotlin.String?
get() = sourceMapSourceRootsField ?: null
set(value) { sourceMapSourceRootsField = value }
private var targetField: kotlin.String? = null
override var target: kotlin.String
get() = targetField ?: "v5"
@@ -81,6 +91,8 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko
noStdlibField?.let { args.noStdlib = it }
outputFileField?.let { args.outputFile = it }
sourceMapField?.let { args.sourceMap = it }
sourceMapPrefixField?.let { args.sourceMapPrefix = it }
sourceMapSourceRootsField?.let { args.sourceMapSourceRoots = it }
targetField?.let { args.target = it }
typedArraysField?.let { args.typedArrays = it }
}
@@ -98,6 +110,8 @@ internal fun org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments.fil
noStdlib = true
outputFile = null
sourceMap = false
sourceMapPrefix = null
sourceMapSourceRoots = null
target = "v5"
typedArrays = false
}