diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmVersions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmVersions.kt index 182f92565d4..4f461e48ec7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmVersions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmVersions.kt @@ -19,12 +19,11 @@ fun versionToNpmRanges(version: String): Set { .visit(parser.rangeSet())!! } -fun buildNpmVersion( +fun includedRange( includedVersions: List, - excludedVersions: List, includedWithCaret: Boolean = false -): String { - val includedRange: NpmRange? = try { +): NpmRange = + try { includedVersions .flatMap { versionToNpmRanges(it) } .map { if (includedWithCaret) it.caretizeSingleVersion() else it } @@ -33,13 +32,18 @@ fun buildNpmVersion( requireNotNull(intersection) { "Included versions have no intersection $includedVersions" } - intersection!! + intersection } } catch (e: UnsupportedOperationException) { throw InvalidUserDataException("No ranges for included versions $includedVersions") } - includedRange!! +fun buildNpmVersion( + includedVersions: List, + excludedVersions: List, + includedWithCaret: Boolean = false +): String { + val includedRange: NpmRange = includedRange(includedVersions, includedWithCaret) if (excludedVersions.isEmpty()) return includedRange.toString() 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 cb39b5196ff..5042d601e6a 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 @@ -9,6 +9,7 @@ import com.google.gson.ExclusionStrategy import com.google.gson.FieldAttributes import com.google.gson.Gson import com.google.gson.GsonBuilder +import org.gradle.api.GradleException import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import java.io.File @@ -126,7 +127,7 @@ fun packageJson( npmDependencies.forEach { val module = it.key - dependencies[it.key] = chooseVersion(dependencies[module], it.version) + dependencies[module] = chooseVersion(module, dependencies[module], it.version) } npmDependencies.forEach { @@ -146,12 +147,19 @@ fun 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 == "*") { +private fun chooseVersion( + module: String, + oldVersion: String?, + newVersion: String +): String { + if (oldVersion == null) { return newVersion } - return oldVersion ?: newVersion + return (includedRange(listOf(oldVersion)) intersect includedRange(listOf(newVersion)))?.toString() + ?: throw GradleException( + """ + There is already declared version of '$module' with version '$oldVersion' which does not intersects with another declared version '${newVersion}' + """.trimIndent() + ) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt index 4f27b658563..030acf7f526 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt @@ -5,8 +5,10 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn -import com.github.gundy.semver4j.SemVer +import org.gradle.api.GradleException import org.jetbrains.kotlin.gradle.targets.js.npm.FILE_VERSION_PREFIX +import org.jetbrains.kotlin.gradle.targets.js.npm.includedRange +import org.jetbrains.kotlin.gradle.targets.js.npm.intersect import java.io.File internal class YarnEntryRegistry(private val lockFile: File) { @@ -16,21 +18,27 @@ internal class YarnEntryRegistry(private val lockFile: File) { fun find(packageKey: String, version: String): YarnLock.Entry { val key = dependencyKey(packageKey, version) - var entry = entryMap[key] + val entry = entryMap[key] - if (entry == null && version == "*") { - val searchKey = dependencyKey(packageKey, "") - entry = entryMap.entries - .filter { it.key.startsWith(searchKey) } - .firstOrNull { - SemVer.satisfies(it.key.removePrefix(searchKey), "*") + if (entry != null) { + return entry + } + + val versionRange = includedRange(listOf(version)) + + entryMap.entries + .firstOrNull { (_, entry) -> + if (entry.version == null) { + false + } else { + (versionRange intersect includedRange(listOf(entry.version))) != null } - ?.value - } + } + ?.let { return it.value } - return checkNotNull(entry) { + throw GradleException( "Cannot find $key in yarn.lock" - } + ) } private val YarnLock.Entry.dependencyKey: String