diff --git a/lib/match-conditions.js b/lib/match-conditions.js index 3e98e158..bbc83562 100644 --- a/lib/match-conditions.js +++ b/lib/match-conditions.js @@ -50,6 +50,12 @@ export class ConditionMatcher { } isStringMatch(conditions) { + // "a && b" — split on && and require every part to match. + if (conditions.includes('&&')) { + const parts = conditions.split('&&').map((s) => s.trim()); + if (parts.some((part) => part === '')) return false; + return parts.every((part) => this.isStringMatch(part)); + } if ( semver.validRange(conditions) && semver.satisfies(this.semVersion, conditions) diff --git a/test/test-match-conditions.js b/test/test-match-conditions.js index a710e319..197553a8 100644 --- a/test/test-match-conditions.js +++ b/test/test-match-conditions.js @@ -128,5 +128,41 @@ test('isMatch', (t) => { t.ok(isMatch(true), 'true is matched'); t.notOk(isMatch(false), 'false is not matched'); t.notOk(isMatch(123), 'invalid input is not matched'); + t.ok(isMatch('darwin && v5'), 'isMatch delegates && string to isStringMatch'); + t.notOk( + isMatch('darwin && hurd'), + 'isMatch delegates partial && string -> false' + ); + t.end(); +}); + +test('isStringMatch - && malformed', (t) => { + t.notOk( + testMatcher.isStringMatch('darwin &&'), + 'trailing && produces empty part -> false' + ); + t.notOk( + testMatcher.isStringMatch('&& darwin'), + 'leading && produces empty part -> false' + ); + t.notOk(testMatcher.isStringMatch('&&'), 'bare && -> false'); + t.notOk( + testMatcher.isStringMatch('darwin && && v5'), + 'repeated && produces empty middle part -> false' + ); + t.end(); +}); + +test('isStringMatch - && syntax with semver range: aix && >=26', (t) => { + const testMatcher = new ConditionMatcher({ + version: 'v26.0.0', + platform: 'aix', + arch: 'ppc64', + distro: '', + release: '', + endian: 'big', + fips: '' + }); + t.ok(testMatcher.isStringMatch('aix && >=26'), 'aix + v26 -> true'); t.end(); });