[Gradle, JS] Use not inverted visitor but straightforward visitor
^KT-41054 fixed
This commit is contained in:
+50
-4
@@ -6,16 +6,25 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js.npm
|
||||
|
||||
import com.github.gundy.semver4j.model.Version
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmVersionInvertedVisitor.Companion.GT
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmVersionInvertedVisitor.Companion.GTEQ
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmVersionInvertedVisitor.Companion.LT
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmVersionInvertedVisitor.Companion.LTEQ
|
||||
|
||||
data class NpmRange(
|
||||
val startVersion: SemVer? = null,
|
||||
val startInclusive: Boolean = false,
|
||||
val endVersion: SemVer? = null,
|
||||
val endInclusive: Boolean = false
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "${if (startVersion != null) "${if (startInclusive) GTEQ else GT}$startVersion" else ""} ${if (endVersion != null) "${if (endInclusive) LTEQ else LT}$endVersion" else ""}"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val NONE_RANGE = NpmRange(
|
||||
startVersion = Version.fromString(NONE_VERSION)
|
||||
endVersion = Version.fromString(NONE_VERSION)
|
||||
.toSemVer()
|
||||
)
|
||||
|
||||
@@ -36,12 +45,49 @@ infix fun NpmRange.union(other: NpmRange): List<NpmRange> {
|
||||
}
|
||||
}
|
||||
|
||||
fun NpmRange.invert(): List<NpmRange> {
|
||||
val result = mutableListOf<NpmRange>()
|
||||
if (startVersion != null || endVersion == null) {
|
||||
result.add(
|
||||
NpmRange(
|
||||
endVersion = startVersion,
|
||||
endInclusive = !startInclusive
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (endVersion != null || startVersion == null) {
|
||||
result.add(
|
||||
NpmRange(
|
||||
startVersion = endVersion,
|
||||
startInclusive = !endInclusive
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return result.distinct()
|
||||
}
|
||||
|
||||
infix fun List<NpmRange>.intersect(others: List<NpmRange>): List<NpmRange> = flatMap { current ->
|
||||
others.mapNotNull { other ->
|
||||
current intersect other
|
||||
}
|
||||
}
|
||||
|
||||
infix fun NpmRange.intersect(other: NpmRange): NpmRange? {
|
||||
if (!isIntersect(other)) return null
|
||||
|
||||
val startVersion = max(this.startVersion, other.startVersion)
|
||||
val startVersion = when {
|
||||
startVersion == null -> other.startVersion
|
||||
other.startVersion == null -> startVersion
|
||||
else -> max(this.startVersion, other.startVersion)
|
||||
}
|
||||
|
||||
val endVersion = min(this.endVersion, other.endVersion)
|
||||
val endVersion = when {
|
||||
endVersion == null -> other.endVersion
|
||||
other.endVersion == null -> endVersion
|
||||
else -> min(this.endVersion, other.endVersion)
|
||||
}
|
||||
|
||||
return NpmRange(
|
||||
startVersion = startVersion,
|
||||
|
||||
+1
-8
@@ -31,14 +31,7 @@ class NpmVersionConstraint(
|
||||
)
|
||||
}
|
||||
|
||||
val version = buildNpmVersion(listOf(requiredVersion), rejectedVersions)
|
||||
|
||||
val requiredRange = "^$version"
|
||||
return if (SemVer.valid(requiredRange) && SemVer.satisfies(version, requiredRange)) {
|
||||
requiredRange
|
||||
} else {
|
||||
version
|
||||
}
|
||||
return buildNpmVersion(listOf(requiredVersion), rejectedVersions)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+95
-26
@@ -10,17 +10,17 @@ import com.github.gundy.semver4j.generated.grammar.NodeSemverExpressionParser
|
||||
import com.github.gundy.semver4j.model.Version
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
|
||||
class NpmVersionInvertedVisitor : NodeSemverExpressionBaseVisitor<NpmRange>() {
|
||||
override fun visitEmptyRange(ctx: NodeSemverExpressionParser.EmptyRangeContext): NpmRange =
|
||||
NONE_RANGE
|
||||
class NpmVersionInvertedVisitor : NodeSemverExpressionBaseVisitor<List<NpmRange?>>() {
|
||||
override fun visitEmptyRange(ctx: NodeSemverExpressionParser.EmptyRangeContext): List<NpmRange?> =
|
||||
listOf(null)
|
||||
|
||||
override fun visitWildcard(ctx: NodeSemverExpressionParser.WildcardContext): NpmRange =
|
||||
NONE_RANGE
|
||||
override fun visitWildcard(ctx: NodeSemverExpressionParser.WildcardContext): List<NpmRange?> =
|
||||
listOf(null)
|
||||
|
||||
override fun visitWildcardRange(ctx: NodeSemverExpressionParser.WildcardRangeContext): NpmRange =
|
||||
inverseVersion(ctx.partialWildcardSemver().text)
|
||||
override fun visitWildcardRange(ctx: NodeSemverExpressionParser.WildcardRangeContext): List<NpmRange> =
|
||||
listOf(eq(ctx.partialWildcardSemver().text))
|
||||
|
||||
override fun visitOperator(ctx: NodeSemverExpressionParser.OperatorContext): NpmRange {
|
||||
override fun visitOperator(ctx: NodeSemverExpressionParser.OperatorContext): List<NpmRange> {
|
||||
val eq: TerminalNode? = ctx.unaryOperator().EQ()
|
||||
val gt: TerminalNode? = ctx.unaryOperator().GT()
|
||||
val lt: TerminalNode? = ctx.unaryOperator().LT()
|
||||
@@ -28,51 +28,120 @@ class NpmVersionInvertedVisitor : NodeSemverExpressionBaseVisitor<NpmRange>() {
|
||||
val version = ctx.partialWildcardSemver().text
|
||||
|
||||
if (gt == null && lt == null) {
|
||||
return inverseVersion(version)
|
||||
return listOf(eq(version))
|
||||
}
|
||||
|
||||
val invertedEqToken = if (eq != null) EQ else ""
|
||||
val eqToken = eq != null
|
||||
|
||||
if (lt != null) {
|
||||
return "$GT$invertedEqToken$version"
|
||||
return listOf(
|
||||
if (eqToken) lteq(version) else lt(version)
|
||||
)
|
||||
}
|
||||
|
||||
return "$LT$invertedEqToken$version"
|
||||
return listOf(
|
||||
if (eqToken) gteq(version) else gt(version)
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitWildcardOperator(ctx: NodeSemverExpressionParser.WildcardOperatorContext): NpmRange =
|
||||
NONE_RANGE
|
||||
override fun visitWildcardOperator(ctx: NodeSemverExpressionParser.WildcardOperatorContext): List<NpmRange?> =
|
||||
listOf(null)
|
||||
|
||||
override fun visitTildeRange(ctx: NodeSemverExpressionParser.TildeRangeContext): NpmRange {
|
||||
override fun visitTildeRange(ctx: NodeSemverExpressionParser.TildeRangeContext): List<NpmRange> {
|
||||
val version = Version.fromString(ctx.fullSemver().text)
|
||||
val nextVersion = version
|
||||
.incrementMinor()
|
||||
|
||||
return "$LT$version $OR $GTEQ$nextVersion"
|
||||
return listOf(gteq(version))
|
||||
}
|
||||
|
||||
override fun visitCaretRange(ctx: NodeSemverExpressionParser.CaretRangeContext): NpmRange {
|
||||
override fun visitCaretRange(ctx: NodeSemverExpressionParser.CaretRangeContext): List<NpmRange> {
|
||||
val version = Version.fromString(ctx.fullSemver().text)
|
||||
val nextVersion = version
|
||||
.incrementMajor()
|
||||
|
||||
return "$LT$version $OR $GTEQ$nextVersion"
|
||||
return lt(version) union gteq(nextVersion)
|
||||
}
|
||||
|
||||
override fun visitFullySpecifiedSemver(ctx: NodeSemverExpressionParser.FullySpecifiedSemverContext): NpmRange =
|
||||
override fun visitFullySpecifiedSemver(ctx: NodeSemverExpressionParser.FullySpecifiedSemverContext): List<NpmRange> =
|
||||
inverseVersion(ctx.fullSemver().text)
|
||||
|
||||
override fun visitLogicalAndOfSimpleExpressions(ctx: NodeSemverExpressionParser.LogicalAndOfSimpleExpressionsContext): NpmRange =
|
||||
ctx.simple()
|
||||
.joinToString(OR) { visit(it) }
|
||||
override fun visitLogicalAndOfSimpleExpressions(ctx: NodeSemverExpressionParser.LogicalAndOfSimpleExpressionsContext): List<NpmRange?> =
|
||||
try {
|
||||
ctx.simple()
|
||||
.flatMap { visit(it) }
|
||||
.filterNotNull()
|
||||
.reduce { acc: NpmRange?, next: NpmRange ->
|
||||
val npmRange = if (acc == null) null else acc intersect next
|
||||
npmRange
|
||||
}
|
||||
} catch (e: UnsupportedOperationException) {
|
||||
null
|
||||
}.let { listOf(it) }
|
||||
|
||||
override fun visitLogicalOrOfMultipleRanges(ctx: NodeSemverExpressionParser.LogicalOrOfMultipleRangesContext): NpmRange =
|
||||
override fun visitLogicalOrOfMultipleRanges(ctx: NodeSemverExpressionParser.LogicalOrOfMultipleRangesContext): List<NpmRange?> =
|
||||
ctx.basicRange()
|
||||
.joinToString(AND) { visitBasicRange(it) }
|
||||
.flatMap { visitBasicRange(it) }
|
||||
.onEach { if (it == null) return listOf(null) }
|
||||
.filterNotNull()
|
||||
.fold(listOf<NpmRange>()) { ranges, range ->
|
||||
if (ranges.isEmpty()) listOf(range)
|
||||
else ranges
|
||||
.flatMap { it union range }
|
||||
|
||||
private fun inverseVersion(version: String): String =
|
||||
"$GT$version $OR $LT$version"
|
||||
}
|
||||
|
||||
private fun inverseVersion(version: String): List<NpmRange> =
|
||||
inverseVersion(Version.fromString(version))
|
||||
|
||||
private fun inverseVersion(version: Version): List<NpmRange> =
|
||||
gt(version) union lt(version)
|
||||
|
||||
private fun lt(version: String): NpmRange =
|
||||
lt(Version.fromString(version))
|
||||
|
||||
private fun lt(version: Version): NpmRange =
|
||||
NpmRange(
|
||||
endVersion = version.toSemVer(),
|
||||
endInclusive = false
|
||||
)
|
||||
|
||||
private fun lteq(version: String): NpmRange =
|
||||
lteq(Version.fromString(version))
|
||||
|
||||
private fun lteq(version: Version): NpmRange =
|
||||
NpmRange(
|
||||
endVersion = version.toSemVer(),
|
||||
endInclusive = true
|
||||
)
|
||||
|
||||
private fun gt(version: String): NpmRange =
|
||||
gt(Version.fromString(version))
|
||||
|
||||
private fun gt(version: Version): NpmRange =
|
||||
NpmRange(
|
||||
startVersion = version.toSemVer()
|
||||
)
|
||||
|
||||
private fun gteq(version: String): NpmRange =
|
||||
gteq(Version.fromString(version))
|
||||
|
||||
private fun gteq(version: Version): NpmRange =
|
||||
NpmRange(
|
||||
startVersion = version.toSemVer(),
|
||||
startInclusive = true
|
||||
)
|
||||
|
||||
private fun eq(version: String): NpmRange =
|
||||
eq(Version.fromString(version))
|
||||
|
||||
private fun eq(version: Version): NpmRange =
|
||||
NpmRange(
|
||||
startVersion = version.toSemVer(),
|
||||
startInclusive = true,
|
||||
endVersion = version.toSemVer(),
|
||||
endInclusive = true
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val EQ = "="
|
||||
|
||||
+41
-7
@@ -9,9 +9,9 @@ import com.github.gundy.semver4j.generated.grammar.NodeSemverExpressionLexer
|
||||
import com.github.gundy.semver4j.generated.grammar.NodeSemverExpressionParser
|
||||
import org.antlr.v4.runtime.ANTLRInputStream
|
||||
import org.antlr.v4.runtime.CommonTokenStream
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmVersionInvertedVisitor.Companion.AND
|
||||
import org.gradle.api.InvalidUserDataException
|
||||
|
||||
fun invertNpmVersion(version: String): String {
|
||||
fun versionToNpmRanges(version: String): List<NpmRange?> {
|
||||
val lexer = NodeSemverExpressionLexer(ANTLRInputStream(version))
|
||||
val tokens = CommonTokenStream(lexer)
|
||||
val parser = NodeSemverExpressionParser(tokens)
|
||||
@@ -22,8 +22,42 @@ fun invertNpmVersion(version: String): String {
|
||||
fun buildNpmVersion(
|
||||
includedVersions: List<String>,
|
||||
excludedVersions: List<String>
|
||||
): String =
|
||||
includedVersions
|
||||
.joinToString(AND) { it.trimIndent() } +
|
||||
excludedVersions
|
||||
.joinToString(prefix = AND, separator = AND) { invertNpmVersion(it.trimIndent()) }
|
||||
): String {
|
||||
val includedRange: NpmRange? = try {
|
||||
includedVersions
|
||||
.flatMap { versionToNpmRanges(it) }
|
||||
.onEach {
|
||||
if (it == null) {
|
||||
throw InvalidUserDataException("Problem with parsing of included versions $includedVersions")
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.reduce { acc: NpmRange, next: NpmRange ->
|
||||
val intersection = acc intersect next
|
||||
requireNotNull(intersection) {
|
||||
"Included versions have no intersection $includedVersions"
|
||||
}
|
||||
intersection!!
|
||||
}
|
||||
} catch (e: UnsupportedOperationException) {
|
||||
throw InvalidUserDataException("No ranges for included versions $includedVersions")
|
||||
}
|
||||
|
||||
includedRange!!
|
||||
|
||||
val excludedRanges: List<NpmRange> = try {
|
||||
excludedVersions
|
||||
.flatMap { versionToNpmRanges(it) }
|
||||
.mapNotNull { it?.invert() }
|
||||
.reduce { acc, next -> acc intersect next }
|
||||
} catch (e: UnsupportedOperationException) {
|
||||
throw InvalidUserDataException("No ranges for excluded versions $excludedVersions")
|
||||
}
|
||||
|
||||
return if (excludedRanges.isEmpty()) {
|
||||
includedRange.toString()
|
||||
} else
|
||||
excludedRanges
|
||||
.mapNotNull { it intersect includedRange }
|
||||
.joinToString(NpmVersionInvertedVisitor.OR)
|
||||
}
|
||||
+2
-2
@@ -178,8 +178,8 @@ fun Version.toSemVer(): SemVer =
|
||||
major.toBigInteger(),
|
||||
minor.toBigInteger(),
|
||||
patch.toBigInteger(),
|
||||
preRelease = preReleaseIdentifiers.joinToString("."),
|
||||
build = buildIdentifiers.joinToString(".")
|
||||
preRelease = preReleaseIdentifiers.joinToString(".").let { if (it.isNotEmpty()) it else null },
|
||||
build = buildIdentifiers.joinToString(".").let { if (it.isNotEmpty()) it else null }
|
||||
)
|
||||
|
||||
fun min(a: SemVer?, b: SemVer?): SemVer? {
|
||||
|
||||
Reference in New Issue
Block a user