diff --git a/src/constants.mjs b/src/constants.mjs index 07c5753..94ff706 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -1,12 +1,3 @@ -export const STYLE_SEMVER = 'semver' -export const STYLE_TIMEVER = 'timever' -export const STYLE_AUTO = 'auto' -export const TIMEVER_REGEX = /(\d{8})[.-](\d{6})Z(?:-(alpha|beta|rc)\.(\d)+)?$/ -export const TIMEVER_DATE_POS = 1 -export const TIMEVER_TIME_POS = 2 -export const TIMEVER_PRETYPE_POS = 3 -export const TIMEVER_PREVER_POS = 4 - export const xRangeRE = // v single tuple v doublpe tuple v triple tuple v quad/pre-release tuple // v if there's more than a single digit, it can't lead with a zero diff --git a/src/index.js b/src/index.js index 0313c42..355fc0a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,9 @@ export * from './ext-constants' export * from './filter-valid-version-or-range' export * from './first-past' -export * from './is-time-version' export * from './min-version' export * from './next-version' export * from './prerelease' export * from './valid-version-or-range' export * from './version-compare' -export * from './version-style' export * from './x-sort' diff --git a/src/is-time-version.mjs b/src/is-time-version.mjs deleted file mode 100644 index 5d97d04..0000000 --- a/src/is-time-version.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import { TIMEVER_REGEX } from './constants' - -const isTimeVersion = (version) => { - return !!version.match(TIMEVER_REGEX) -} - -export { isTimeVersion } diff --git a/src/next-version.mjs b/src/next-version.mjs index 74a7894..b648b13 100644 --- a/src/next-version.mjs +++ b/src/next-version.mjs @@ -1,32 +1,11 @@ import createError from 'http-errors' import semver from 'semver' -import { STYLE_AUTO, STYLE_SEMVER, STYLE_TIMEVER } from './constants' import { increments, prereleaseIncrements, releaseTypes } from './ext-constants' -import { versionStyle } from './version-style' - -const makeTS = ({ date = new Date() } = {}) => { - const timestamp = date.getUTCFullYear() - + (date.getUTCMonth() + 1 + '').padStart(2, '0') - + (date.getUTCDate() + '').padStart(2, '0') - + '.' - + (date.getUTCHours() + '').padStart(2, '0') - + (date.getUTCMinutes() + '').padStart(2, '0') - + (date.getUTCSeconds() + '').padStart(2, '0') - + 'Z' - - return timestamp -} /** - * Given a current version, increment, and optional style, generates the next version string. The version style is - * typically identified automatically but can be specified explicitly (if switching styles, for example). Supports - * 'semver' and 'timever' style. See [semver specification](https://semver.org/). Timever style consists of: - * - a major version integer (indicating compatibility series) - * - a UTC timestamp in the form of 'YYYYMMDD.HHMMSSZ' - * - the timestamp is generated during the execution of this method. - * - * For semver style, this method follows the [semver spec](https://semver.org) except that: + * Given a current version, increment generates the next version string. This method follows the + * [semver spec](https://semver.org) except that: * - `increment` 'prerelease' cannot be used with non-prerelease versions (results in execption) * - supports 'pretype' and specific pre-release sequence 'alpha' -> 'beta' -> 'rc' -> released * @@ -37,7 +16,7 @@ const makeTS = ({ date = new Date() } = {}) => { * means to increment the pre-release ID from 'alpha' -> 'beta' -> 'rc' -> none. Passing in a `currVer` with any * other prerelease ID with a 'pretype' increment will result in undefined results */ -const nextVersion = ({ currVer, date, increment, loose = false, style = STYLE_AUTO }) => { +const nextVersion = ({ currVer, increment, loose = false }) => { if (increment !== undefined && !increments.includes(increment)) { throw createError.BadRequest(`Invalid increment '${increment}' specified.`) } @@ -65,21 +44,12 @@ const nextVersion = ({ currVer, date, increment, loose = false, style = STYLE_AU } } - if (style === STYLE_AUTO || style === undefined) { - style = versionStyle(currVer) - } - // alpha -> beta -> rc if (increment === 'pretype') { if (currPrerelease === 'alpha') nextVer = currVer.replace(/([0-1.Z-]+)alpha(\.\d+)?/, '$1beta.0') else if (currPrerelease === 'beta') nextVer = currVer.replace(/([0-1.Z-]+)beta(\.\d+)?/, '$1rc.0') else if (currPrerelease === 'rc') { // is special in the case of timever + pretype - if (style === STYLE_SEMVER) { - nextVer = currVer.replace(/([0-9.Z]+)-rc(?:\.\d+)?/, '$1') - } - else { - nextVer = currVer.replace(/(\d+)\.\d{8}\.\d{6}Z-rc\.\d+/, '$1.' + makeTS()) - } + nextVer = currVer.replace(/([0-9.Z]+)-rc(?:\.\d+)?/, '$1') } return nextVer // we're done @@ -94,49 +64,21 @@ const nextVersion = ({ currVer, date, increment, loose = false, style = STYLE_AU } if (increment === 'gold') { - if (style === STYLE_SEMVER) { - nextVer = currVer.replace(/^([\d.Z]+)-(?:alpha|beta|rc)(?:\.\d+)?/, '$1') - } - else { // style === STYLE_TIMEVER - nextVer = currVer.replace(/(\d+)\.\d{8}\.\d{6}Z-(?:alpha|beta|rc)\.\d+/, '$1.' + makeTS()) - } + nextVer = currVer.replace(/^([\d.Z]+)-(?:alpha|beta|rc)(?:\.\d+)?/, '$1') } - else if (style === STYLE_SEMVER) { + else { nextVer = currVer.replace(/^([\d.Z-]+)(?:alpha|beta|rc)(?:\.\d+)?/, `$1${increment}.0`) } - else { // style === STYLE_TIMEVER - nextVer = currVer.replace(/(\d+)\.\d{8}\.\d{6}Z-(?:alpha|beta|rc)\.\d+/, '$1.' + makeTS() + increment + '.0') - } return nextVer } - if (style === STYLE_SEMVER) { - // if we're going 'pre', but currVer is a pre-style, then we need to specify the first stage in the pre, aka, alpha - nextVer = increment.startsWith('pre') && currVer.match(/^[\d.Z-]+$/) - ? semver.inc(currVer, increment, 'alpha') - : semver.inc(currVer, increment) - } - else { // it's 'timever' style - const timestamp = makeTS({ date }) - - const [currMajor] = currVer.split('.') - const nextMajor = increment === 'major' || increment === 'premajor' ? '' + (parseInt(currMajor) + 1) : currMajor - if (currPrerelease === null && increment.startsWith('pre')) { - nextVer = nextMajor + '.' + timestamp + '-alpha.0' - } - else if (currPrerelease !== null && increment === 'prerelease') { - const currPreReleaseString = (currVer.match(/(?:alpha|beta|rc)\.(\d+)$/) || [null, null])[1] - const currPreRelease = parseInt(currPreReleaseString) - const currBare = currVer.replace(/\d+$/, '') - nextVer = currBare + (currPreRelease + 1) - } - else { - nextVer = nextMajor + '.' + timestamp + (currPrerelease ? '-' + currPrerelease : '') - } - } + // if we're going 'pre', but currVer is a pre-style, then we need to specify the first stage in the pre, aka, alpha + nextVer = increment.startsWith('pre') && currVer.match(/^[\d.Z-]+$/) + ? semver.inc(currVer, increment, 'alpha') + : semver.inc(currVer, increment) return nextVer } -export { makeTS, nextVersion, STYLE_AUTO, STYLE_TIMEVER, STYLE_SEMVER } +export { nextVersion } diff --git a/src/prerelease.mjs b/src/prerelease.mjs index d0af7c6..2f8c86d 100644 --- a/src/prerelease.mjs +++ b/src/prerelease.mjs @@ -1,20 +1,7 @@ import semver from 'semver' -import { TIMEVER_REGEX, TIMEVER_PRETYPE_POS, TIMEVER_PREVER_POS } from './constants' -import { isTimeVersion } from './is-time-version' - const prerelease = (version) => { - if (isTimeVersion(version)) { - const match = version.match(TIMEVER_REGEX) - const preType = match[TIMEVER_PRETYPE_POS] - if (preType !== undefined) { - return [preType, parseInt(match[TIMEVER_PREVER_POS])] - } - else return null - } - else { - return semver.prerelease(version) - } + return semver.prerelease(version) } export { prerelease } diff --git a/src/test/next-version.test.js b/src/test/next-version.test.js index a6181d7..b41880d 100644 --- a/src/test/next-version.test.js +++ b/src/test/next-version.test.js @@ -1,15 +1,7 @@ -/* global beforeAll describe expect test */ +/* global describe expect test */ import * as ver from '../next-version' -describe('makeTS', () => { - test('Jan 3, 2023 0513.25 => 20230303.051325Z', () => { - const date = new Date('2023-03-03T05:13:25.000Z') - - expect(ver.makeTS({ date })).toBe('20230303.051325Z') - }) -}) - describe('nextVersion', () => { describe('handles semver style', () => { test.each([ @@ -68,132 +60,4 @@ describe('nextVersion', () => { const currVer = `1.0.0-${currPrerelease}` expect(() => ver.version({ currVer, increment : 'prerelease' })) }) - - describe('handles timever style', () => { - let preTS, resultReleased, resultPreInit, resultPreOngoing, resultPretype, postTS - const startTimeVer = '1.20230103.051325Z' - const testReleased = [ - [startTimeVer, undefined, '1'], - [startTimeVer, 'patch', '1'], - [startTimeVer, 'minor', '1'], - [startTimeVer, 'major', '2'] - ] - const testPreInit = [ - [startTimeVer, 'prepatch', '1', '-alpha.0'], - [startTimeVer, 'preminor', '1', '-alpha.0'], - [startTimeVer, 'premajor', '2', '-alpha.0'] - ] - const testPreOngoing = [ - [startTimeVer + '-alpha.1', 'prerelease', '-alpha.2'], - [startTimeVer + '-beta.1', 'prerelease', '-beta.2'], - [startTimeVer + '-rc.1', 'prerelease', '-rc.2'] - ] - const testPretype = [ - [startTimeVer + '-alpha.1', 'pretype', startTimeVer + '-beta.0'], - [startTimeVer + '-beta.1', 'pretype', startTimeVer + '-rc.0'] - ] - beforeAll(async() => { - const start = new Date() - preTS = ver.makeTS(start) - await new Promise(resolve => setTimeout(resolve, 1000)) - resultReleased = testReleased - .map(([currVer, increment, nextMajor]) => [ver.nextVersion({ currVer, increment }), nextMajor]) - resultPreInit = testPreInit - .map(([currVer, increment, result]) => [ver.nextVersion({ currVer, increment })]) - resultPreOngoing = testPreOngoing - .map(([currVer, increment, nextPre]) => [ver.nextVersion({ currVer, increment })]) - resultPretype = testPretype - .map(([currVer, increment]) => [ver.nextVersion({ currVer, increment })]) - await new Promise(resolve => setTimeout(resolve, 1000)) - const end = new Date() - postTS = ver.makeTS(end) - }) - - // TODO: it would be nice to do these as some kind of loop, but since we do all our calculations at once (in the - // beforeAll) in order to minimize the sleep times, '.each' won't work because the values are calculated before the - // before. - describe('released increment', () => { - test(`${testReleased[0][0]} + ${testReleased[0][1]} -> ${testReleased[0][2]}.`, () => { - expect(resultReleased[0][0]).toMatch(/1\.\d{8}.\d{6}Z/) - const raw = ['1.' + preTS, resultReleased[0][0], `${testReleased[0][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - - test(`${testReleased[1][0]} + ${testReleased[1][1]} -> ${testReleased[1][2]}.`, () => { - expect(resultReleased[1][0]).toMatch(/1\.\d{8}.\d{6}Z/) - const raw = ['1.' + preTS, resultReleased[1][0], `${testReleased[1][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - - test(`${testReleased[2][0]} + ${testReleased[2][1]} -> ${testReleased[2][2]}.`, () => { - expect(resultReleased[2][0]).toMatch(/1\.\d{8}.\d{6}Z/) - const raw = ['1.' + preTS, resultReleased[2][0], `${testReleased[2][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - - test(`${testReleased[3][0]} + ${testReleased[3][1]} -> ${testReleased[3][2]}.`, () => { - expect(resultReleased[3][0]).toMatch(/2\.\d{8}.\d{6}Z/) - const raw = ['1.' + preTS, resultReleased[3][0], `${testReleased[3][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - }) - - describe('initial pre-increment', () => { - test(`${testPreInit[0][0]} + ${testPreInit[0][1]} -> 1.${testPreInit[0][3]}`, () => { - expect(resultPreInit[0][0]).toMatch(/1\.\d{8}.\d{6}Z-alpha\.0/) - const raw = ['1.' + preTS, resultPreInit[0][0], `${testPreInit[0][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - - test(`${testPreInit[1][0]} + ${testPreInit[1][1]} -> ${testPreInit[1][2]}.${testPreInit[1][3]}`, () => { - expect(resultPreInit[1][0]).toMatch(/1\.\d{8}.\d{6}Z-alpha\.0/) - const raw = ['1.' + preTS, resultPreInit[1][0], `${testPreInit[1][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - - test(`${testPreInit[2][0]} + ${testPreInit[2][1]} -> ${testPreInit[2][2]}.${testPreInit[2][3]}`, () => { - expect(resultPreInit[2][0]).toMatch(/2\.\d{8}.\d{6}Z-alpha\.0/) - const raw = ['1.' + preTS, resultPreInit[2][0], `${testPreInit[2][2]}.${postTS}`] - const sorted = [...raw].sort() - expect(raw).toEqual(sorted) - }) - }) - - describe('ongoing pre-increment', () => { - test(`${testPreOngoing[0][0]} + ${testPreOngoing[0][1]} -> ${testPreOngoing[0][0].slice(0, -1) + '2'}`, () => { - expect(resultPreOngoing[0][0]).toBe(testPreOngoing[0][0].slice(0, -1) + '2') - }) - - test(`${testPreOngoing[1][0]} + ${testPreOngoing[1][1]} -> ${testPreOngoing[1][0].slice(0, -1) + '2'}`, () => { - expect(resultPreOngoing[1][0]).toBe(testPreOngoing[1][0].slice(0, -1) + '2') - }) - - test(`${testPreOngoing[2][0]} + ${testPreOngoing[2][1]} -> ${testPreOngoing[2][0].slice(0, -1) + '2'}`, () => { - expect(resultPreOngoing[2][0]).toBe(testPreOngoing[2][0].slice(0, -1) + '2') - }) - }) - - describe('pretype', () => { - test(`${testPretype[0][0]} + ${testPretype[0][1]} -> ${testPretype[0][2]}`, () => - expect(resultPretype[0][0]).toBe(testPretype[0][2])) - - test(`${testPretype[1][0]} + ${testPretype[1][1]} -> ${testPretype[1][2]}`, () => - expect(resultPretype[1][0]).toBe(testPretype[1][2])) - - test(`${startTimeVer}-rc.1 + pretype -> 1.`, () => { - const currVer = startTimeVer + '-rc.1' - const newVer = ver.nextVersion({ currVer, increment : 'pretype' }) - expect(newVer).toMatch(/^\d+.\d{8}.\d{6}Z$/) - expect(newVer).not.toBe(startTimeVer) - const sorted = [startTimeVer, newVer].sort() - expect(sorted[0]).toBe(startTimeVer) - }) - }) - }) }) diff --git a/src/test/prerelease.test.mjs b/src/test/prerelease.test.mjs index 8addede..c1c99cc 100644 --- a/src/test/prerelease.test.mjs +++ b/src/test/prerelease.test.mjs @@ -6,9 +6,6 @@ describe('prerelease', () => { test.each([ ['1.0.0', null], ['1.0.0-beta.0', ['beta', 0]], - ['1.0.0-alpha.2', ['alpha', 2]], - ['20230303.051512Z', null], - ['20230304.051512Z-beta.0', ['beta', 0]], - ['20230304.051512Z-alpha.2', ['alpha', 2]] + ['1.0.0-alpha.2', ['alpha', 2]] ])('version %p yields %s', (version, expected) => expect(prerelease(version)).toEqual(expected)) }) diff --git a/src/test/version-compare.test.mjs b/src/test/version-compare.test.mjs index 428ffdf..d910663 100644 --- a/src/test/version-compare.test.mjs +++ b/src/test/version-compare.test.mjs @@ -9,21 +9,16 @@ describe('maxVersion', () => { [['1.0.0-alpha.0', '1.0.0'], '1.0.0'], [['1.0.0-alpha.0', '1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '1.0.0'], [['1.0.0-alpha.2', '1.0.0-alpha.13'], '1.0.0-alpha.13'], - [['2.0.0-alpha.0', '1.0.0'], '2.0.0-alpha.0'], - [['20230303.051512Z', '20230304.051512Z-alpha.0'], '20230304.051512Z-alpha.0'], - [['20230304.051512Z-beta.0', '20230304.051512Z-alpha.0'], '20230304.051512Z-beta.0'], - [['20230304.051512Z-beta.0', '20230304.051512Z-rc.0'], '20230304.051512Z-rc.0'] + [['2.0.0-alpha.0', '1.0.0'], '2.0.0-alpha.0'] ])('versions %p -> %s', (versions, expected) => expect(maxVersion({ versions })).toBe(expected)) test('[] => null', () => expect(maxVersion({ versions : [] })).toBe(null)) test('mixed version types raises exception', - () => expect(() => maxVersion({ versions : ['1.0.0', '20230501-101010Z'] })).toThrow()) + () => expect(() => maxVersion({ versions : ['1.0.0', 'not-a-valid-vesion'] })).toThrow()) test.each([ - [['1.0.0', '20230501-101010Z', 'abc'], '1.0.0'], - [['abc', '1.0.0', '20230501-101010Z'], '1.0.0'], - [['abc', '20230501-101010Z', '1.0.0'], '20230501-101010Z'] + [['1.0.0', 'not-a-valid-vesion', 'abc'], '1.0.0'] ])("'ignoreNonVersions' filters non-version strings from the version list", (versions, expected) => expect(maxVersion({ ignoreNonVersions : true, versions })).toBe(expected)) }) @@ -35,21 +30,16 @@ describe('minVersion', () => { [['1.0.0-alpha.0', '1.0.0'], '1.0.0-alpha.0'], [['1.0.0-alpha.0', '1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '1.0.0-alpha.0'], [['2.0.0-alpha.0', '1.0.0'], '1.0.0'], - [['1.0.0-alpha.2', '1.0.0-alpha.13'], '1.0.0-alpha.2'], - [['20230303.051512Z', '20230304.051512Z-alpha.0'], '20230303.051512Z'], - [['20230304.051512Z-beta.0', '20230304.051512Z-alpha.0'], '20230304.051512Z-alpha.0'], - [['20230304.051512Z-beta.0', '20230304.051512Z-rc.0'], '20230304.051512Z-beta.0'] + [['1.0.0-alpha.2', '1.0.0-alpha.13'], '1.0.0-alpha.2'] ])('versions %p -> %s', (versions, expected) => expect(minVersion({ versions })).toBe(expected)) test('[] => null', () => expect(minVersion({ versions : [] })).toBe(null)) test('mixed version types raises exception', - () => expect(() => minVersion({ versions : ['1.0.0', '20230501-101010Z'] })).toThrow()) + () => expect(() => minVersion({ versions : ['1.0.0', 'not-a-valid-vesion'] })).toThrow()) test.each([ - [['1.0.0', '20230501-101010Z', 'abc'], '1.0.0'], - [['abc', '1.0.0', '20230501-101010Z'], '1.0.0'], - [['abc', '20230501-101010Z', '1.0.0'], '20230501-101010Z'] + [['1.0.0', 'not-a-valid-vesion', 'abc'], '1.0.0'] ])("'ignoreNonVersions' filters non-version strings from the version list", (versions, expected) => expect(minVersion({ ignoreNonVersions : true, versions })).toBe(expected)) }) diff --git a/src/version-compare.mjs b/src/version-compare.mjs index 2844a62..f60b6f8 100644 --- a/src/version-compare.mjs +++ b/src/version-compare.mjs @@ -1,91 +1,57 @@ import semver from 'semver' -import { STYLE_AUTO, STYLE_SEMVER } from './constants' -import { isTimeVersion } from './is-time-version' -import { versionStyle } from './version-style' - -const compareHelper = ({ semverTest, style, timeverTest, versions }) => { +const compareHelper = ({ semverTest, timeverTest, versions }) => { let currLead for (let i = 0; i < versions.length; i += 1) { const testVer = versions[i] if (currLead === undefined) { currLead = testVer } - else if (style === STYLE_SEMVER) { - if (semverTest(currLead, testVer)) { - currLead = testVer - } - } - else { - if (timeverTest(currLead, testVer)) { - currLead = testVer - } + else if (semverTest(currLead, testVer)) { + currLead = testVer } } return currLead } -const maxVersion = ({ ignoreNonVersions, style, versions }) => { +const maxVersion = ({ ignoreNonVersions, versions }) => { if (!versions || versions.length === 0) { return null } - ([style, versions] = styleValidator({ ignoreNonVersions, style, versions })) + versions = filterValidVersions(versions, { ignoreNonVersions }) - return compareHelper({ semverTest : semver.lt, style, timeverTest : (a, b) => a.localeCompare(b) < 0, versions }) + return compareHelper({ semverTest : semver.lt, timeverTest : (a, b) => a.localeCompare(b) < 0, versions }) } -const minVersion = ({ ignoreNonVersions, style, versions }) => { +const minVersion = ({ ignoreNonVersions, versions }) => { if (!versions || versions.length === 0) { return null } - ([style, versions] = styleValidator({ ignoreNonVersions, style, versions })) + versions = filterValidVersions(versions, { ignoreNonVersions }) - return compareHelper({ semverTest : semver.gt, style, timeverTest : (a, b) => a.localeCompare(b) > 0, versions }) + return compareHelper({ semverTest : semver.gt, timeverTest : (a, b) => a.localeCompare(b) > 0, versions }) } -const styleValidator = ({ ignoreNonVersions, style, versions }) => { - if (style === STYLE_AUTO || style === undefined) { - style = versions.reduce((res, v) => { - try { - if (res !== undefined) { - return res - } - else { - return versionStyle(v) - } - } - catch (e) { - if (ignoreNonVersions === true) { - return undefined - } - else { - throw e - } - } - }, undefined) - } - +const filterValidVersions = (versions, { ignoreNonVersions }) => { versions = versions.filter((version) => { - const valid = style === STYLE_SEMVER - ? semver.valid(version) !== null - : isTimeVersion(version) + const valid = semver.valid(version) !== null if (valid === false) { if (ignoreNonVersions === true) { return false } else { - throw new Error(`Version style mis-match; initial version of style '${style}', but '${version}' is not valid for that style.`) + throw new Error(`'${version}' is not a valid semver.`) } } return true }) - return [style, versions] + return versions } export { maxVersion, minVersion } diff --git a/src/version-style.mjs b/src/version-style.mjs deleted file mode 100644 index 64777dc..0000000 --- a/src/version-style.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import semver from 'semver' - -import { STYLE_SEMVER, STYLE_TIMEVER } from './constants' -import { isTimeVersion } from './is-time-version' - -const versionStyle = (version) => { - const style = isTimeVersion(version) ? STYLE_TIMEVER : STYLE_SEMVER - - if (style === STYLE_SEMVER && !semver.valid(version)) { - throw new Error(`Could not determine version type for '${version}'.`) - } - - return style -} - -export { versionStyle }