@@ -21,7 +21,7 @@ export type SemverTokens = [major: number, minor: number, patch: number]
21
21
* @experimental
22
22
*/
23
23
export class Semver {
24
- private constructor ( public tokens : SemverTokens ) { }
24
+ constructor ( public tokens : SemverTokens ) { }
25
25
26
26
get major ( ) : number {
27
27
return this . tokens [ 0 ]
@@ -33,7 +33,35 @@ export class Semver {
33
33
return this . tokens [ 2 ]
34
34
}
35
35
36
- static of ( input : SemverInput ) : Semver {
36
+ isAfter = ( other : SemverInput ) : boolean => this . cmp ( other ) > 0
37
+ isSameOrAfter = ( other : SemverInput ) : boolean => this . cmp ( other ) >= 0
38
+ isBefore = ( other : SemverInput ) : boolean => this . cmp ( other ) < 0
39
+ isSameOrBefore = ( other : SemverInput ) : boolean => this . cmp ( other ) <= 0
40
+ isSame = ( other : SemverInput ) : boolean => this . cmp ( other ) === 0
41
+
42
+ /**
43
+ * Returns 1 if this > other
44
+ * returns 0 if they are equal
45
+ * returns -1 if this < other
46
+ */
47
+ cmp ( other : SemverInput ) : - 1 | 0 | 1 {
48
+ const { tokens } = semver2 . of ( other )
49
+ for ( let i = 0 ; i < 3 ; i ++ ) {
50
+ if ( this . tokens [ i ] ! < tokens [ i ] ! ) return - 1
51
+ if ( this . tokens [ i ] ! > tokens [ i ] ! ) return 1
52
+ }
53
+ return 0
54
+ }
55
+
56
+ toJSON = ( ) : string => this . toString ( )
57
+
58
+ toString ( ) : string {
59
+ return this . tokens . join ( '.' )
60
+ }
61
+ }
62
+
63
+ class SemverFactory {
64
+ of ( input : SemverInput ) : Semver {
37
65
const s = this . parseOrNull ( input )
38
66
39
67
_assert ( s !== null , `Cannot parse "${ input } " into Semver` , {
@@ -44,7 +72,7 @@ export class Semver {
44
72
return s
45
73
}
46
74
47
- static parseOrNull ( input : SemverInput | undefined | null ) : Semver | null {
75
+ parseOrNull ( input : SemverInput | undefined | null ) : Semver | null {
48
76
if ( ! input ) return null
49
77
if ( input instanceof Semver ) return input
50
78
@@ -55,68 +83,47 @@ export class Semver {
55
83
/**
56
84
* Returns the highest (max) Semver from the array, or undefined if the array is empty.
57
85
*/
58
- static maxOrUndefined ( items : SemverInput [ ] ) : Semver | undefined {
59
- return items . length ? Semver . max ( items ) : undefined
86
+ maxOrUndefined ( items : SemverInput [ ] ) : Semver | undefined {
87
+ return items . length ? this . max ( items ) : undefined
60
88
}
61
89
62
90
/**
63
91
* Returns the highest Semver from the array.
64
92
* Throws if the array is empty.
65
93
*/
66
- static max ( items : SemverInput [ ] ) : Semver {
94
+ max ( items : SemverInput [ ] ) : Semver {
67
95
_assert ( items . length , 'semver.max called on empty array' )
68
96
return items . map ( i => this . of ( i ) ) . reduce ( ( max , item ) => ( max . isSameOrAfter ( item ) ? max : item ) )
69
97
}
70
98
71
99
/**
72
100
* Returns the lowest (min) Semver from the array, or undefined if the array is empty.
73
101
*/
74
- static minOrUndefined ( items : SemverInput [ ] ) : Semver | undefined {
75
- return items . length ? Semver . min ( items ) : undefined
102
+ minOrUndefined ( items : SemverInput [ ] ) : Semver | undefined {
103
+ return items . length ? this . min ( items ) : undefined
76
104
}
77
105
78
106
/**
79
107
* Returns the lowest Semver from the array.
80
108
* Throws if the array is empty.
81
109
*/
82
- static min ( items : SemverInput [ ] ) : Semver {
110
+ min ( items : SemverInput [ ] ) : Semver {
83
111
_assert ( items . length , 'semver.min called on empty array' )
84
112
return items . map ( i => this . of ( i ) ) . reduce ( ( min , item ) => ( min . isSameOrBefore ( item ) ? min : item ) )
85
113
}
114
+ }
86
115
87
- isAfter = ( other : SemverInput ) : boolean => this . cmp ( other ) > 0
88
- isSameOrAfter = ( other : SemverInput ) : boolean => this . cmp ( other ) >= 0
89
- isBefore = ( other : SemverInput ) : boolean => this . cmp ( other ) < 0
90
- isSameOrBefore = ( other : SemverInput ) : boolean => this . cmp ( other ) <= 0
91
- isSame = ( other : SemverInput ) : boolean => this . cmp ( other ) === 0
92
-
93
- /**
94
- * Returns 1 if this > other
95
- * returns 0 if they are equal
96
- * returns -1 if this < other
97
- */
98
- cmp ( other : SemverInput ) : - 1 | 0 | 1 {
99
- const { tokens } = Semver . of ( other )
100
- for ( let i = 0 ; i < 3 ; i ++ ) {
101
- if ( this . tokens [ i ] ! < tokens [ i ] ! ) return - 1
102
- if ( this . tokens [ i ] ! > tokens [ i ] ! ) return 1
103
- }
104
- return 0
105
- }
116
+ interface SemverFn extends SemverFactory {
117
+ ( input : SemverInput ) : Semver
118
+ }
106
119
107
- toJSON = ( ) : string => this . toString ( )
120
+ const semverFactory = new SemverFactory ( )
108
121
109
- toString ( ) : string {
110
- return this . tokens . join ( '.' )
111
- }
112
- }
122
+ export const semver2 = semverFactory . of . bind ( semverFactory ) as SemverFn
113
123
114
- /**
115
- * Shortcut for Semver.of(input)
116
- */
117
- export function _semver ( input : SemverInput ) : Semver {
118
- return Semver . of ( input )
119
- }
124
+ // The line below is the blackest of black magic I have ever written in 2024.
125
+ // And probably 2023 as well.
126
+ Object . setPrototypeOf ( semver2 , semverFactory )
120
127
121
128
/**
122
129
* Returns 1 if a > b
@@ -127,7 +134,7 @@ export function _semver(input: SemverInput): Semver {
127
134
*
128
135
* Credit: https://stackoverflow.com/a/47159772/4919972
129
136
*/
130
- export function _semverCompare ( a : string , b : string ) : - 1 | 0 | 1 {
137
+ export function _quickSemverCompare ( a : string , b : string ) : - 1 | 0 | 1 {
131
138
const t1 = a . split ( '.' )
132
139
const t2 = b . split ( '.' )
133
140
const s1 = _range ( 3 )
0 commit comments