-
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(compare): refactor getHighestVersion
- Loading branch information
1 parent
67deec7
commit 5c41df8
Showing
6 changed files
with
110 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 42 additions & 33 deletions
75
src/get-context/get-groups/version-group/instance-group/get-highest-version.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,48 @@ | ||
import coerce from 'semver/functions/coerce'; | ||
import eq from 'semver/functions/eq'; | ||
import gt from 'semver/functions/gt'; | ||
import valid from 'semver/functions/valid'; | ||
import { RANGE } from '../../../../constants'; | ||
import { isSemver } from '../../../../lib/is-semver'; | ||
import { R } from '@mobily/ts-belt'; | ||
import { BaseError } from '../../../../lib/error'; | ||
import { clean } from './lib/clean'; | ||
import { compareSemver } from './lib/compare-semver'; | ||
import { getRangeScore } from './lib/get-range-score'; | ||
|
||
export function getHighestVersion(versions: string[]): string { | ||
return versions.reduce<string>((rawHighest, raw) => { | ||
const version = valid(coerce(raw)) || ''; | ||
const highest = valid(coerce(rawHighest)) || ''; | ||
if (raw === '*' || rawHighest === '*') return '*'; | ||
if (!isSemver(raw) || version === '') return rawHighest; | ||
if (highest === '') return raw; | ||
if (gt(version, highest)) return raw; | ||
if (eq(version, highest) && getRangeScore(raw) > getRangeScore(rawHighest)) | ||
return raw; | ||
return rawHighest; | ||
}, ''); | ||
interface HighestVersion { | ||
withRange: string; | ||
semver: string; | ||
} | ||
|
||
function getRangeScore(version: string): number { | ||
if (version === '') return 0; | ||
if (version === RANGE.ANY) return 8; | ||
const range = getRange(version); | ||
if (range === RANGE.GT) return 7; | ||
if (range === RANGE.GTE) return 6; | ||
if (range === RANGE.MINOR) return 5; | ||
if (version.indexOf('.x') !== -1) return 4; | ||
if (range === RANGE.PATCH) return 3; | ||
if (range === RANGE.EXACT) return 2; | ||
if (range === RANGE.LTE) return 1; | ||
if (range === RANGE.LT) return 0; | ||
return 0; | ||
export function getHighestVersion( | ||
versions: string[], | ||
): R.Result<string, BaseError> { | ||
let highest: HighestVersion | undefined; | ||
|
||
for (const withRange of versions) { | ||
switch (compareSemver(withRange, highest?.semver)) { | ||
// highest possible, quit early | ||
case '*': { | ||
return R.Ok(withRange); | ||
} | ||
// impossible to know how the user wants to resolve unsupported versions | ||
case 'invalid': { | ||
return R.Error(new BaseError(`"${withRange}" is not supported`)); | ||
} | ||
// we found a new highest version | ||
case 'gt': { | ||
highest = newHighestVersion(withRange); | ||
continue; | ||
} | ||
// versions are the same, but one range might be greedier than another | ||
case 'eq': { | ||
const score = getRangeScore(withRange); | ||
const highestScore = getRangeScore(`${highest?.withRange}`); | ||
if (score > highestScore) highest = newHighestVersion(withRange); | ||
} | ||
} | ||
} | ||
|
||
return highest && highest.withRange | ||
? R.Ok(highest.withRange) | ||
: R.Error(new BaseError(`getHighestVersion(): did not return a version`)); | ||
} | ||
|
||
function getRange(version: string): string { | ||
return version.slice(0, version.search(/[0-9]/)); | ||
function newHighestVersion(withRange: string): HighestVersion { | ||
return { withRange, semver: clean(withRange) }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/get-context/get-groups/version-group/instance-group/lib/clean.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import coerce from 'semver/functions/coerce'; | ||
import valid from 'semver/functions/valid'; | ||
|
||
/** Convert eg "1" to "1.0.0" which the semver lib does not understand */ | ||
export function clean(v: string): string { | ||
return valid(coerce(v)) || ''; | ||
} |
15 changes: 15 additions & 0 deletions
15
src/get-context/get-groups/version-group/instance-group/lib/compare-semver.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import gt from 'semver/functions/gt'; | ||
import { isSemver } from '../../../../../lib/is-semver'; | ||
import { clean } from './clean'; | ||
|
||
/** Is this next version to be inspected higher than the current highest? */ | ||
export function compareSemver( | ||
next: string, | ||
highest: string | undefined, | ||
): '*' | 'invalid' | 'gt' | 'lt' | 'eq' { | ||
if (next === '*') return '*'; | ||
if (!isSemver(next)) return 'invalid'; | ||
if (!highest || gt(clean(next), highest)) return 'gt'; | ||
if (gt(clean(next), highest)) return 'lt'; | ||
return 'eq'; | ||
} |
22 changes: 22 additions & 0 deletions
22
src/get-context/get-groups/version-group/instance-group/lib/get-range-score.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { RANGE } from '../../../../../constants'; | ||
|
||
const scoresByRange: Record<string, number | undefined> = { | ||
[RANGE.ANY]: 8, | ||
[RANGE.GT]: 7, | ||
[RANGE.GTE]: 6, | ||
[RANGE.MINOR]: 5, | ||
[RANGE.LOOSE]: 4, | ||
[RANGE.PATCH]: 3, | ||
[RANGE.EXACT]: 2, | ||
[RANGE.LTE]: 1, | ||
[RANGE.LT]: 0, | ||
}; | ||
|
||
/** Rank a Semver Range according to its greediness */ | ||
export function getRangeScore(version: string): number { | ||
const range = | ||
version.indexOf('.x') !== -1 | ||
? RANGE.LOOSE | ||
: version.slice(0, version.search(/[0-9]/)); | ||
return scoresByRange[range] || 0; | ||
} |