Skip to content

fix: order pre-release by field count when one is a prefix of the other - #81

Open
spokodev wants to merge 1 commit into
omichelsen:mainfrom
spokodev:fix-prerelease-field-count-precedence
Open

fix: order pre-release by field count when one is a prefix of the other#81
spokodev wants to merge 1 commit into
omichelsen:mainfrom
spokodev:fix-prerelease-field-count-precedence

Conversation

@spokodev

Copy link
Copy Markdown

Problem

When one version's pre-release identifier set is a prefix of the other's, they compare as equal instead of the shorter one being lower:

compareVersions('1.0.0-alpha', '1.0.0-alpha.0'); //  0   (expected -1)
compareVersions('1.0.0-rc.1', '1.0.0-rc.1.0');   //  0   (expected -1)

Both inputs in each pair are valid (validate() is true), and node-semver orders them as -1. This affects sort(), satisfies(), and any dedupe that relies on the ordering of adjacent pre-releases.

Cause

src/compareVersions.ts compared pre-release identifiers with compareSegments, which pads the shorter list with '0' (a[i] || '0'). That default is correct for the numeric core — it powers the documented partial-version feature (1.0 == 1.0.0) — but inside a pre-release a missing field must rank lower, regardless of the other side's value. With the '0' padding, ['alpha'] vs ['alpha','0'] becomes ['alpha','0'] vs ['alpha','0'] → equal.

Fix

SemVer 2.0.0 §11.4.4: "A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal." Compare the shared identifiers, then break ties on the field count:

const s1 = p1.split('.');
const s2 = p2.split('.');
for (let i = 0; i < Math.min(s1.length, s2.length); i++) {
  const c = compareSegments([s1[i]], [s2[i]]);
  if (c !== 0) return c;
}
return Math.sign(s1.length - s2.length);

Verification

  • New cases in test/compare.ts (#spec-item-9); fail before, pass after.
  • Full suite: 342 passing, 0 regressions.
  • Differential vs node-semver over 500,000 valid version pairs (pre-releases of varying length, numeric + alphanumeric identifiers): 16 divergences before → 0 after.

`1.0.0-alpha` compared equal to `1.0.0-alpha.0`, and `1.0.0-rc.1` equal to
`1.0.0-rc.1.0`. Pre-release identifiers were compared with `compareSegments`,
which pads the shorter list with `'0'` — correct for the numeric core (the
`1.0` == `1.0.0` partial-version feature) but wrong inside a pre-release,
where a missing field must make the shorter set lower.

Per SemVer 2.0.0 section 11.4.4, when all preceding identifiers are equal the
larger set of pre-release fields has the higher precedence. Compare the shared
identifiers, then break ties on the field count.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant