From c64a80d132dd97be0905a384e4e325fca72db248 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Mon, 20 Apr 2020 13:34:05 +0300 Subject: [PATCH] [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 --- .../targets/js/npm/GradleNodeModuleBuilder.kt | 11 +- .../gradle/targets/js/npm/NpmDependency.kt | 7 +- .../gradle/targets/js/npm/NpmProject.kt | 9 ++ .../gradle/targets/js/npm/PackageJson.kt | 61 ++++++++++- .../targets/js/npm/PublicPackageJsonTask.kt | 102 ++++++++++++++++++ .../KotlinCompilationNpmResolution.kt | 2 +- .../resolver/KotlinCompilationNpmResolver.kt | 61 +++++------ 7 files changed, 207 insertions(+), 46 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt index 5d604abf9aa..1aa993ff394 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt @@ -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) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt index 81964901841..a88e43b70ca 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt @@ -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}" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt index c6d9238db82..262d59d5cf1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt @@ -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? diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PackageJson.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PackageJson.kt index af750da91d8..c166f5fd2a1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PackageJson.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PackageJson.kt @@ -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? = 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() @@ -76,4 +80,51 @@ class PackageJson( fun fromSrcPackageJson(packageJson: File?): PackageJson? = packageJson?.reader()?.use { Gson().fromJson(it, PackageJson::class.java) - } \ No newline at end of file + } + +fun packageJson( + npmProject: NpmProject, + npmDependencies: Collection +): PackageJson { + val compilation = npmProject.compilation + + val packageJson = PackageJson( + npmProject.name, + fixSemver(compilation.target.project.version.toString()) + ) + + packageJson.main = npmProject.main + + val dependencies = mutableMapOf() + + 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 +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt new file mode 100644 index 00000000000..188d2d2c2de --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt @@ -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 + get() = compilationResolution.externalNpmDependencies + .map { + NestedNpmDependency( + scope = it.scope, + name = it.name, + version = it.version + ) + } + + private val realExternalDependencies: Collection + 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.processDependencies() { + val newDependencies = mutableMapOf() + 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 +) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt index 1659e806fca..2bc6ed4557a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt @@ -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. */ diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt index c5d8c74de44..33036bdd0e5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt @@ -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 = + project.registerTask( + 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 = 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() - - 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 - } } } \ No newline at end of file