org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.targets.js.npm
import com.google.gson.*
import org.gradle.api.GradleException
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
import java.io.File
import java.io.Serializable
// Gson set nulls reflectively no matter on default values and non-null types
class PackageJson(
var name: String,
var version: String
) : Serializable {
internal val customFields = mutableMapOf()
val empty: Boolean
get() = main == null &&
private == null &&
workspaces == null &&
dependencies.isEmpty() &&
devDependencies.isEmpty()
val scopedName: ScopedName
get() = scopedName(name)
var private: Boolean? = null
var main: String? = null
var workspaces: Collection? = null
var resolutions: Map? = null
var types: String? = null
@Suppress("USELESS_ELVIS")
val devDependencies = mutableMapOf()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val dependencies = mutableMapOf()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val peerDependencies = mutableMapOf()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val optionalDependencies = mutableMapOf()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val bundledDependencies = mutableListOf()
get() = field ?: mutableListOf()
fun customField(pair: Pair) {
customFields[pair.first] = pair.second
}
fun customField(key: String, value: Any?) {
customFields[key] = value
}
fun customField(key: String, value: Number) {
customFields[key] = value
}
fun customField(key: String, value: Boolean) {
customFields[key] = value
}
companion object {
fun scopedName(name: String): ScopedName = if (name.contains("/")) ScopedName(
scope = name.substringBeforeLast("/").removePrefix("@"),
name = name.substringAfterLast("/")
) else ScopedName(scope = null, name = name)
operator fun invoke(scope: String, name: String, version: String) =
PackageJson(ScopedName(scope, name).toString(), version)
}
data class ScopedName(val scope: String?, val name: String) {
override fun toString() = if (scope == null) name else "@$scope/$name"
}
fun saveTo(packageJsonFile: File) {
val gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.serializeNulls()
.registerTypeAdapterFactory(PackageJsonTypeAdapter())
.create()
packageJsonFile.ensureParentDirsCreated()
val jsonTree = gson.toJsonTree(this)
val previous = if (packageJsonFile.exists()) {
packageJsonFile.reader().use {
JsonParser.parseReader(it)
}
} else null
if (jsonTree != previous) {
packageJsonFile.writer().use {
gson.toJson(jsonTree, it)
}
}
}
}
fun fromSrcPackageJson(packageJson: File?): PackageJson? =
packageJson?.reader()?.use {
Gson().fromJson(it, PackageJson::class.java)
}
internal fun packageJson(
name: String,
version: String,
main: String,
npmDependencies: Collection,
packageJsonHandlers: List Unit>
): PackageJson {
val packageJson = PackageJson(
name,
fixSemver(version)
)
packageJson.main = main
val dependencies = mutableMapOf()
npmDependencies.forEach {
val module = it.name
dependencies[module] = chooseVersion(module, dependencies[module], it.version)
}
npmDependencies.forEach {
val dependency = dependencies.getValue(it.name)
when (it.scope) {
NpmDependency.Scope.NORMAL -> packageJson.dependencies[it.name] = dependency
NpmDependency.Scope.DEV -> packageJson.devDependencies[it.name] = dependency
NpmDependency.Scope.OPTIONAL -> packageJson.optionalDependencies[it.name] = dependency
NpmDependency.Scope.PEER -> packageJson.peerDependencies[it.name] = dependency
}
}
packageJsonHandlers.forEach {
it(packageJson)
}
return packageJson
}
private fun chooseVersion(
module: String,
oldVersion: String?,
newVersion: String
): String {
if (oldVersion == null) {
return newVersion
}
return (includedRange(oldVersion) intersect includedRange(newVersion))?.toString()
?: throw GradleException(
"""
There is already declared version of '$module' with version '$oldVersion' which does not intersects with another declared version '${newVersion}'
""".trimIndent()
)
}
internal const val fakePackageJsonValue = "FAKE"