[JS IR] Append source mapping URL every time on writing JS code

Source mapping URL is not saved in JS module cache anymore,
 because it was a wrong url pointed to the source map from the cache.
 Instead, the URL is appended every time on writing the output JS code.

^KT-56469 Fixed
This commit is contained in:
Alexander Korepanov
2023-02-07 16:46:37 +01:00
committed by Space Team
parent e810142d74
commit c82b4f6fe5
3 changed files with 60 additions and 42 deletions
@@ -96,19 +96,18 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
} }
fun fetchCompiledJsCode(artifact: ModuleArtifact) = artifact.artifactsDir?.let { cacheDir -> fun fetchCompiledJsCode(artifact: ModuleArtifact) = artifact.artifactsDir?.let { cacheDir ->
val jsCodeFilePath = File(cacheDir, CACHED_MODULE_JS).ifExists { absolutePath } val jsCodeFile = File(cacheDir, CACHED_MODULE_JS).ifExists { this }
val sourceMapFilePath = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { absolutePath } val sourceMapFile = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { this }
val tsDefinitionsFilePath = File(cacheDir, CACHED_MODULE_D_TS).ifExists { absolutePath } val tsDefinitionsFile = File(cacheDir, CACHED_MODULE_D_TS).ifExists { this }
jsCodeFilePath?.let { CompilationOutputsCached(it, sourceMapFilePath, tsDefinitionsFilePath) } jsCodeFile?.let { CompilationOutputsCached(it, sourceMapFile, tsDefinitionsFile) }
} }
fun commitCompiledJsCode(artifact: ModuleArtifact, compilationOutputs: CompilationOutputsBuilt): CompilationOutputs = fun commitCompiledJsCode(artifact: ModuleArtifact, compilationOutputs: CompilationOutputsBuilt): CompilationOutputs =
artifact.artifactsDir?.let { cacheDir -> artifact.artifactsDir?.let { cacheDir ->
val jsCodeFile = File(cacheDir, CACHED_MODULE_JS) val jsCodeFile = File(cacheDir, CACHED_MODULE_JS)
val jsMapFile = File(cacheDir, CACHED_MODULE_JS_MAP) val jsMapFile = File(cacheDir, CACHED_MODULE_JS_MAP)
compilationOutputs.writeJsCode(jsCodeFile, jsMapFile)
File(cacheDir, CACHED_MODULE_D_TS).writeIfNotNull(compilationOutputs.tsDefinitions?.raw) File(cacheDir, CACHED_MODULE_D_TS).writeIfNotNull(compilationOutputs.tsDefinitions?.raw)
CompilationOutputsBuiltForCache(jsCodeFile.absolutePath, jsMapFile.absolutePath, compilationOutputs) compilationOutputs.writeJsCodeIntoModuleCache(jsCodeFile, jsMapFile)
} ?: compilationOutputs } ?: compilationOutputs
fun loadProgramHeadersFromCache(): List<CachedModuleInfo> { fun loadProgramHeadersFromCache(): List<CachedModuleInfo> {
@@ -64,23 +64,6 @@ abstract class CompilationOutputs {
get() = resolveSibling("$nameWithoutExtension.d.ts").canonicalFile 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) { private fun File.copyModificationTimeFrom(from: File) {
val mtime = from.lastModified() val mtime = from.lastModified()
if (mtime > 0) { 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( class CompilationOutputsCached(
private val jsCodeFilePath: String, private val jsCodeFile: File,
private val sourceMapFilePath: String?, private val sourceMapFile: File?,
private val tsDefinitionsFilePath: String? private val tsDefinitionsFile: File?
) : CompilationOutputs() { ) : CompilationOutputs() {
override val tsDefinitions: TypeScriptFragment? override val tsDefinitions: TypeScriptFragment?
get() = tsDefinitionsFilePath?.let { TypeScriptFragment(File(it).readText()) } get() = tsDefinitionsFile?.let { TypeScriptFragment(it.readText()) }
override val jsProgram: JsProgram? override val jsProgram: JsProgram?
get() = null get() = null
override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) { 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 { if (jsCodeFile.isUpdateRequired(outputJsFile)) {
File(it).copyToIfModified(outputJsMapFile) 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 thisMtime = lastModified()
val targetMtime = target.lastModified() val targetMtime = target.lastModified()
if (thisMtime <= 0 || targetMtime <= 0 || targetMtime > thisMtime) { return thisMtime <= 0 || targetMtime <= 0 || targetMtime > thisMtime
copyTo(target, true)
copyModificationTimeFrom(target)
}
} }
} }
class CompilationOutputsBuiltForCache( class CompilationOutputsBuiltForCache(
private val jsCodeFilePath: String, private val jsCodeFile: File,
private val sourceMapFilePath: String, private val sourceMapFile: File,
private val outputBuilt: CompilationOutputsBuilt private val outputBuilt: CompilationOutputsBuilt
) : CompilationOutputs() { ) : CompilationOutputs() {
@@ -136,7 +148,7 @@ class CompilationOutputsBuiltForCache(
override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) { override fun writeJsCode(outputJsFile: File, outputJsMapFile: File) {
outputBuilt.writeJsCode(outputJsFile, outputJsMapFile) outputBuilt.writeJsCode(outputJsFile, outputJsMapFile)
File(jsCodeFilePath).copyModificationTimeFrom(outputJsFile) jsCodeFile.copyModificationTimeFrom(outputJsFile)
File(sourceMapFilePath).copyModificationTimeFrom(outputJsMapFile) sourceMapFile.copyModificationTimeFrom(outputJsMapFile)
} }
} }
@@ -65,6 +65,8 @@ abstract class AbstractInvalidationTest(
private val TEST_FILE_IGNORE_PATTERN = Regex("^.*\\..+\\.\\w\\w$") private val TEST_FILE_IGNORE_PATTERN = Regex("^.*\\..+\\.\\w\\w$")
private val JS_MODULE_KIND = ModuleKind.COMMON_JS private val JS_MODULE_KIND = ModuleKind.COMMON_JS
private const val SOURCE_MAPPING_URL_PREFIX = "//# sourceMappingURL="
} }
override fun createEnvironment(): KotlinCoreEnvironment { override fun createEnvironment(): KotlinCoreEnvironment {
@@ -117,6 +119,7 @@ abstract class AbstractInvalidationTest(
copy.put(JSConfigurationKeys.GENERATE_DTS, true) copy.put(JSConfigurationKeys.GENERATE_DTS, true)
copy.put(JSConfigurationKeys.MODULE_KIND, JS_MODULE_KIND) copy.put(JSConfigurationKeys.MODULE_KIND, JS_MODULE_KIND)
copy.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true) copy.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
copy.put(JSConfigurationKeys.SOURCE_MAP, true)
copy.languageVersionSettings = with(LanguageVersionSettingsBuilder()) { copy.languageVersionSettings = with(LanguageVersionSettingsBuilder()) {
language.forEach { language.forEach {
@@ -246,7 +249,6 @@ abstract class AbstractInvalidationTest(
} }
} }
private fun verifyJsCode(stepId: Int, mainModuleName: String, jsFiles: List<String>) { private fun verifyJsCode(stepId: Int, mainModuleName: String, jsFiles: List<String>) {
try { try {
V8IrJsTestChecker.checkWithTestFunctionArgs( V8IrJsTestChecker.checkWithTestFunctionArgs(
@@ -293,11 +295,16 @@ abstract class AbstractInvalidationTest(
) )
} }
private fun writeJsCode(mainModuleName: String, jsOutput: CompilationOutputs): List<String> { private fun writeJsCode(stepId: Int, mainModuleName: String, jsOutput: CompilationOutputs): List<String> {
val compiledJsFiles = jsOutput.writeAll(jsDir, mainModuleName, true, mainModuleName, JS_MODULE_KIND).filter { val compiledJsFiles = jsOutput.writeAll(jsDir, mainModuleName, true, mainModuleName, JS_MODULE_KIND).filter {
it.extension == "js" it.extension == "js"
} }
for (jsCodeFile in compiledJsFiles) { 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}") jsCodeFile.writeAsJsModule(jsCodeFile.readText(), "./${jsCodeFile.name}")
} }
@@ -349,7 +356,7 @@ abstract class AbstractInvalidationTest(
) )
val (jsOutput, rebuiltModules) = jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true) 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) verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, projStep.dirtyJS)
verifyJsCode(projStep.id, mainModuleName, writtenFiles) verifyJsCode(projStep.id, mainModuleName, writtenFiles)