diff --git a/src/univers/version_range.py b/src/univers/version_range.py index 399212ad..c62d5b21 100644 --- a/src/univers/version_range.py +++ b/src/univers/version_range.py @@ -357,11 +357,26 @@ def from_native(cls, string): VersionConstraint(comparator=comparator, version=vrc(constraint)) ) else: - if ( - constraint.endswith(".x") - or constraint.startswith("~") - or constraint.startswith("^") - ): + # Handle caret range expression. + if constraint.startswith("^"): + base_version = vrc(constraint.lstrip("^")) + prerelease = base_version.value.prerelease + base_version.value.prerelease = () + if base_version.major: + high = base_version.next_major() + elif base_version.minor: + high = base_version.next_minor() + else: + high = base_version.next_patch() + base_version.value.prerelease = prerelease + lower = base_version + constraints.extend( + [ + VersionConstraint(comparator=">=", version=lower), + VersionConstraint(comparator="<", version=high), + ] + ) + elif constraint.endswith(".x") or constraint.startswith("~"): constraints.extend( get_npm_version_constraints_from_semver_npm_spec( string=constraint, cls=cls diff --git a/tests/data/npm_advisory.json b/tests/data/npm_advisory.json index 9d961e95..6e8e8b58 100644 --- a/tests/data/npm_advisory.json +++ b/tests/data/npm_advisory.json @@ -1845,7 +1845,7 @@ "test_index": 308, "scheme": "npm", "npm_native": "^2.0.18 || ^3.0.16 || ^3.1.6 || ^4.0.8 || ^5.0.0-beta.5", - "expected_vers": "vers:npm/>=2.0.18|<3.0.0|>=3.0.16|>=3.1.6|<4.0.0|<4.0.0|>=4.0.8|>=5.0.0-beta.5|<5.0.0|>=5.0.0|<5.0.1|<6.0.0" + "expected_vers": "vers:npm/>=2.0.18|<3.0.0|>=3.0.16|>=3.1.6|<4.0.0|<4.0.0|>=4.0.8|>=5.0.0-beta.5|<5.0.0|<6.0.0" }, { "test_index": 309, diff --git a/tests/test_version_range.py b/tests/test_version_range.py index 5449acb9..c92de0b7 100644 --- a/tests/test_version_range.py +++ b/tests/test_version_range.py @@ -216,6 +216,39 @@ def test_NpmVersionRange_from_native_with_compatible_with_version_operator(self) version_range = NpmVersionRange.from_native(npm_range) assert version_range == expected + def test_NpmVersionRange_from_native_with_prerelease_carate_range(self): + npm_range = "^1.2.3-beta.1" + expected = NpmVersionRange( + constraints=( + VersionConstraint(comparator=">=", version=SemverVersion(string="1.2.3-beta.1")), + VersionConstraint(comparator="<", version=SemverVersion(string="2.0.0")), + ) + ) + version_range = NpmVersionRange.from_native(npm_range) + assert version_range == expected + + def test_NpmVersionRange_from_native_with_prerelease_carate_range_wihtout_major(self): + npm_range = "^0.2.1-beta" + expected = NpmVersionRange( + constraints=( + VersionConstraint(comparator=">=", version=SemverVersion(string="0.2.1-beta")), + VersionConstraint(comparator="<", version=SemverVersion(string="0.3.0")), + ) + ) + version_range = NpmVersionRange.from_native(npm_range) + assert version_range == expected + + def test_NpmVersionRange_from_native_with_prerelease_carate_range_wihtout_major_and_minor(self): + npm_range = "^0.0.2-beta" + expected = NpmVersionRange( + constraints=( + VersionConstraint(comparator=">=", version=SemverVersion(string="0.0.2-beta")), + VersionConstraint(comparator="<", version=SemverVersion(string="0.0.3")), + ) + ) + version_range = NpmVersionRange.from_native(npm_range) + assert version_range == expected + def test_NpmVersionRange_from_native_with_approximately_equal_to_operator(self): npm_range = "~3.8.2" expected = NpmVersionRange(