javascriptnode.jssortingsemantic-versioning

Sorting mixed format version tags in Node.js


I'm attempting to sort a list of tags in order to find the highest tag number. The tags are from a third-party source like Dockerhub, and are in a mixed format (not all semver) i.e.

const tagList = ['0.0.1', '0.0.2', '0.0.3-alpha1', '1.10-alpine3', '1.9.2']

I've been trying to use the compare-versions library but it falls over on non-semantic versions.

I've previously used a python library to sort tags like this:

from packaging.version import LegacyVersion
 
tag_list = ['0.0.1', '0.0.2', '0.0.3-alpha1', '1.10-alpine3', '1.9.2']

sorted_tags = sorted(tag_list, key=LegacyVersion)

Is there any equivalent package for nodejs, or failing that, a way of sorting these tags?

So far, I have only been able to find compare-versions, but this isn't sufficient. I tried the following:

import { compareVersions } from 'compare-versions';

const versions = ['0.0.1', '0.0.2', '0.0.3-alpha1', '1.10-alpine3', '1.9.2'];
let latest;
try {
  latest = versions.sort(compareVersions).pop();
} catch {
  latest = versions.sort((a,b)=>a-b).pop();
};

console.log(latest);

Expected:

1.10-alpine3

Actual Output:

1.9.2

When I remove the try/catch and just force it to use the compareVersions sort function, I get the following error:

/Users/banbone/project/node_modules/compare-versions/lib/umd/index.js:14
            throw new Error(`Invalid argument not valid semver ('${version}' received)`);
                  ^

Error: Invalid argument not valid semver ('1.10-alpine3' received)
    at validateAndParse (/Users/banbone/project/node_modules/compare-versions/lib/umd/index.js:14:19)
    at compareVersions (/Users/banbone/project/node_modules/compare-versions/lib/umd/index.js:53:20)
    at Array.sort (<anonymous>)
    at file:///Users/banbone/project/test.js:5:19
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

Node.js v21.2.0

Solution

  • Version like 1.10-alpine3 violates Semantic Versioning, it must have three numbers and an optional label.

    A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.0 -> 1.10.0 -> 1.11.0.

    Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

    You can use the semver's coerce function to parse the version into a valid semantic version before performing the comparison.

    const semver = require("semver");
    const versions = ["0.0.1", "0.0.2", "0.0.3-alpha1", "1.10-alpine3", "1.9.2"];
    const latest = versions
      .map((x) => ({ origin: x, coerced: semver.coerce(x).toString() }))
      .sort((x, y) => (semver.gt(x.coerced, y.coerced) ? 1 : -1))
      .map((x) => x.origin);
    console.log(latest);
    //[ '0.0.1', '0.0.2', '0.0.3-alpha1', '1.9.2', '1.10-alpine3' ]