From 85adee9804bf90e973df9e7fb33bc178ab1ebfe7 Mon Sep 17 00:00:00 2001 From: Sergey Rostov Date: Fri, 31 May 2019 12:47:50 +0300 Subject: [PATCH] Gradle, js, npm: resolve and warn about clashes in packages_imported #KT-31697 Fixed --- .../gradle/targets/js/npm/GradleNodeModule.kt | 5 +- .../YarnImportedPackagesVersionResolver.kt | 88 +++++++++++++++++++ .../gradle/targets/js/yarn/YarnWorkspaces.kt | 11 +-- 3 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModule.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModule.kt index cdc8183c7b5..a9ef64bd18b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModule.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModule.kt @@ -10,4 +10,7 @@ import java.io.File /** * Fake NodeJS module directory created from Gradle external module */ -data class GradleNodeModule(val name: String, val version: String, val path: File) \ No newline at end of file +data class GradleNodeModule(val name: String, val version: String, val path: File) { + val semver: SemVer + get() = SemVer.from(version) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt new file mode 100644 index 00000000000..2c46d2d5f51 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2019 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.yarn + +import com.google.gson.Gson +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModule +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectPackage +import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson +import java.io.File + +class YarnImportedPackagesVersionResolver( + private val rootProject: Project, + private val npmProjects: MutableList, + private val nodeJsWorldDir: File +) { + private val resolvedVersion = mutableMapOf() + private val importedProjectWorkspaces = mutableListOf() + private val externalModules = npmProjects.flatMapTo(mutableSetOf()) { + it.gradleDependencies.externalModules + } + + fun resolveAndUpdatePackages(): MutableList { + resolve() + updatePackages() + return importedProjectWorkspaces + } + + private fun resolve() { + externalModules.groupBy { it.name }.forEach { (name, versions) -> + val selected: GradleNodeModule = if (versions.size > 1) { + val sorted = versions.sortedBy { it.semver } + rootProject.logger.warn( + "There are multiple versions of \"$name\" used in nodejs build: ${sorted.joinToString(", ") { it.version }}. " + + "Only latest version will be used." + ) + val selected = sorted.last() + resolvedVersion[name] = selected.version + selected + } else versions.single() + + importedProjectWorkspaces.add(selected.path.relativeTo(nodeJsWorldDir).path) + } + } + + private fun updatePackages() { + if (resolvedVersion.isEmpty()) return + + npmProjects.forEach { + updatePackageJson(it.packageJson, it.npmProject.packageJsonFile) + } + + externalModules.forEach { + val packageJsonFile = it.path.resolve(NpmProject.PACKAGE_JSON) + val packageJson = packageJsonFile.reader().use { + Gson().fromJson(it, PackageJson::class.java) + } + + updatePackageJson(packageJson, packageJsonFile) + } + } + + private fun updatePackageJson( + packageJson: PackageJson, + path: File + ) { + val updates = listOf(packageJson.dependencies, packageJson.devDependencies).map { updateVersionsMap(it) } + if (updates.any { it }) { + packageJson.saveTo(path) + } + } + + private fun updateVersionsMap(map: MutableMap): Boolean { + var doneSomething = false + map.iterator().forEachRemaining { + val resolved = resolvedVersion[it.key] + if (resolved != null) { + it.setValue(resolved) + doneSomething = true + } + } + return doneSomething + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt index b010682c89c..6eeab592fc1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt @@ -7,10 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn import org.gradle.api.Project import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectPackage -import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson +import org.jetbrains.kotlin.gradle.targets.js.npm.* import java.io.File object YarnWorkspaces : YarnBasics() { @@ -53,11 +50,7 @@ object YarnWorkspaces : YarnBasics() { rootPackageJson.private = true val npmProjectWorkspaces = npmProjects.map { it.npmProject.dir.relativeTo(nodeJsWorldDir).path } - val importedProjectWorkspaces = npmProjects.flatMapTo(mutableSetOf()) { - it.gradleDependencies.externalModules.map { importedProject -> - importedProject.path.relativeTo(nodeJsWorldDir).path - } - } + val importedProjectWorkspaces = YarnImportedPackagesVersionResolver(rootProject, npmProjects, nodeJsWorldDir).resolveAndUpdatePackages() rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces