|
|
|
@@ -24,28 +24,45 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|
|
|
|
import org.jetbrains.kotlin.config.Services
|
|
|
|
|
import org.jetbrains.kotlin.js.dce.*
|
|
|
|
|
import java.io.File
|
|
|
|
|
import java.util.zip.ZipFile
|
|
|
|
|
|
|
|
|
|
class K2JSDce : CLITool<K2JSDceArguments>() {
|
|
|
|
|
override fun createArguments(): K2JSDceArguments = K2JSDceArguments()
|
|
|
|
|
|
|
|
|
|
override fun execImpl(messageCollector: MessageCollector, services: Services, arguments: K2JSDceArguments): ExitCode {
|
|
|
|
|
val baseDir = File(arguments.outputDirectory ?: "min")
|
|
|
|
|
val files = arguments.freeArgs.map { arg ->
|
|
|
|
|
val parts = arg.split(File.pathSeparator, ignoreCase = false, limit = 2)
|
|
|
|
|
val inputName = parts[0]
|
|
|
|
|
val moduleName = parts.getOrNull(1) ?: ""
|
|
|
|
|
val resolvedModuleName = if (!moduleName.isEmpty()) moduleName else File(inputName).nameWithoutExtension
|
|
|
|
|
val pathToSourceMapCandidate = "$inputName.map"
|
|
|
|
|
val pathToSourceMap = if (File(pathToSourceMapCandidate).exists()) pathToSourceMapCandidate else null
|
|
|
|
|
InputFile(inputName, pathToSourceMap, File(baseDir, resolvedModuleName + ".js").absolutePath, resolvedModuleName)
|
|
|
|
|
var hasErrors = false
|
|
|
|
|
val files = arguments.freeArgs.flatMap { arg ->
|
|
|
|
|
val files = collectInputFiles(baseDir, arg, messageCollector)
|
|
|
|
|
if (files != null) {
|
|
|
|
|
files
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
hasErrors = true
|
|
|
|
|
emptyList()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasErrors) return ExitCode.COMPILATION_ERROR
|
|
|
|
|
|
|
|
|
|
if (files.isEmpty() && !arguments.version) {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR, "no source files")
|
|
|
|
|
return ExitCode.COMPILATION_ERROR
|
|
|
|
|
}
|
|
|
|
|
if (!checkSourceFiles(messageCollector, files)) {
|
|
|
|
|
return ExitCode.COMPILATION_ERROR
|
|
|
|
|
|
|
|
|
|
val existingFiles = mutableMapOf<String, InputFile>()
|
|
|
|
|
for (file in files) {
|
|
|
|
|
existingFiles[file.outputPath]?.let {
|
|
|
|
|
messageCollector.report(
|
|
|
|
|
CompilerMessageSeverity.ERROR,
|
|
|
|
|
"duplicate target file will be created for '${file.resource.name}' and '${it.resource.name}'")
|
|
|
|
|
return ExitCode.COMPILATION_ERROR
|
|
|
|
|
}
|
|
|
|
|
existingFiles[file.outputPath] = file
|
|
|
|
|
if (File(file.outputPath).isDirectory) {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR, "cannot open output file '${file.outputPath}': it is a directory")
|
|
|
|
|
return ExitCode.COMPILATION_ERROR
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val includedDeclarations = arguments.declarationsToKeep.orEmpty().toSet()
|
|
|
|
@@ -72,35 +89,83 @@ class K2JSDce : CLITool<K2JSDceArguments>() {
|
|
|
|
|
return ExitCode.OK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun checkSourceFiles(messageCollector: MessageCollector, files: List<InputFile>): Boolean {
|
|
|
|
|
return files.fold(true) { ok, file ->
|
|
|
|
|
val inputFile = File(file.path)
|
|
|
|
|
val outputFile = File(file.outputPath)
|
|
|
|
|
|
|
|
|
|
val inputOk = when {
|
|
|
|
|
!inputFile.exists() -> {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR, "source file or directory not found: " + file.path)
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
inputFile.isDirectory -> {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR, "input file '" + file.path + "' is a directory")
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
else -> true
|
|
|
|
|
private fun collectInputFiles(baseDir: File, fileName: String, messageCollector: MessageCollector): List<InputFile>? {
|
|
|
|
|
val file = File(fileName)
|
|
|
|
|
return when {
|
|
|
|
|
file.isDirectory -> {
|
|
|
|
|
collectInputFilesFromDirectory(baseDir, fileName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val outputOk = when {
|
|
|
|
|
outputFile.exists() && outputFile.isDirectory -> {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR, "cannot open output file '${outputFile.path}': is a directory")
|
|
|
|
|
false
|
|
|
|
|
file.isFile -> {
|
|
|
|
|
when {
|
|
|
|
|
fileName.endsWith(".js") -> {
|
|
|
|
|
listOf(singleInputFile(baseDir, fileName))
|
|
|
|
|
}
|
|
|
|
|
fileName.endsWith(".zip") || fileName.endsWith(".jar") -> {
|
|
|
|
|
collectInputFilesFromZip(baseDir, fileName)
|
|
|
|
|
}
|
|
|
|
|
else -> {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR,
|
|
|
|
|
"invalid file name '$fileName'; must end either with '.js', '.zip' or '.jar'")
|
|
|
|
|
null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else -> true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok and inputOk and outputOk
|
|
|
|
|
else -> {
|
|
|
|
|
messageCollector.report(CompilerMessageSeverity.ERROR, "source file or directory not found: $fileName")
|
|
|
|
|
null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun singleInputFile(baseDir: File, path: String): InputFile {
|
|
|
|
|
val moduleName = getModuleNameFromPath(path)
|
|
|
|
|
val pathToSourceMapCandidate = "$path.map"
|
|
|
|
|
val pathToSourceMap = if (File(pathToSourceMapCandidate).exists()) pathToSourceMapCandidate else null
|
|
|
|
|
return InputFile(InputResource.file(path), pathToSourceMap?.let { InputResource.file(it) },
|
|
|
|
|
File(baseDir, "$moduleName.js").absolutePath, moduleName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun collectInputFilesFromZip(baseDir: File, path: String): List<InputFile> {
|
|
|
|
|
return ZipFile(path).use { zipFile ->
|
|
|
|
|
zipFile.entries().asSequence()
|
|
|
|
|
.filter { !it.isDirectory }
|
|
|
|
|
.filter { it.name.endsWith(".js") }
|
|
|
|
|
.filter { zipFile.getEntry(it.name.metaJs()) != null }
|
|
|
|
|
.distinctBy { it.name }
|
|
|
|
|
.map { entry ->
|
|
|
|
|
val moduleName = getModuleNameFromPath(entry.name)
|
|
|
|
|
val pathToSourceMapCandidate = "${entry.name}.map"
|
|
|
|
|
val pathToSourceMap = if (zipFile.getEntry(pathToSourceMapCandidate) != null) pathToSourceMapCandidate else null
|
|
|
|
|
InputFile(InputResource.zipFile(path, entry.name), pathToSourceMap?.let { InputResource.zipFile(path, it) },
|
|
|
|
|
File(baseDir, "$moduleName.js").absolutePath, moduleName)
|
|
|
|
|
}
|
|
|
|
|
.toList()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun collectInputFilesFromDirectory(baseDir: File, path: String): List<InputFile> {
|
|
|
|
|
return File(path).walkTopDown().asSequence()
|
|
|
|
|
.filter { !it.isDirectory }
|
|
|
|
|
.filter { it.name.endsWith(".js") }
|
|
|
|
|
.filter { File(it.path.metaJs()).exists() }
|
|
|
|
|
.map { entry ->
|
|
|
|
|
val moduleName = getModuleNameFromPath(entry.name)
|
|
|
|
|
val pathToSourceMapCandidate = "${entry.path}.map"
|
|
|
|
|
val pathToSourceMap = if (File(pathToSourceMapCandidate).exists()) pathToSourceMapCandidate else null
|
|
|
|
|
InputFile(InputResource.file(entry.path), pathToSourceMap?.let { InputResource.file(it) },
|
|
|
|
|
File(baseDir, "$moduleName.js").absolutePath, moduleName)
|
|
|
|
|
}
|
|
|
|
|
.toList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun String.metaJs() = removeSuffix(".js") + ".meta.js"
|
|
|
|
|
|
|
|
|
|
private fun getModuleNameFromPath(path: String): String {
|
|
|
|
|
val dotIndex = path.lastIndexOf('.')
|
|
|
|
|
val slashIndex = maxOf(path.lastIndexOf('/'), path.lastIndexOf('\\'))
|
|
|
|
|
return path.substring(slashIndex + 1, if (dotIndex < 0) path.length else dotIndex)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun executableScriptFileName(): String = "kotlin-dce-js"
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|