UNPKG

@naturalcycles/js-lib

Version:

Standard library for universal (browser + Node.js) javascript

156 lines (155 loc) 4.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.semver2 = exports.Semver = void 0; exports._quickSemverCompare = _quickSemverCompare; const range_1 = require("./array/range"); const assert_1 = require("./error/assert"); /** * Simple Semver implementation. * * Suitable for Browser usage, unlike npm `semver` which is Node-targeted and simply too big for the Browser. * * Parsing algorithm is simple: * 1. Split by `.` * 2. parseInt each of 3 tokens, set to 0 if falsy * * toString returns `major.minor.patch` * Missing tokens are replaced with 0. * * _semver('1').toString() === '1.0.0' * * @experimental */ class Semver { tokens; constructor(tokens) { this.tokens = tokens; } get major() { return this.tokens[0]; } get minor() { return this.tokens[1]; } get patch() { return this.tokens[2]; } isAfter = (other) => this.compare(other) > 0; isSameOrAfter = (other) => this.compare(other) >= 0; isBefore = (other) => this.compare(other) < 0; isSameOrBefore = (other) => this.compare(other) <= 0; isSame = (other) => this.compare(other) === 0; /** * Returns 1 if this > other * returns 0 if they are equal * returns -1 if this < other */ compare(other) { const { tokens } = exports.semver2.fromInput(other); for (let i = 0; i < 3; i++) { if (this.tokens[i] < tokens[i]) return -1; if (this.tokens[i] > tokens[i]) return 1; } return 0; } toJSON = () => this.toString(); toString() { return this.tokens.join('.'); } } exports.Semver = Semver; class SemverFactory { fromInput(input) { const s = this.fromInputOrUndefined(input); (0, assert_1._assert)(s, `Cannot parse "${input}" into Semver`, { input, }); return s; } fromInputOrUndefined(input) { if (!input) return; if (input instanceof Semver) return input; const t = input.split('.'); return new Semver((0, range_1._range)(3).map(i => parseInt(t[i]) || 0)); } /** * Returns the highest (max) Semver from the array, or undefined if the array is empty. */ maxOrUndefined(items) { let max; for (const item of items) { const input = this.fromInputOrUndefined(item); if (!max || input?.isAfter(max)) { max = input; } } return max; } /** * Returns the highest Semver from the array. * Throws if the array is empty. */ max(items) { const max = this.maxOrUndefined(items); (0, assert_1._assert)(max, 'semver.max called on empty array'); return max; } /** * Returns the lowest (min) Semver from the array, or undefined if the array is empty. */ minOrUndefined(items) { let min; for (const item of items) { const input = this.fromInputOrUndefined(item); if (!min || input?.isBefore(min)) { min = input; } } return min; } /** * Returns the lowest Semver from the array. * Throws if the array is empty. */ min(items) { const min = this.minOrUndefined(items); (0, assert_1._assert)(min, 'semver.min called on empty array'); return min; } /** * Sorts an array of Semvers in `dir` order (ascending by default). */ sort(items, dir = 'asc', mutate = false) { const mod = dir === 'desc' ? -1 : 1; return (mutate ? items : [...items]).sort((a, b) => a.compare(b) * mod); } } const semverFactory = new SemverFactory(); exports.semver2 = semverFactory.fromInput.bind(semverFactory); // The line below is the blackest of black magic I have ever written in 2024. // And probably 2023 as well. Object.setPrototypeOf(exports.semver2, semverFactory); /** * Returns 1 if a > b * returns 0 if they are equal * returns -1 if a < b * * Quick&dirty implementation, which should suffice for 95% of the cases. * * Credit: https://stackoverflow.com/a/47159772/4919972 */ function _quickSemverCompare(a, b) { const t1 = a.split('.'); const t2 = b.split('.'); const s1 = (0, range_1._range)(3) .map(i => (t1[i] || '').padStart(5)) .join(''); const s2 = (0, range_1._range)(3) .map(i => (t2[i] || '').padStart(5)) .join(''); return s1 < s2 ? -1 : s1 > s2 ? 1 : 0; }