diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt index d32c2caae8d..2d6ac9e8b03 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt @@ -96,19 +96,18 @@ class JsMultiModuleCache(private val moduleArtifacts: List) { } fun fetchCompiledJsCode(artifact: ModuleArtifact) = artifact.artifactsDir?.let { cacheDir -> - val jsCodeFilePath = File(cacheDir, CACHED_MODULE_JS).ifExists { absolutePath } - val sourceMapFilePath = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { absolutePath } - val tsDefinitionsFilePath = File(cacheDir, CACHED_MODULE_D_TS).ifExists { absolutePath } - jsCodeFilePath?.let { CompilationOutputsCached(it, sourceMapFilePath, tsDefinitionsFilePath) } + val jsCodeFile = File(cacheDir, CACHED_MODULE_JS).ifExists { this } + val sourceMapFile = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { this } + val tsDefinitionsFile = File(cacheDir, CACHED_MODULE_D_TS).ifExists { this } + jsCodeFile?.let { CompilationOutputsCached(it, sourceMapFile, tsDefinitionsFile) } } fun commitCompiledJsCode(artifact: ModuleArtifact, compilationOutputs: CompilationOutputsBuilt): CompilationOutputs = artifact.artifactsDir?.let { cacheDir -> val jsCodeFile = File(cacheDir, CACHED_MODULE_JS) val jsMapFile = File(cacheDir, CACHED_MODULE_JS_MAP) - compilationOutputs.writeJsCode(jsCodeFile, jsMapFile) File(cacheDir, CACHED_MODULE_D_TS).writeIfNotNull(compilationOutputs.tsDefinitions?.raw) - CompilationOutputsBuiltForCache(jsCodeFile.absolutePath, jsMapFile.absolutePath, compilationOutputs) + compilationOutputs.writeJsCodeIntoModuleCache(jsCodeFile, jsMapFile) } ?: compilationOutputs fun loadProgramHeadersFromCache(): List { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/CompilationOutputs.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/CompilationOutputs.kt index e51f4ffb0e2..db2d327dfe9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/CompilationOutputs.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/CompilationOutputs.kt @@ -64,23 +64,6 @@ abstract class CompilationOutputs { get() = resolveSibling("$nameWithoutExtension.d.ts").canonicalFile } -class CompilationOutputsBuilt( - private val rawJsCode: String, - private val sourceMap: String?, - override val tsDefinitions: TypeScriptFragment?, - override val jsProgram: JsProgram?, -) : CompilationOutputs() { - override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) { - var jsCodeWithSourceMap = rawJsCode - - sourceMap?.let { - outputJsMapFile.writeText(it) - jsCodeWithSourceMap = "$jsCodeWithSourceMap\n//# sourceMappingURL=${outputJsMapFile.name}\n" - } - outputJsFile.writeText(jsCodeWithSourceMap) - } -} - private fun File.copyModificationTimeFrom(from: File) { val mtime = from.lastModified() if (mtime > 0) { @@ -88,38 +71,67 @@ private fun File.copyModificationTimeFrom(from: File) { } } +private fun File.asSourceMappingUrl(): String { + return "\n//# sourceMappingURL=${name}\n" +} + +class CompilationOutputsBuilt( + private val rawJsCode: String, + private val sourceMap: String?, + override val tsDefinitions: TypeScriptFragment?, + override val jsProgram: JsProgram?, +) : CompilationOutputs() { + override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) { + val sourceMappingUrl = sourceMap?.let { + outputJsMapFile.writeText(it) + outputJsMapFile.asSourceMappingUrl() + } ?: "" + outputJsFile.writeText(rawJsCode + sourceMappingUrl) + } + + fun writeJsCodeIntoModuleCache(outputJsFile: File, outputJsMapFile: File): CompilationOutputsBuiltForCache { + sourceMap?.let { outputJsMapFile.writeText(it) } + outputJsFile.writeText(rawJsCode) + return CompilationOutputsBuiltForCache(outputJsFile, outputJsMapFile, this) + } +} + class CompilationOutputsCached( - private val jsCodeFilePath: String, - private val sourceMapFilePath: String?, - private val tsDefinitionsFilePath: String? + private val jsCodeFile: File, + private val sourceMapFile: File?, + private val tsDefinitionsFile: File? ) : CompilationOutputs() { override val tsDefinitions: TypeScriptFragment? - get() = tsDefinitionsFilePath?.let { TypeScriptFragment(File(it).readText()) } + get() = tsDefinitionsFile?.let { TypeScriptFragment(it.readText()) } override val jsProgram: JsProgram? get() = null override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) { - File(jsCodeFilePath).copyToIfModified(outputJsFile) + val sourceMappingUrl = sourceMapFile?.let { + if (it.isUpdateRequired(outputJsMapFile)) { + it.copyTo(outputJsMapFile, true) + it.copyModificationTimeFrom(outputJsMapFile) + } + outputJsMapFile.asSourceMappingUrl() + } ?: "" - sourceMapFilePath?.let { - File(it).copyToIfModified(outputJsMapFile) + if (jsCodeFile.isUpdateRequired(outputJsFile)) { + outputJsFile.writeText(jsCodeFile.readText() + sourceMappingUrl) + jsCodeFile.copyModificationTimeFrom(outputJsFile) } } - private fun File.copyToIfModified(target: File) { + private fun File.isUpdateRequired(target: File): Boolean { val thisMtime = lastModified() val targetMtime = target.lastModified() - if (thisMtime <= 0 || targetMtime <= 0 || targetMtime > thisMtime) { - copyTo(target, true) - copyModificationTimeFrom(target) - } + return thisMtime <= 0 || targetMtime <= 0 || targetMtime > thisMtime } } class CompilationOutputsBuiltForCache( - private val jsCodeFilePath: String, - private val sourceMapFilePath: String, + private val jsCodeFile: File, + private val sourceMapFile: File, private val outputBuilt: CompilationOutputsBuilt ) : CompilationOutputs() { @@ -136,7 +148,7 @@ class CompilationOutputsBuiltForCache( override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) { outputBuilt.writeJsCode(outputJsFile, outputJsMapFile) - File(jsCodeFilePath).copyModificationTimeFrom(outputJsFile) - File(sourceMapFilePath).copyModificationTimeFrom(outputJsMapFile) + jsCodeFile.copyModificationTimeFrom(outputJsFile) + sourceMapFile.copyModificationTimeFrom(outputJsMapFile) } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt b/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt index 6cd92a83108..7f7f8804199 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/incremental/AbstractInvalidationTest.kt @@ -65,6 +65,8 @@ abstract class AbstractInvalidationTest( private val TEST_FILE_IGNORE_PATTERN = Regex("^.*\\..+\\.\\w\\w$") private val JS_MODULE_KIND = ModuleKind.COMMON_JS + + private const val SOURCE_MAPPING_URL_PREFIX = "//# sourceMappingURL=" } override fun createEnvironment(): KotlinCoreEnvironment { @@ -117,6 +119,7 @@ abstract class AbstractInvalidationTest( copy.put(JSConfigurationKeys.GENERATE_DTS, true) copy.put(JSConfigurationKeys.MODULE_KIND, JS_MODULE_KIND) copy.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true) + copy.put(JSConfigurationKeys.SOURCE_MAP, true) copy.languageVersionSettings = with(LanguageVersionSettingsBuilder()) { language.forEach { @@ -246,7 +249,6 @@ abstract class AbstractInvalidationTest( } } - private fun verifyJsCode(stepId: Int, mainModuleName: String, jsFiles: List) { try { V8IrJsTestChecker.checkWithTestFunctionArgs( @@ -293,11 +295,16 @@ abstract class AbstractInvalidationTest( ) } - private fun writeJsCode(mainModuleName: String, jsOutput: CompilationOutputs): List { + private fun writeJsCode(stepId: Int, mainModuleName: String, jsOutput: CompilationOutputs): List { val compiledJsFiles = jsOutput.writeAll(jsDir, mainModuleName, true, mainModuleName, JS_MODULE_KIND).filter { it.extension == "js" } for (jsCodeFile in compiledJsFiles) { + val sourceMappingUrlLine = jsCodeFile.readLines().singleOrNull { it.startsWith(SOURCE_MAPPING_URL_PREFIX) } + JUnit4Assertions.assertEquals("$SOURCE_MAPPING_URL_PREFIX${jsCodeFile.name}.map", sourceMappingUrlLine) { + "Mismatched source map url at step $stepId" + } + jsCodeFile.writeAsJsModule(jsCodeFile.readText(), "./${jsCodeFile.name}") } @@ -349,7 +356,7 @@ abstract class AbstractInvalidationTest( ) val (jsOutput, rebuiltModules) = jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true) - val writtenFiles = writeJsCode(mainModuleName, jsOutput) + val writtenFiles = writeJsCode(projStep.id, mainModuleName, jsOutput) verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, projStep.dirtyJS) verifyJsCode(projStep.id, mainModuleName, writtenFiles)