[Gradle, JS] Move suppress gson null issue for particular fields

[Gradle, JS] Fix package json creation from existed package json w/o version

[Gradle, JS] Add task for publishing package json

[Gradle, JS] Add skip on empty npm dependencies

[Gradle, JS] Publishing package json with installed modules

[Gradle, JS] Add publishingPackageJson for compilation npm resolution

[Gradle, JS] Add publishingPackageJson to jar task

[Gradle, JS] PublishingPackageJsonTask now in jar

[Gradle, JS] Actual skipping of package json creation

[Gradle, JS] jar task depends only on main publishing package json

[Gradle, JS] Move calculation of packageJson

[Gradle, JS] Filter file dependencies
This commit is contained in:
Ilya Goncharov
2020-04-20 13:34:05 +03:00
parent c91089be59
commit c64a80d132
7 changed files with 207 additions and 46 deletions
@@ -1,6 +1,6 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
* 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
@@ -39,8 +39,11 @@ internal class GradleNodeModuleBuilder(
fun rebuild(): File? {
if (files.isEmpty()) return null
val packageJson = fromSrcPackageJson(srcPackageJsonFile)
?: PackageJson(dependency.moduleName, dependency.moduleVersion)
val packageJson = fromSrcPackageJson(srcPackageJsonFile)?.apply {
// Gson set nulls reflectively no matter on default values and non-null types
@Suppress("USELESS_ELVIS")
version = version ?: dependency.moduleVersion
} ?: PackageJson(dependency.moduleName, dependency.moduleVersion)
val jsFiles = files.filter { it.name.endsWith(".js") }
if (jsFiles.size == 1) {
@@ -1,6 +1,6 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
* 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
@@ -127,6 +127,9 @@ data class NpmDependency(
override fun getReason(): String? = reason
}
internal fun String.isFileVersion() =
startsWith(FILE_VERSION_PREFIX)
internal fun fileVersion(directory: File): String =
"$FILE_VERSION_PREFIX${directory.canonicalPath}"
@@ -64,6 +64,15 @@ open class NpmProject(val compilation: KotlinJsCompilation) {
val externalsDir: File
get() = externalsDirRoot.resolve("src")
val publicPackageJson: File
get() = project.buildDir
.resolve("tmp")
.resolve(publicPackageJsonTaskName)
.resolve(PACKAGE_JSON)
val publicPackageJsonTaskName: String
get() = compilation.disambiguateName(PublicPackageJsonTask.NAME)
internal val modules = NpmProjectModules(dir)
private val rootNodeModules: NpmProjectModules?
@@ -1,6 +1,6 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
* 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
@@ -10,8 +10,7 @@ import com.google.gson.GsonBuilder
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
import java.io.File
// Gson set nulls reflective no matter on default values and non-null types
@Suppress("USELESS_ELVIS")
// Gson set nulls reflectively no matter on default values and non-null types
class PackageJson(
var name: String,
var version: String
@@ -32,18 +31,23 @@ class PackageJson(
var workspaces: Collection<String>? = null
@Suppress("USELESS_ELVIS")
val devDependencies = mutableMapOf<String, String>()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val dependencies = mutableMapOf<String, String>()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val peerDependencies = mutableMapOf<String, String>()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val optionalDependencies = mutableMapOf<String, String>()
get() = field ?: mutableMapOf()
@Suppress("USELESS_ELVIS")
val bundledDependencies = mutableListOf<String>()
get() = field ?: mutableListOf()
@@ -76,4 +80,51 @@ class PackageJson(
fun fromSrcPackageJson(packageJson: File?): PackageJson? =
packageJson?.reader()?.use {
Gson().fromJson(it, PackageJson::class.java)
}
}
fun packageJson(
npmProject: NpmProject,
npmDependencies: Collection<NpmDependency>
): PackageJson {
val compilation = npmProject.compilation
val packageJson = PackageJson(
npmProject.name,
fixSemver(compilation.target.project.version.toString())
)
packageJson.main = npmProject.main
val dependencies = mutableMapOf<String, String>()
npmDependencies.forEach {
val module = it.key
dependencies[it.key] = chooseVersion(dependencies[module], it.version)
}
npmDependencies.forEach {
val dependency = dependencies.getValue(it.key)
when (it.scope) {
NpmDependency.Scope.NORMAL -> packageJson.dependencies[it.key] = dependency
NpmDependency.Scope.DEV -> packageJson.devDependencies[it.key] = dependency
NpmDependency.Scope.OPTIONAL -> packageJson.optionalDependencies[it.key] = dependency
NpmDependency.Scope.PEER -> packageJson.peerDependencies[it.key] = dependency
}
}
compilation.packageJsonHandlers.forEach {
it(packageJson)
}
return packageJson
}
// TODO: real versions conflict resolution
private fun chooseVersion(oldVersion: String?, newVersion: String): String {
// https://yarnpkg.com/lang/en/docs/dependency-versions/#toc-x-ranges
if (oldVersion == "*") {
return newVersion
}
return oldVersion ?: newVersion
}
@@ -0,0 +1,102 @@
/*
* 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 org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import java.io.File
import javax.inject.Inject
open class PublicPackageJsonTask
@Inject
constructor(
private val nodeJs: NodeJsRootExtension,
private val npmProject: NpmProject
) : DefaultTask() {
private val compilationResolution
get() = nodeJs.npmResolutionManager.requireInstalled()[project][npmProject.compilation]
@get:Nested
internal val externalDependencies: Collection<NestedNpmDependency>
get() = compilationResolution.externalNpmDependencies
.map {
NestedNpmDependency(
scope = it.scope,
name = it.name,
version = it.version
)
}
private val realExternalDependencies: Collection<NpmDependency>
get() = compilationResolution.externalNpmDependencies
@Input
var skipOnEmptyNpmDependencies: Boolean = false
@get:OutputFile
val packageJson: File
get() = npmProject.publicPackageJson
@TaskAction
fun resolve() {
packageJson(npmProject, realExternalDependencies).let { packageJson ->
if (skipOnEmptyNpmDependencies && packageJson.skipRequired()) {
return
}
packageJson.apply {
listOf(
dependencies,
peerDependencies,
optionalDependencies
).forEach { it.processDependencies() }
}
packageJson.devDependencies.clear()
packageJson.saveTo(npmProject.publicPackageJson)
}
}
private fun MutableMap<String, String>.processDependencies() {
val newDependencies = mutableMapOf<String, String>()
filterNot { (_, version) -> version.isFileVersion() }
.forEach { (key, version) ->
newDependencies[key] = version
}
clear()
newDependencies.forEach { (key, version) ->
this[key] = version
}
}
private fun PackageJson.skipRequired() =
dependencies.isEmpty() &&
peerDependencies.isEmpty() &&
optionalDependencies.isEmpty() &&
optionalDependencies.isEmpty() &&
bundledDependencies.isEmpty()
companion object {
const val NAME = "publicPackageJson"
}
}
internal data class NestedNpmDependency(
@Input
val scope: NpmDependency.Scope,
@Input
val name: String,
@Input
val version: String
)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.
*/
@@ -14,6 +14,8 @@ import org.gradle.api.initialization.IncludedBuild
import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Zip
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
@@ -21,11 +23,11 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName
import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.targets.js.npm.*
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency.Scope.*
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON
import org.jetbrains.kotlin.gradle.targets.js.npm.plugins.CompilationResolverPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.CompositeProjectComponentArtifactMetadata
import org.jetbrains.kotlin.gradle.utils.`is`
import org.jetbrains.kotlin.gradle.utils.topRealPath
@@ -45,6 +47,26 @@ internal class KotlinCompilationNpmResolver(
val target get() = compilation.target
val project get() = target.project
val packageJsonTaskHolder = KotlinPackageJsonTask.create(compilation)
val publicPackageJsonTaskHolder: TaskProvider<PublicPackageJsonTask> =
project.registerTask<PublicPackageJsonTask>(
npmProject.publicPackageJsonTaskName,
listOf(nodeJs, npmProject)
) {
it.skipOnEmptyNpmDependencies = true
it.dependsOn(nodeJs.npmInstallTask)
it.dependsOn(packageJsonTaskHolder)
}.also { packageJsonTask ->
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
project.tasks
.withType(Zip::class.java)
.named(npmProject.target.artifactsTaskName)
.configure {
it.from(packageJsonTask)
}
}
}
val plugins: List<CompilationResolverPlugin> = projectResolver.resolver.plugins.flatMap {
it.createCompilationResolverPlugins(this)
}
@@ -301,13 +323,11 @@ internal class KotlinCompilationNpmResolver(
}
.filterNotNull()
val packageJson = PackageJson(
npmProject.name,
fixSemver(project.version.toString())
val packageJson = packageJson(
npmProject,
externalNpmDependencies
)
packageJson.main = npmProject.main
compositeDependencies.forEach {
packageJson.dependencies[it.name] = it.version
}
@@ -320,23 +340,6 @@ internal class KotlinCompilationNpmResolver(
packageJson.dependencies[it.name] = fileVersion(it.path)
}
val dependencies = mutableMapOf<String, String>()
externalNpmDependencies.forEach {
val module = it.key
dependencies[it.key] = chooseVersion(dependencies[module], it.version)
}
externalNpmDependencies.forEach {
val dependency = dependencies.getValue(it.key)
when (it.scope) {
NORMAL -> packageJson.dependencies[it.key] = dependency
DEV -> packageJson.devDependencies[it.key] = dependency
OPTIONAL -> packageJson.optionalDependencies[it.key] = dependency
PEER -> packageJson.peerDependencies[it.key] = dependency
}
}
compilation.packageJsonHandlers.forEach {
it(packageJson)
}
@@ -366,15 +369,5 @@ internal class KotlinCompilationNpmResolver(
?.map { it.resolve(PACKAGE_JSON) }
?: emptyList()
}
// TODO: real versions conflict resolution
private fun chooseVersion(oldVersion: String?, newVersion: String): String {
// https://yarnpkg.com/lang/en/docs/dependency-versions/#toc-x-ranges
if (oldVersion == "*") {
return newVersion
}
return oldVersion ?: newVersion
}
}
}