[Gradle, JS] Use not inverted visitor but straightforward visitor

^KT-41054 fixed
This commit is contained in:
Ilya Goncharov
2020-08-05 14:44:33 +03:00
parent 9bb49ac370
commit 966c9dae68
5 changed files with 189 additions and 47 deletions
@@ -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,
@@ -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)
}
}
@@ -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 = "="
@@ -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)
}
@@ -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? {