Use source map remapper in JS inliner

This commit is contained in:
Alexey Andreev
2017-05-23 19:50:38 +03:00
parent 9c4ec902b0
commit bf21cfd6e0
14 changed files with 344 additions and 89 deletions
@@ -22,6 +22,7 @@ import com.intellij.util.Processor
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
import java.io.File
import java.io.IOException
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
object JsLibraryUtils {
@@ -43,49 +44,53 @@ object JsLibraryUtils {
}
}
@JvmStatic fun traverseJsLibraries(libs: List<File>, action: (content: String, path: String) -> Unit) {
@JvmStatic fun traverseJsLibraries(libs: List<File>, action: (JsLibrary) -> Unit) {
libs.forEach { traverseJsLibrary(it, action) }
}
@JvmStatic fun traverseJsLibrary(lib: File, action: (content: String, path: String) -> Unit) {
@JvmStatic fun traverseJsLibrary(lib: File, action: (JsLibrary) -> Unit) {
when {
lib.isDirectory -> traverseDirectory(lib, action)
FileUtil.isJarOrZip(lib) -> traverseArchive(lib, action)
lib.name.endsWith(KotlinJavascriptMetadataUtils.JS_EXT) -> {
lib.runIfFileExists(action)
lib.runIfFileExists(lib.path, action)
val jsFile = lib.withReplacedExtensionOrNull(
KotlinJavascriptMetadataUtils.META_JS_SUFFIX, KotlinJavascriptMetadataUtils.JS_EXT
)
jsFile?.runIfFileExists(action)
jsFile?.runIfFileExists(jsFile.path, action)
}
}
}
private fun File.runIfFileExists(action: (content: String, path: String) -> Unit) {
private fun File.runIfFileExists(relativePath: String, action: (JsLibrary) -> Unit) {
if (isFile) {
action(FileUtil.loadFile(this), "")
action(JsLibrary(readText(), relativePath, correspondingSourceMapFile().contentIfExists()))
}
}
private fun copyJsFilesFromDirectory(dir: File, outputLibraryJsPath: String) {
traverseDirectory(dir) { content, relativePath ->
FileUtil.writeToFile(File(outputLibraryJsPath, relativePath), content)
traverseDirectory(dir) { (content, path) ->
FileUtil.writeToFile(File(outputLibraryJsPath, path), content)
}
}
private fun processDirectory(dir: File, action: (content: String, relativePath: String) -> Unit) {
private fun File.contentIfExists(): String? = if (exists()) readText() else null
private fun File.correspondingSourceMapFile(): File = File(parentFile, name + ".map")
private fun processDirectory(dir: File, action: (JsLibrary) -> Unit) {
FileUtil.processFilesRecursively(dir, Processor<File> { file ->
val relativePath = FileUtil.getRelativePath(dir, file)
?: throw IllegalArgumentException("relativePath should not be null $dir $file")
if (file.isFile && relativePath.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
if (relativePath.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val suggestedRelativePath = getSuggestedPath(relativePath) ?: return@Processor true
action(FileUtil.loadFile(file), suggestedRelativePath)
file.runIfFileExists(suggestedRelativePath, action)
}
true
})
}
private fun traverseDirectory(dir: File, action: (content: String, relativePath: String) -> Unit) {
private fun traverseDirectory(dir: File, action: (JsLibrary) -> Unit) {
try {
processDirectory(dir, action)
}
@@ -95,26 +100,48 @@ object JsLibraryUtils {
}
private fun copyJsFilesFromZip(file: File, outputLibraryJsPath: String) {
traverseArchive(file) { content, relativePath ->
FileUtil.writeToFile(File(outputLibraryJsPath, relativePath), content)
traverseArchive(file) { library ->
FileUtil.writeToFile(File(outputLibraryJsPath, library.path), library.content)
}
}
private fun traverseArchive(file: File, action: (content: String, relativePath: String) -> Unit) {
private fun traverseArchive(file: File, action: (JsLibrary) -> Unit) {
val zipFile = ZipFile(file.path)
try {
val zipEntries = zipFile.entries()
val librariesWithoutSourceMaps = mutableListOf<JsLibrary>()
val possibleMapFiles = mutableMapOf<String, ZipEntry>()
while (zipEntries.hasMoreElements()) {
val entry = zipEntries.nextElement()
val entryName = entry.name
if (!entry.isDirectory && entryName.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val relativePath = getSuggestedPath(entryName) ?: continue
if (!entry.isDirectory) {
if (entryName.endsWith(KotlinJavascriptMetadataUtils.JS_EXT)) {
val relativePath = getSuggestedPath(entryName) ?: continue
val stream = zipFile.getInputStream(entry)
val content = FileUtil.loadTextAndClose(stream)
action(content, relativePath)
val stream = zipFile.getInputStream(entry)
val content = FileUtil.loadTextAndClose(stream)
librariesWithoutSourceMaps += JsLibrary(content, relativePath, null)
}
else if (entryName.endsWith(KotlinJavascriptMetadataUtils.JS_MAP_EXT)) {
possibleMapFiles[entryName.removeSuffix(KotlinJavascriptMetadataUtils.JS_MAP_EXT)] = entry
}
}
}
librariesWithoutSourceMaps
.map {
val zipEntry = possibleMapFiles[it.path]
if (zipEntry != null) {
val stream = zipFile.getInputStream(zipEntry)
val content = FileUtil.loadTextAndClose(stream)
it.copy(sourceMapContent = content)
}
else {
it
}
}
.forEach(action)
}
catch (ex: IOException) {
LOG.error("Could not extract files from archive ${file.name}: ${ex.message}")
@@ -136,3 +163,5 @@ object JsLibraryUtils {
return path
}
}
data class JsLibrary(val content: String, val path: String, val sourceMapContent: String?)
@@ -57,6 +57,7 @@ class JsMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
object KotlinJavascriptMetadataUtils {
const val JS_EXT: String = ".js"
const val META_JS_SUFFIX: String = ".meta.js"
const val JS_MAP_EXT: String = ".js.map"
private val KOTLIN_JAVASCRIPT_METHOD_NAME = "kotlin_module_metadata"
private val KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN = "\\.kotlin_module_metadata\\(".toPattern()
@@ -78,8 +79,8 @@ object KotlinJavascriptMetadataUtils {
fun loadMetadata(file: File): List<KotlinJavascriptMetadata> {
assert(file.exists()) { "Library $file not found" }
val metadataList = arrayListOf<KotlinJavascriptMetadata>()
JsLibraryUtils.traverseJsLibrary(file) { content, _ ->
parseMetadata(content, metadataList)
JsLibraryUtils.traverseJsLibrary(file) { library ->
parseMetadata(library.content, metadataList)
}
return metadataList