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 762839abd9a..7d574a913bb 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 @@ -76,24 +76,90 @@ private fun isKotlinJsRuntimeFile(file: File): Boolean { } internal fun fixSemver(version: String): String { - val c = version.split("-", limit = 2) - val main = c[0] - val preRelease = if (c.size > 1) c[1] else null - val numbers = main.split(".") - val major = numbers[0] - val minor = if (numbers.size > 1) numbers[1] else null - val patch = if (numbers.size > 2) numbers[2] else null - return buildString { - append(major.filter { it.isDigit() }) - append(".") - append(minor?.filter { it.isDigit() } ?: "0") - append(".") - append(patch?.filter { it.isDigit() } ?: "0") - if (preRelease != null) { - append("-") - append(preRelease) + var i = 0 + var number = 0 + var major = "1" + var minor = "0" + var patch = "0" + val rest = StringBuilder() + val digits = StringBuilder() + + fun setComponent() { + val digitsFiltered = digits.toString().trimStart { it == '0' }.takeIf { it.isNotEmpty() } ?: "0" + when (number) { + 0 -> major = digitsFiltered + 1 -> minor = digitsFiltered + 2 -> patch = digitsFiltered + else -> error(number) } } + + while (i < version.length) { + val c = version[i++] + if (c.isDigit()) digits.append(c) + else if (c == '-' || c == '+') { + // examples: + // 1.2.3-RC1-1234, + // 1.2-RC1-1234 + // 1.2-beta.11+sha.0x + setComponent() + number = 3 + rest.append(c) + break + } else if (c == '.') { + rest.append(c) + setComponent() + digits.setLength(0) + number++ + if (number > 2) break + } else rest.append(c) + } + + if (number <= 2) setComponent() + + rest.append(version.substring(i)) + + val restFiltered = rest.filter { + it in '0'..'9' || + it in 'A'..'Z' || + it in 'a'..'z' || + it == '.' || + it == '-' || + it == '+' + } + val restComponents = restFiltered.split('+', limit = 2) + + val preRelease = restComponents.getOrNull(0) + ?.foldDelimiters() + ?.trim { it == '-' || it == '.' } + ?.takeIf { it.isNotEmpty() } + + val build = restComponents.getOrNull(1) + ?.filter { it != '+' } + ?.trim { it == '-' || it == '.' } + ?.takeIf { it.isNotEmpty() } + + return "$major.$minor.$patch" + + (if (preRelease != null) "-$preRelease" else "") + + (if (build != null) "+$build" else "") +} + +private fun String.foldDelimiters(): String { + val result = StringBuilder(length) + var endsWithDelimiter = false + for (i in 0 until length) { + val c = this[i] + if (c == '+' || c == '-' || c == '.') { + if (!endsWithDelimiter) { + result.append(c) + endsWithDelimiter = true + } + } else { + endsWithDelimiter = false + result.append(c) + } + } + return result.toString() } fun makeNodeModule( diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilderKtTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilderKtTest.kt index 58799fb48dc..3015ed32b3c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilderKtTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilderKtTest.kt @@ -8,11 +8,57 @@ package org.jetbrains.kotlin.gradle.targets.js.npm import org.junit.Test import org.junit.Assert.* +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class GradleNodeModuleBuilderKtTest(val expected: String, val actual: String) { + companion object { + @Parameterized.Parameters(name = "fix({1}) == {0}") + @JvmStatic + fun data() = listOf( + arrayOf("1.3.0-SNAPSHOT", "1.3-SNAPSHOT"), + arrayOf("1.2.3-RC1-1234", "1.2.3-RC1-1234"), + arrayOf("0.0.0-abc-qx", "abc-qx"), + arrayOf("1.0.0-o.p-qx", "1.o.p-qx"), + arrayOf("1.2.3-o.p-qa", "1.o2.3p-qa"), + arrayOf("1.0.0", "1.0.0+"), + arrayOf("1.2.3-beta.11+sha.0nsfgkjkjsdf", "1.2.3-beta.11+sha.0nsfgkjkjsdf"), + arrayOf("1.2.0-beta.11+sha.0nsfgkjkjsdf", "1.2-beta.11+sha.0nsfgkjkjsdf"), + arrayOf("1.0.0-beta.11+sha.0nsfgkjkjsdf", "1-beta.11+sha.0nsfgkjkjsdf"), + arrayOf("1.2.3-beta.1-1.ab-c+sha.0nsfgkjkjs-df", "1.2.3-beta.1-1.ab-c+sha.0nsfgkjkjs-df"), + arrayOf("1.0.0-alpha.1", "1.0.0-alpha.1"), + arrayOf("1.0.0-alpha.beta", "1.0.0-alpha.beta"), + arrayOf("1.0.0-alpha.12.ab-c", "1.0.0-alpha.12.ab-c"), + arrayOf("1.0.0-alpha.12.x-yz", "1.0.0-alpha.12.x-yz"), + arrayOf("1.0.0", "1"), + arrayOf("1.2.0", "1.2"), + arrayOf("1.2.3", "1.2.3"), + arrayOf("1.2.3", "01.2.3"), + arrayOf("0.2.3", "0.2.3"), + arrayOf("0.0.3", "0.0.3"), + arrayOf("0.0.0", "0.0.0"), + arrayOf("1.2.3", "1.2.3-"), + arrayOf("1.2.3-x", "1.2.3-x"), + arrayOf("1.2.3-xy", "1.2.3-xy"), + arrayOf("1.2.3-xy-z", "1.2.3-xy-z"), + arrayOf("1.2.3-xy-z", "1.2.3-xy-z+"), + arrayOf("1.2.3-xy-z+1", "1.2.3-xy-z+1"), + arrayOf("1.2.3-x.xy-z+1", "1x.2.3-xy-z+1"), + arrayOf("1.2.3-x.y.xy-z+1", "1x.2y.3-xy-z+1"), + arrayOf("1.2.3-x.y.z-xy-z+1", "1x.2y.3z-xy-z+1"), + arrayOf("1.2.3-x.y.z-xy-z+1u", "1x.2y.3z-xy-z+1u"), + arrayOf("1.2.3-x.y.z-x.y-z+1u", "1x.2y.3z-x.y-z+1u"), + arrayOf("1.2.3-x.y.z-x.y-z+1u", "1x.2y.3z!-x.y-z+1u"), + arrayOf("1.2.3-x.y.z-x.y-z+1u", "1x.2y.3z-x.y!-z+1u"), + arrayOf("1.2.3-x.y.z-x.y-z+1u", "!1x.2y.3z-x.y!-z+1u"), + arrayOf("1.2.3-x.y.z-x.y-z+1u", "1x.!2y.3z-x.y!-z+1u") + ) + } + -class GradleNodeModuleBuilderKtTest { @Test - fun fixSemver() { - assertEquals("1.3.0-SNAPSHOT", fixSemver("1.3-SNAPSHOT")) - assertEquals("1.2.3-RC1-1234", fixSemver("1.2.3-RC1-1234")) + fun testFixSemver() { + assertEquals(expected, fixSemver(actual)) } } \ No newline at end of file