@@ -69,20 +69,12 @@ export function satisfy(version: string, range: string): boolean {
6969 return false ;
7070 }
7171
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
8073 const extractedVersion = extractComparator ( version ) ;
81-
8274 if ( ! extractedVersion ) {
75+ // If the version string is invalid, it can't satisfy any range
8376 return false ;
8477 }
85-
8678 const [
8779 ,
8880 versionOperator ,
@@ -106,42 +98,113 @@ export function satisfy(version: string, range: string): boolean {
10698 preRelease : versionPreRelease ?. split ( '.' ) ,
10799 } ;
108100
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 ( '||' ) ;
111103
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 ;
114110 }
115111
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 ;
141203 }
142204 }
143205
144- return true ;
206+ // If none of the OR sub-ranges were satisfied
207+ return false ;
145208}
146209
147210export function isLegallyVersion ( version : string ) : boolean {
0 commit comments