@@ -69,20 +69,12 @@ export function satisfy(version: string, range: string): boolean {
69
69
return false ;
70
70
}
71
71
72
- const parsedRange = parseRange ( range ) ;
73
- const parsedComparator = parsedRange
74
- . split ( ' ' )
75
- . map ( ( rangeVersion ) => parseComparatorString ( rangeVersion ) )
76
- . join ( ' ' ) ;
77
- const comparators = parsedComparator
78
- . split ( / \s + / )
79
- . map ( ( comparator ) => parseGTE0 ( comparator ) ) ;
72
+ // Extract version details once
80
73
const extractedVersion = extractComparator ( version ) ;
81
-
82
74
if ( ! extractedVersion ) {
75
+ // If the version string is invalid, it can't satisfy any range
83
76
return false ;
84
77
}
85
-
86
78
const [
87
79
,
88
80
versionOperator ,
@@ -106,42 +98,113 @@ export function satisfy(version: string, range: string): boolean {
106
98
preRelease : versionPreRelease ?. split ( '.' ) ,
107
99
} ;
108
100
109
- for ( const comparator of comparators ) {
110
- const extractedComparator = extractComparator ( comparator ) ;
101
+ // Split the range by || to handle OR conditions
102
+ const orRanges = range . split ( '||' ) ;
111
103
112
- if ( ! extractedComparator ) {
113
- return false ;
104
+ for ( const orRange of orRanges ) {
105
+ const trimmedOrRange = orRange . trim ( ) ;
106
+ if ( ! trimmedOrRange ) {
107
+ // An empty range string signifies wildcard *, satisfy any valid version
108
+ // (We already checked if the version itself is valid)
109
+ return true ;
114
110
}
115
111
116
- const [
117
- ,
118
- rangeOperator ,
119
- ,
120
- rangeMajor ,
121
- rangeMinor ,
122
- rangePatch ,
123
- rangePreRelease ,
124
- ] = extractedComparator ;
125
- const rangeAtom : CompareAtom = {
126
- operator : rangeOperator ,
127
- version : combineVersion (
128
- rangeMajor ,
129
- rangeMinor ,
130
- rangePatch ,
131
- rangePreRelease ,
132
- ) , // exclude build atom
133
- major : rangeMajor ,
134
- minor : rangeMinor ,
135
- patch : rangePatch ,
136
- preRelease : rangePreRelease ?. split ( '.' ) ,
137
- } ;
138
-
139
- if ( ! compare ( rangeAtom , versionAtom ) ) {
140
- return false ; // early return
112
+ // Handle simple wildcards explicitly before complex parsing
113
+ if ( trimmedOrRange === '*' || trimmedOrRange === 'x' ) {
114
+ return true ;
115
+ }
116
+
117
+ try {
118
+ // Apply existing parsing logic to the current OR sub-range
119
+ const parsedSubRange = parseRange ( trimmedOrRange ) ; // Handles hyphens, trims etc.
120
+
121
+ // Check if the result of initial parsing is empty, which can happen
122
+ // for some wildcard cases handled by parseRange/parseComparatorString.
123
+ // E.g. `parseStar` used in `parseComparatorString` returns ''.
124
+ if ( ! parsedSubRange . trim ( ) ) {
125
+ // If parsing results in empty string, treat as wildcard match
126
+ return true ;
127
+ }
128
+
129
+ const parsedComparatorString = parsedSubRange
130
+ . split ( ' ' )
131
+ . map ( ( rangeVersion ) => parseComparatorString ( rangeVersion ) ) // Expands ^, ~
132
+ . join ( ' ' ) ;
133
+
134
+ // Check again if the comparator string became empty after specific parsing like ^ or ~
135
+ if ( ! parsedComparatorString . trim ( ) ) {
136
+ return true ;
137
+ }
138
+
139
+ // Split the sub-range by space for implicit AND conditions
140
+ const comparators = parsedComparatorString
141
+ . split ( / \s + / )
142
+ . map ( ( comparator ) => parseGTE0 ( comparator ) )
143
+ // Filter out empty strings that might result from multiple spaces
144
+ . filter ( Boolean ) ;
145
+
146
+ // If a sub-range becomes empty after parsing (e.g., invalid characters),
147
+ // it cannot be satisfied. This check might be redundant now but kept for safety.
148
+ if ( comparators . length === 0 ) {
149
+ continue ;
150
+ }
151
+
152
+ let subRangeSatisfied = true ;
153
+ for ( const comparator of comparators ) {
154
+ const extractedComparator = extractComparator ( comparator ) ;
155
+
156
+ // If any part of the AND sub-range is invalid, the sub-range is not satisfied
157
+ if ( ! extractedComparator ) {
158
+ subRangeSatisfied = false ;
159
+ break ;
160
+ }
161
+
162
+ const [
163
+ ,
164
+ rangeOperator ,
165
+ ,
166
+ rangeMajor ,
167
+ rangeMinor ,
168
+ rangePatch ,
169
+ rangePreRelease ,
170
+ ] = extractedComparator ;
171
+ const rangeAtom : CompareAtom = {
172
+ operator : rangeOperator ,
173
+ version : combineVersion (
174
+ rangeMajor ,
175
+ rangeMinor ,
176
+ rangePatch ,
177
+ rangePreRelease ,
178
+ ) ,
179
+ major : rangeMajor ,
180
+ minor : rangeMinor ,
181
+ patch : rangePatch ,
182
+ preRelease : rangePreRelease ?. split ( '.' ) ,
183
+ } ;
184
+
185
+ // Check if the version satisfies this specific comparator in the AND chain
186
+ if ( ! compare ( rangeAtom , versionAtom ) ) {
187
+ subRangeSatisfied = false ; // This part of the AND condition failed
188
+ break ; // No need to check further comparators in this sub-range
189
+ }
190
+ }
191
+
192
+ // If all AND conditions within this OR sub-range were met, the overall range is satisfied
193
+ if ( subRangeSatisfied ) {
194
+ return true ;
195
+ }
196
+ } catch ( e ) {
197
+ // Log error and treat this sub-range as unsatisfied
198
+ console . error (
199
+ `[semver] Error processing range part "${ trimmedOrRange } ":` ,
200
+ e ,
201
+ ) ;
202
+ continue ;
141
203
}
142
204
}
143
205
144
- return true ;
206
+ // If none of the OR sub-ranges were satisfied
207
+ return false ;
145
208
}
146
209
147
210
export function isLegallyVersion ( version : string ) : boolean {
0 commit comments