1
1
import TOML from '@ltd/j-toml'
2
2
import JSZip from 'jszip'
3
3
import yaml from 'js-yaml'
4
+ import { satisfies } from 'semver'
4
5
5
6
export const inferVersionInfo = async function ( rawFile , project , gameVersions ) {
6
7
function versionType ( number ) {
@@ -17,50 +18,87 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
17
18
}
18
19
}
19
20
20
- // TODO: This func does not handle accurate semver parsing. We should eventually
21
- function gameVersionRange ( gameVersionString , gameVersions ) {
22
- if ( ! gameVersionString ) {
21
+ function getGameVersionsMatchingSemverRange ( range , gameVersions ) {
22
+ if ( ! range ) {
23
23
return [ ]
24
24
}
25
+ const ranges = Array . isArray ( range ) ? range : [ range ]
26
+ return gameVersions . filter ( ( version ) => {
27
+ const semverVersion = version . split ( '.' ) . length === 2 ? `${ version } .0` : version // add patch version if missing (e.g. 1.16 -> 1.16.0)
28
+ return ranges . some ( ( v ) => satisfies ( semverVersion , v ) )
29
+ } )
30
+ }
25
31
26
- // Truncate characters after `-` & `+`
27
- const gameString = gameVersionString . replace ( / - | \+ .* $ / g, '' )
28
-
29
- let prefix = ''
30
- if ( gameString . includes ( '~' ) ) {
31
- // Include minor versions
32
- // ~1.2.3 -> 1.2
33
- prefix = gameString . replace ( '~' , '' ) . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' )
34
- } else if ( gameString . includes ( '>=' ) ) {
35
- // Include minor versions
36
- // >=1.2.3 -> 1.2
37
- prefix = gameString . replace ( '>=' , '' ) . split ( '.' ) . slice ( 0 , 2 ) . join ( '.' )
38
- } else if ( gameString . includes ( '^' ) ) {
39
- // Include major versions
40
- // ^1.2.3 -> 1
41
- prefix = gameString . replace ( '^' , '' ) . split ( '.' ) [ 0 ]
42
- } else if ( gameString . includes ( 'x' ) ) {
43
- // Include versions that match `x.x.x`
44
- // 1.2.x -> 1.2
45
- prefix = gameString . replace ( / \. x $ / , '' )
46
- } else {
47
- // Include exact version
48
- // 1.2.3 -> 1.2.3
49
- prefix = gameString
32
+ function getGameVersionsMatchingMavenRange ( range , gameVersions ) {
33
+ if ( ! range ) {
34
+ return [ ]
35
+ }
36
+ const ranges = [ ]
37
+
38
+ while ( range . startsWith ( '[' ) || range . startsWith ( '(' ) ) {
39
+ let index = range . indexOf ( ')' )
40
+ const index2 = range . indexOf ( ']' )
41
+ if ( index === - 1 || ( index2 !== - 1 && index2 < index ) ) {
42
+ index = index2
43
+ }
44
+ if ( index === - 1 ) break
45
+ ranges . push ( range . substring ( 0 , index + 1 ) )
46
+ range = range . substring ( index + 1 ) . trim ( )
47
+ if ( range . startsWith ( ',' ) ) {
48
+ range = range . substring ( 1 ) . trim ( )
49
+ }
50
50
}
51
51
52
- const simplified = gameVersions
53
- . filter ( ( it ) => it . version_type === 'release' )
54
- . map ( ( it ) => it . version )
55
- return simplified . filter ( ( version ) => version . startsWith ( prefix ) )
52
+ if ( range ) {
53
+ ranges . push ( range )
54
+ }
55
+
56
+ const LESS_THAN_EQUAL = / ^ \( , ( .* ) ] $ /
57
+ const LESS_THAN = / ^ \( , ( .* ) \) $ /
58
+ const EQUAL = / ^ \[ ( .* ) ] $ /
59
+ const GREATER_THAN_EQUAL = / ^ \[ ( .* ) , \) $ /
60
+ const GREATER_THAN = / ^ \( ( .* ) , \) $ /
61
+ const BETWEEN = / ^ \( ( .* ) , ( .* ) \) $ /
62
+ const BETWEEN_EQUAL = / ^ \[ ( .* ) , ( .* ) ] $ /
63
+ const BETWEEN_LESS_THAN_EQUAL = / ^ \( ( .* ) , ( .* ) ] $ /
64
+ const BETWEEN_GREATER_THAN_EQUAL = / ^ \[ ( .* ) , ( .* ) \) $ /
65
+
66
+ const semverRanges = [ ]
67
+
68
+ for ( const range of ranges ) {
69
+ let result
70
+ if ( ( result = range . match ( LESS_THAN_EQUAL ) ) ) {
71
+ semverRanges . push ( `<=${ result [ 1 ] } ` )
72
+ } else if ( ( result = range . match ( LESS_THAN ) ) ) {
73
+ semverRanges . push ( `<${ result [ 1 ] } ` )
74
+ } else if ( ( result = range . match ( EQUAL ) ) ) {
75
+ semverRanges . push ( `${ result [ 1 ] } ` )
76
+ } else if ( ( result = range . match ( GREATER_THAN_EQUAL ) ) ) {
77
+ semverRanges . push ( `>=${ result [ 1 ] } ` )
78
+ } else if ( ( result = range . match ( GREATER_THAN ) ) ) {
79
+ semverRanges . push ( `>${ result [ 1 ] } ` )
80
+ } else if ( ( result = range . match ( BETWEEN ) ) ) {
81
+ semverRanges . push ( `>${ result [ 1 ] } <${ result [ 2 ] } ` )
82
+ } else if ( ( result = range . match ( BETWEEN_EQUAL ) ) ) {
83
+ semverRanges . push ( `>=${ result [ 1 ] } <=${ result [ 2 ] } ` )
84
+ } else if ( ( result = range . match ( BETWEEN_LESS_THAN_EQUAL ) ) ) {
85
+ semverRanges . push ( `>${ result [ 1 ] } <=${ result [ 2 ] } ` )
86
+ } else if ( ( result = range . match ( BETWEEN_GREATER_THAN_EQUAL ) ) ) {
87
+ semverRanges . push ( `>=${ result [ 1 ] } <${ result [ 2 ] } ` )
88
+ }
89
+ }
90
+ return getGameVersionsMatchingSemverRange ( semverRanges , gameVersions )
56
91
}
57
92
93
+ const simplifiedGameVersions = gameVersions
94
+ . filter ( ( it ) => it . version_type === 'release' )
95
+ . map ( ( it ) => it . version )
96
+
58
97
const inferFunctions = {
59
98
// Forge 1.13+
60
99
'META-INF/mods.toml' : async ( file , zip ) => {
61
100
const metadata = TOML . parse ( file , { joiner : '\n' } )
62
101
63
- // TODO: Parse minecraft version ranges
64
102
if ( metadata . mods && metadata . mods . length > 0 ) {
65
103
let versionNum = metadata . mods [ 0 ] . version
66
104
@@ -80,11 +118,23 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
80
118
}
81
119
}
82
120
121
+ let gameVersions = [ ]
122
+ const mcDependencies = Object . values ( metadata . dependencies )
123
+ . flat ( )
124
+ . filter ( ( dependency ) => dependency . modId === 'minecraft' )
125
+ if ( mcDependencies . length > 0 ) {
126
+ gameVersions = getGameVersionsMatchingMavenRange (
127
+ mcDependencies [ 0 ] . versionRange ,
128
+ simplifiedGameVersions
129
+ )
130
+ }
131
+
83
132
return {
84
133
name : `${ project . title } ${ versionNum } ` ,
85
134
version_number : versionNum ,
86
135
version_type : versionType ( versionNum ) ,
87
136
loaders : [ 'forge' ] ,
137
+ game_versions : gameVersions ,
88
138
}
89
139
} else {
90
140
return { }
@@ -99,9 +149,9 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
99
149
version_number : metadata . version ,
100
150
version_type : versionType ( metadata . version ) ,
101
151
loaders : [ 'forge' ] ,
102
- game_versions : gameVersions
103
- . filter ( ( x ) => x . version . startsWith ( metadata . mcversion ) && x . version_type === 'release' )
104
- . map ( ( x ) => x . version ) ,
152
+ game_versions : simplifiedGameVersions . filter ( ( version ) =>
153
+ version . startsWith ( metadata . mcversion )
154
+ ) ,
105
155
}
106
156
} ,
107
157
// Fabric
@@ -114,7 +164,7 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
114
164
loaders : [ 'fabric' ] ,
115
165
version_type : versionType ( metadata . version ) ,
116
166
game_versions : metadata . depends
117
- ? gameVersionRange ( metadata . depends . minecraft , gameVersions )
167
+ ? getGameVersionsMatchingSemverRange ( metadata . depends . minecraft , simplifiedGameVersions )
118
168
: [ ] ,
119
169
}
120
170
} ,
@@ -128,11 +178,11 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
128
178
loaders : [ 'quilt' ] ,
129
179
version_type : versionType ( metadata . quilt_loader . version ) ,
130
180
game_versions : metadata . quilt_loader . depends
131
- ? gameVersionRange (
181
+ ? getGameVersionsMatchingSemverRange (
132
182
metadata . quilt_loader . depends . find ( ( x ) => x . id === 'minecraft' )
133
183
? metadata . quilt_loader . depends . find ( ( x ) => x . id === 'minecraft' ) . versions
134
184
: [ ] ,
135
- gameVersions
185
+ simplifiedGameVersions
136
186
)
137
187
: [ ] ,
138
188
}
0 commit comments