diff --git a/lib/types/.eslintrc.js b/lib/types/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..e60f63cce989a27cbb13058f8484234af1499db8 --- /dev/null +++ b/lib/types/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + 'import/prefer-default-export': 0, + }, +}; diff --git a/lib/types/semver-stable.d.ts b/lib/types/semver-stable.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7d58f30fc33897a0e370b23ae50c07517e11b78 --- /dev/null +++ b/lib/types/semver-stable.d.ts @@ -0,0 +1,7 @@ +declare module 'semver-stable' { + export function is(version: string): boolean; + + export function max(versions: string[]): string; + + export function maxSatisfying(versions: string[], range: string): string; +} diff --git a/lib/types/semver-utils.d.ts b/lib/types/semver-utils.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..402fe0b29ada105ce6ee52a29b233221683e107a --- /dev/null +++ b/lib/types/semver-utils.d.ts @@ -0,0 +1,7 @@ +declare module 'semver-utils' { + export function parse(version: string): any; + + export function stringify(veriosn: any): string; + + export function parseRange(range: string): any[]; +} diff --git a/lib/versioning/cargo/index.js b/lib/versioning/cargo/index.ts similarity index 67% rename from lib/versioning/cargo/index.js rename to lib/versioning/cargo/index.ts index 063c2e451d41e84c941f90928b11b89fc996cfc2..6446cbcb7a7257b159b434bedc61f1ab66b94af6 100644 --- a/lib/versioning/cargo/index.js +++ b/lib/versioning/cargo/index.ts @@ -1,6 +1,7 @@ -const npm = require('../npm'); +import { api as npm } from '../npm'; +import { VersioningApi, RangeStrategy } from '../common'; -function convertToCaret(item) { +function convertToCaret(item: string) { // In Cargo, "1.2.3" doesn't mean exactly 1.2.3, it means >= 1.2.3 < 2.0.0 if (isVersion(item)) { // NOTE: Partial versions like '1.2' don't get converted to '^1.2' @@ -11,17 +12,17 @@ function convertToCaret(item) { return item.trim(); } -function cargo2npm(input) { +function cargo2npm(input: string) { let versions = input.split(','); versions = versions.map(convertToCaret); return versions.join(' '); } -function notEmpty(s) { +function notEmpty(s: string) { return s !== ''; } -function npm2cargo(input) { +function npm2cargo(input: string) { // Note: this doesn't remove the ^ const res = input .split(' ') @@ -37,22 +38,23 @@ function npm2cargo(input) { return res.join(', '); } -const isLessThanRange = (version, range) => +const isLessThanRange = (version: string, range: string) => npm.isLessThanRange(version, cargo2npm(range)); -const isValid = input => npm.isValid(cargo2npm(input)); +export const isValid = (input: string) => npm.isValid(cargo2npm(input)); -const isVersion = input => npm.isVersion(input); +const isVersion = (input: string) => npm.isVersion(input); -const matches = (version, range) => npm.matches(version, cargo2npm(range)); +const matches = (version: string, range: string) => + npm.matches(version, cargo2npm(range)); -const maxSatisfyingVersion = (versions, range) => +const maxSatisfyingVersion = (versions: string[], range: string) => npm.maxSatisfyingVersion(versions, cargo2npm(range)); -const minSatisfyingVersion = (versions, range) => +const minSatisfyingVersion = (versions: string[], range: string) => npm.minSatisfyingVersion(versions, cargo2npm(range)); -const isSingleVersion = constraint => +const isSingleVersion = (constraint: string) => constraint.trim().startsWith('=') && isVersion( constraint @@ -61,7 +63,12 @@ const isSingleVersion = constraint => .trim() ); -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { if (rangeStrategy === 'pin' || isSingleVersion(currentValue)) { let res = '='; if (currentValue.startsWith('= ')) { @@ -84,7 +91,7 @@ function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { return newCargo; } -module.exports = { +export const api: VersioningApi = { ...npm, getNewValue, isLessThanRange, @@ -94,3 +101,4 @@ module.exports = { maxSatisfyingVersion, minSatisfyingVersion, }; +export default api; diff --git a/lib/versioning/common.ts b/lib/versioning/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc31d41e48f9506484f009443b96a59f47b1d988 --- /dev/null +++ b/lib/versioning/common.ts @@ -0,0 +1,41 @@ +import { SemVer, Range } from 'semver'; + +export type RangeStrategy = + | 'auto' + | 'bump' + | 'pin' + | 'replace' + | 'update-lockfile' + | 'widen'; + +export interface VersioningApi { + // validation + isCompatible(version: string, range?: string): string | boolean | null; + isSingleVersion(version: string): string | boolean | null; + isStable(version: string): boolean; + isValid(version: string): string | boolean | null; + isVersion(version: string): string | boolean | null; + + // digestion of version + getMajor(version: string | SemVer): null | number; + getMinor(version: string | SemVer): null | number; + getPatch(version: string | SemVer): null | number; + + // comparison + equals(version: string, other: string): boolean; + isGreaterThan(version: string, other: string): boolean; + isLessThanRange?(version: string, range: string): boolean; + maxSatisfyingVersion(versions: string[], range: string): string | null; + minSatisfyingVersion(versions: string[], range: string): string | null; + getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string + ): string; + sortVersions(version: string, other: string): number; + + matches(version: string, range: string | Range): boolean; + + valueToVersion?(version: string): string; +} diff --git a/lib/versioning/composer/index.js b/lib/versioning/composer/index.ts similarity index 67% rename from lib/versioning/composer/index.js rename to lib/versioning/composer/index.ts index 2e3c00991777c7e00a4aa4578754c45a8fe26296..bdbb52de71be4260372785c818829479ed5230e6 100644 --- a/lib/versioning/composer/index.js +++ b/lib/versioning/composer/index.ts @@ -1,8 +1,9 @@ -const { coerce } = require('semver'); -const { logger } = require('../../logger'); -const npm = require('../npm'); +import { coerce } from 'semver'; +import { logger } from '../../logger'; +import { api as npm } from '../npm'; +import { VersioningApi, RangeStrategy } from '../common'; -function padZeroes(input) { +function padZeroes(input: string) { const sections = input.split('.'); while (sections.length < 3) { sections.push('0'); @@ -10,7 +11,7 @@ function padZeroes(input) { return sections.join('.'); } -function composer2npm(input) { +function composer2npm(input: string) { if (npm.isVersion(input)) { return input; } @@ -25,45 +26,57 @@ function composer2npm(input) { return output; } -const equals = (a, b) => npm.equals(composer2npm(a), composer2npm(b)); +const equals = (a: string, b: string) => + npm.equals(composer2npm(a), composer2npm(b)); -const getMajor = version => npm.getMajor(coerce(composer2npm(version))); +const getMajor = (version: string) => + npm.getMajor(coerce(composer2npm(version))); -const getMinor = version => npm.getMinor(coerce(composer2npm(version))); +const getMinor = (version: string) => + npm.getMinor(coerce(composer2npm(version))); -const getPatch = version => npm.getPatch(coerce(composer2npm(version))); +const getPatch = (version: string) => + npm.getPatch(coerce(composer2npm(version))); -const isGreaterThan = (a, b) => +const isGreaterThan = (a: string, b: string) => npm.isGreaterThan(composer2npm(a), composer2npm(b)); -const isLessThanRange = (version, range) => +const isLessThanRange = (version: string, range: string) => npm.isLessThanRange(composer2npm(version), composer2npm(range)); -const isSingleVersion = input => +const isSingleVersion = (input: string) => input && npm.isSingleVersion(composer2npm(input)); -const isStable = version => version && npm.isStable(composer2npm(version)); +const isStable = (version: string) => + version && npm.isStable(composer2npm(version)); -const isValid = input => input && npm.isValid(composer2npm(input)); +export const isValid = (input: string) => + input && npm.isValid(composer2npm(input)); -const isVersion = input => input && npm.isVersion(composer2npm(input)); +export const isVersion = (input: string) => + input && npm.isVersion(composer2npm(input)); -const matches = (version, range) => +const matches = (version: string, range: string) => npm.matches(composer2npm(version), composer2npm(range)); -const maxSatisfyingVersion = (versions, range) => +const maxSatisfyingVersion = (versions: string[], range: string) => npm.maxSatisfyingVersion(versions.map(composer2npm), composer2npm(range)); -const minSatisfyingVersion = (versions, range) => +const minSatisfyingVersion = (versions: string[], range: string) => npm.minSatisfyingVersion(versions.map(composer2npm), composer2npm(range)); -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { if (rangeStrategy === 'pin') { return toVersion; } const toMajor = getMajor(toVersion); const toMinor = getMinor(toVersion); - let newValue; + let newValue: string; if (isVersion(currentValue)) { newValue = toVersion; } else if ( @@ -125,11 +138,11 @@ function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { return newValue; } -function sortVersions(a, b) { +function sortVersions(a: string, b: string) { return npm.sortVersions(composer2npm(a), composer2npm(b)); } -module.exports = { +export const api: VersioningApi = { equals, getMajor, getMinor, @@ -147,3 +160,4 @@ module.exports = { getNewValue, sortVersions, }; +export default api; diff --git a/lib/versioning/docker/index.js b/lib/versioning/docker/index.ts similarity index 79% rename from lib/versioning/docker/index.js rename to lib/versioning/docker/index.ts index 859e45d7eb98d93d91594b8b9085892296883b02..9f7f4ab4c131f1e256c6b0996f076733431b21f5 100644 --- a/lib/versioning/docker/index.js +++ b/lib/versioning/docker/index.ts @@ -1,6 +1,7 @@ -const generic = require('../loose/generic'); +import * as generic from '../loose/generic'; +import { VersioningApi } from '../common'; -function parse(version) { +function parse(version: string) { const versionPieces = version.replace(/^v/, '').split('-'); const prefix = versionPieces.shift(); const suffix = versionPieces.join('-'); @@ -11,12 +12,12 @@ function parse(version) { return { release, suffix }; } -function valueToVersion(value) { +function valueToVersion(value: string) { // Remove any suffix after '-', e.g. '-alpine' return value ? value.split('-')[0] : value; } -function compare(version1, vervion2) { +function compare(version1: string, vervion2: string) { const parsed1 = parse(version1); const parsed2 = parse(vervion2); // istanbul ignore if @@ -42,7 +43,7 @@ function compare(version1, vervion2) { return parsed2.suffix.localeCompare(parsed1.suffix); } -function isCompatible(version, range) { +function isCompatible(version: string, range: string) { const parsed1 = parse(version); const parsed2 = parse(range); return ( @@ -51,7 +52,7 @@ function isCompatible(version, range) { ); } -module.exports = { +export const api: VersioningApi = { ...generic.create({ parse, compare, @@ -59,3 +60,5 @@ module.exports = { isCompatible, valueToVersion, }; + +export default api; diff --git a/lib/versioning/hashicorp/index.js b/lib/versioning/hashicorp/index.ts similarity index 54% rename from lib/versioning/hashicorp/index.js rename to lib/versioning/hashicorp/index.ts index 8d711a5ebfa89e872e61670158d44f1507beb4de..5ed2786188c9e550b8b104cbeea5b64eaba98f24 100644 --- a/lib/versioning/hashicorp/index.js +++ b/lib/versioning/hashicorp/index.ts @@ -1,25 +1,31 @@ -const npm = require('../npm'); +import { api as npm } from '../npm'; +import { VersioningApi, RangeStrategy } from '../common'; -function hashicorp2npm(input) { +function hashicorp2npm(input: string) { // The only case incompatible with semver is a "short" ~>, e.g. ~> 1.2 return input.replace(/~>(\s*\d+\.\d+$)/, '^$1').replace(',', ''); } -const isLessThanRange = (version, range) => +const isLessThanRange = (version: string, range: string) => npm.isLessThanRange(hashicorp2npm(version), hashicorp2npm(range)); -const isValid = input => npm.isValid(hashicorp2npm(input)); +export const isValid = (input: string) => npm.isValid(hashicorp2npm(input)); -const matches = (version, range) => +const matches = (version: string, range: string) => npm.matches(hashicorp2npm(version), hashicorp2npm(range)); -const maxSatisfyingVersion = (versions, range) => +const maxSatisfyingVersion = (versions: string[], range: string) => npm.maxSatisfyingVersion(versions.map(hashicorp2npm), hashicorp2npm(range)); -const minSatisfyingVersion = (versions, range) => +const minSatisfyingVersion = (versions: string[], range: string) => npm.minSatisfyingVersion(versions.map(hashicorp2npm), hashicorp2npm(range)); -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { // handle specia. ~> 1.2 case if (currentValue.match(/(~>\s*)\d+\.\d+$/)) { return currentValue.replace( @@ -30,7 +36,7 @@ function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { return npm.getNewValue(currentValue, rangeStrategy, fromVersion, toVersion); } -module.exports = { +export const api: VersioningApi = { ...npm, isLessThanRange, isValid, @@ -39,3 +45,7 @@ module.exports = { minSatisfyingVersion, getNewValue, }; + +export const isVersion = api.isVersion; + +export default api; diff --git a/lib/versioning/hex/index.js b/lib/versioning/hex/index.ts similarity index 67% rename from lib/versioning/hex/index.js rename to lib/versioning/hex/index.ts index 24de039ef52f0646d8ec28f6f2715807119dbb44..1af0860fe2c5361be74b6054171b756bb5829663 100644 --- a/lib/versioning/hex/index.js +++ b/lib/versioning/hex/index.ts @@ -1,6 +1,7 @@ -const npm = require('../npm'); +import { api as npm } from '../npm'; +import { VersioningApi, RangeStrategy } from '../common'; -function hex2npm(input) { +function hex2npm(input: string) { return input .replace(/~>\s*(\d+\.\d+)$/, '^$1') .replace(/~>\s*(\d+\.\d+\.\d+)/, '~$1') @@ -9,7 +10,7 @@ function hex2npm(input) { .replace(/!=\s*(\d+\.\d+(\.\d+.*)?)/, '>$1 <$1'); } -function npm2hex(input) { +function npm2hex(input: string) { const res = input .split(' ') .map(str => str.trim()) @@ -30,21 +31,26 @@ function npm2hex(input) { return output; } -const isLessThanRange = (version, range) => +const isLessThanRange = (version: string, range: string) => npm.isLessThanRange(hex2npm(version), hex2npm(range)); -const isValid = input => npm.isValid(hex2npm(input)); +const isValid = (input: string) => npm.isValid(hex2npm(input)); -const matches = (version, range) => +const matches = (version: string, range: string) => npm.matches(hex2npm(version), hex2npm(range)); -const maxSatisfyingVersion = (versions, range) => +const maxSatisfyingVersion = (versions: string[], range: string) => npm.maxSatisfyingVersion(versions.map(hex2npm), hex2npm(range)); -const minSatisfyingVersion = (versions, range) => +const minSatisfyingVersion = (versions: string[], range: string) => npm.minSatisfyingVersion(versions.map(hex2npm), hex2npm(range)); -const getNewValue = (currentValue, rangeStrategy, fromVersion, toVersion) => { +const getNewValue = ( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) => { let newSemver = npm.getNewValue( hex2npm(currentValue), rangeStrategy, @@ -55,14 +61,14 @@ const getNewValue = (currentValue, rangeStrategy, fromVersion, toVersion) => { if (currentValue.match(/~>\s*(\d+\.\d+)$/)) newSemver = newSemver.replace( /\^\s*(\d+\.\d+)/, - (str, p1) => '~> ' + p1.slice(0, -2) + (_str, p1) => '~> ' + p1.slice(0, -2) ); else newSemver = newSemver.replace(/~\s*(\d+\.\d+\.\d)/, '~> $1'); return newSemver; }; -module.exports = { +export const api: VersioningApi = { ...npm, isLessThanRange, isValid, @@ -71,3 +77,5 @@ module.exports = { minSatisfyingVersion, getNewValue, }; + +export default api; diff --git a/lib/versioning/index.js b/lib/versioning/index.js deleted file mode 100644 index 55ff6ff4a87e992f0ad95b6a35d23a8689675fac..0000000000000000000000000000000000000000 --- a/lib/versioning/index.js +++ /dev/null @@ -1,27 +0,0 @@ -const { logger } = require('../logger'); -const supportedSchemes = require('../config/definitions') - .getOptions() - .find(option => option.name === 'versionScheme').allowedValues; - -const schemes = {}; - -for (const scheme of supportedSchemes) { - schemes[scheme] = require('./' + scheme); // eslint-disable-line -} - -module.exports = { - get, -}; - -function get(versionScheme) { - if (!versionScheme) { - logger.debug('Missing versionScheme'); - return schemes.semver; - } - const scheme = schemes[versionScheme]; - if (!scheme) { - logger.warn({ versionScheme }, 'Unknown version scheme'); - return schemes.semver; - } - return scheme; -} diff --git a/lib/versioning/index.ts b/lib/versioning/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e92da8d1b415e1a31e2aec7d7447333a3807de5a --- /dev/null +++ b/lib/versioning/index.ts @@ -0,0 +1,30 @@ +import { logger } from '../logger'; +import { getOptions } from '../config/definitions'; +import { VersioningApi } from './common'; + +export * from './common'; + +const supportedSchemes = getOptions().find( + option => option.name === 'versionScheme' +).allowedValues; + +const schemes: Record<string, VersioningApi> = {}; + +for (const scheme of supportedSchemes) { + schemes[scheme] = require('./' + scheme).api; // eslint-disable-line +} + +export { get }; + +function get(versionScheme: string) { + if (!versionScheme) { + logger.debug('Missing versionScheme'); + return schemes.semver; + } + const scheme = schemes[versionScheme]; + if (!scheme) { + logger.warn({ versionScheme }, 'Unknown version scheme'); + return schemes.semver; + } + return scheme; +} diff --git a/lib/versioning/ivy/index.js b/lib/versioning/ivy/index.ts similarity index 79% rename from lib/versioning/ivy/index.js rename to lib/versioning/ivy/index.ts index ffcc1e883c3310d8104977fb00b2a88c415d639a..68d516c88234f3595c33ce7eba485eedde46b88f 100644 --- a/lib/versioning/ivy/index.js +++ b/lib/versioning/ivy/index.ts @@ -1,3 +1,12 @@ +import maven from '../maven'; +import { TYPE_QUALIFIER, tokenize, isSubversion } from '../maven/compare'; +import { + REV_TYPE_LATEST, + REV_TYPE_SUBREV, + parseDynamicRevision, +} from './parse'; +import { VersioningApi } from '../common'; + const { equals, getMajor, @@ -11,24 +20,16 @@ const { minSatisfyingVersion, getNewValue, sortVersions, -} = require('../maven/index'); - -const { TYPE_QUALIFIER, tokenize, isSubversion } = require('../maven/compare'); +} = maven; -const { - REV_TYPE_LATEST, - REV_TYPE_SUBREV, - parseDynamicRevision, -} = require('./parse'); - -function isVersion(str) { +function isVersion(str: string) { if (!str) { return false; } return isSingleVersion(str) || !!parseDynamicRevision(str); } -function matches(a, b) { +function matches(a: string, b: string) { if (!a) return false; if (!b) return false; const dynamicRevision = parseDynamicRevision(b); @@ -54,7 +55,7 @@ function matches(a, b) { return mavenMatches(a, value); } -module.exports = { +export const api: VersioningApi = { equals, getMajor, getMinor, @@ -71,3 +72,5 @@ module.exports = { getNewValue, sortVersions, }; + +export default api; diff --git a/lib/versioning/ivy/parse.js b/lib/versioning/ivy/parse.ts similarity index 76% rename from lib/versioning/ivy/parse.js rename to lib/versioning/ivy/parse.ts index d08d80d18dc19e80fec67e759874bedf9c3cfec9..e5123baf52f61aaaa6cbc4984d6def673bcea2ac 100644 --- a/lib/versioning/ivy/parse.js +++ b/lib/versioning/ivy/parse.ts @@ -1,10 +1,16 @@ -const { isSingleVersion, parseRange, rangeToStr } = require('../maven/compare'); +import { isSingleVersion, parseRange, rangeToStr } from '../maven/compare'; const REV_TYPE_LATEST = 'REV_TYPE_LATEST'; const REV_TYPE_SUBREV = 'REV_TYPE_SUBREVISION'; const REV_TYPE_RANGE = 'REV_TYPE_RANGE'; -function parseDynamicRevision(str) { +export interface Revision { + type: typeof REV_TYPE_LATEST | typeof REV_TYPE_RANGE | typeof REV_TYPE_SUBREV; + + value: string; +} + +function parseDynamicRevision(str: string): Revision { if (!str) return null; const LATEST_REGEX = /^latest\.|^latest$/i; @@ -38,7 +44,7 @@ function parseDynamicRevision(str) { return null; } -module.exports = { +export { REV_TYPE_LATEST, REV_TYPE_SUBREV, REV_TYPE_RANGE, diff --git a/lib/versioning/loose/generic.js b/lib/versioning/loose/generic.ts similarity index 53% rename from lib/versioning/loose/generic.js rename to lib/versioning/loose/generic.ts index e286720bd17740bc5b624e67be8af66ec36c09cf..f9bd102cae90480aaf0c2d577856561e749bccbf 100644 --- a/lib/versioning/loose/generic.js +++ b/lib/versioning/loose/generic.ts @@ -1,12 +1,32 @@ +import { VersioningApi, RangeStrategy } from '../common'; + +export interface GenericVersion { + release: number[]; + suffix?: string; +} +export interface VersionParser { + (version: string); +} + +export interface VersionComparator { + (version: string, other: string): number; +} + // helper functions to ease create other versioning schemas with little code // especially if those schemas do not support ranges -exports.create = ({ parse, compare }) => { - let schema = {}; +export const create = ({ + parse, + compare, +}: { + parse: VersionParser; + compare: VersionComparator; +}) => { + let schema: VersioningApi = {} as any; if (parse) { - schema = { ...schema, ...exports.parser(parse) }; + schema = { ...schema, ...parser(parse) }; } if (compare) { - schema = { ...schema, ...exports.comparer(compare) }; + schema = { ...schema, ...comparer(compare) }; } return schema; }; @@ -14,27 +34,27 @@ exports.create = ({ parse, compare }) => { // since this file was meant for no range support, a range = version // parse should return null if version not valid // parse should return an object with property release, an array of version sections major.minor.patch -exports.parser = parse => { - function isValid(version) { +export const parser = (parse: VersionParser): Partial<VersioningApi> => { + function isValid(version: string) { if (!version) { return null; } const parsed = parse(version); return parsed ? version : null; } - function getSection(version, index) { + function getSection(version: string, index: number) { const parsed = parse(version); return parsed && parsed.release.length > index ? parsed.release[index] : null; } - function getMajor(version) { + function getMajor(version: string) { return getSection(version, 0); } - function getMinor(version) { + function getMinor(version: string) { return getSection(version, 1); } - function getPatch(version) { + function getPatch(version: string) { return getSection(version, 2); } @@ -42,7 +62,7 @@ exports.parser = parse => { // validation isCompatible: isValid, isSingleVersion: isValid, - isStable: isValid, + isStable: v => !!isValid(v), isValid, isVersion: isValid, // digestion of version @@ -54,29 +74,36 @@ exports.parser = parse => { // this is the main reason this file was created // most operations below could be derived from a compare function -exports.comparer = compare => { - function equals(version, other) { +export const comparer = ( + compare: VersionComparator +): Partial<VersioningApi> => { + function equals(version: string, other: string) { return compare(version, other) === 0; } - function isGreaterThan(version, other) { + function isGreaterThan(version: string, other: string) { return compare(version, other) > 0; } - function isLessThanRange(version, range) { + function isLessThanRange(version: string, range: string) { return compare(version, range) < 0; } // we don't not have ranges, so versions has to be equal - function maxSatisfyingVersion(versions, range) { + function maxSatisfyingVersion(versions: string[], range: string) { return versions.find(v => equals(v, range)) || null; } - function minSatisfyingVersion(versions, range) { + function minSatisfyingVersion(versions: string[], range: string) { return versions.find(v => equals(v, range)) || null; } - function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { + function getNewValue( + _currentValue: string, + _rangeStrategy: RangeStrategy, + _fromVersion: string, + toVersion: string + ) { return toVersion; } - function sortVersions(version, other) { + function sortVersions(version: string, other: string) { return compare(version, other); } diff --git a/lib/versioning/loose/index.js b/lib/versioning/loose/index.ts similarity index 79% rename from lib/versioning/loose/index.js rename to lib/versioning/loose/index.ts index 83995868f03c5e0f1914f5ba22dc67ceb314e0fb..9c6f53985b74e15e762517d3d25a784297638d00 100644 --- a/lib/versioning/loose/index.js +++ b/lib/versioning/loose/index.ts @@ -1,8 +1,9 @@ -const generic = require('./generic'); +import * as generic from './generic'; +import { VersioningApi } from '../common'; const pattern = /^v?(\d+(?:\.\d+)*)(.*)$/; -function parse(version) { +function parse(version: string) { const matches = pattern.exec(version); if (!matches) { return null; @@ -15,7 +16,7 @@ function parse(version) { return { release, suffix: suffix || '' }; } -function compare(version1, vervion2) { +function compare(version1: string, vervion2: string) { const parsed1 = parse(version1); const parsed2 = parse(vervion2); // istanbul ignore if @@ -41,7 +42,9 @@ function compare(version1, vervion2) { return parsed1.suffix.localeCompare(parsed2.suffix); } -module.exports = generic.create({ +export const api: VersioningApi = generic.create({ parse, compare, }); + +export default api; diff --git a/lib/versioning/maven/compare.js b/lib/versioning/maven/compare.ts similarity index 75% rename from lib/versioning/maven/compare.js rename to lib/versioning/maven/compare.ts index 1ce7fbeff4aefbb227bd392f2482fb0b6c96fcfe..621803733cc3987ac27f3635d8e98dee931f605d 100644 --- a/lib/versioning/maven/compare.js +++ b/lib/versioning/maven/compare.ts @@ -4,7 +4,26 @@ const PREFIX_HYPHEN = 'PREFIX_HYPHEN'; const TYPE_NUMBER = 'TYPE_NUMBER'; const TYPE_QUALIFIER = 'TYPE_QUALIFIER'; -function iterateChars(str, cb) { +export interface BaseToken { + prefix: string; + type: typeof TYPE_NUMBER | typeof TYPE_QUALIFIER; + val: number | string; + isTransition?: boolean; +} + +export interface NumberToken extends BaseToken { + type: typeof TYPE_NUMBER; + val: number; +} + +export interface QualifierToken extends BaseToken { + type: typeof TYPE_QUALIFIER; + val: string; +} + +export type Token = NumberToken | QualifierToken; + +function iterateChars(str: string, cb: (p: string, n: string) => void) { let prev = null; let next = null; for (let i = 0; i < str.length; i += 1) { @@ -15,22 +34,22 @@ function iterateChars(str, cb) { cb(prev, null); } -function isDigit(char) { +function isDigit(char: string) { return /^\d$/.test(char); } -function isLetter(char) { +function isLetter(char: string) { return /^[a-z]$/i.test(char); } -function isTransition(prevChar, nextChar) { +function isTransition(prevChar: string, nextChar: string) { return ( (isDigit(prevChar) && isLetter(nextChar)) || (isLetter(prevChar) && isDigit(nextChar)) ); } -function iterateTokens(versionStr, cb) { +function iterateTokens(versionStr: string, cb: (token: Token) => void) { let currentPrefix = PREFIX_HYPHEN; let currentVal = ''; @@ -74,21 +93,21 @@ function iterateTokens(versionStr, cb) { }); } -function isNull(token) { +function isNull(token: Token) { const val = token.val; return val === 0 || val === '' || val === 'final' || val === 'ga'; } -const zeroToken = { +const zeroToken: NumberToken = { prefix: PREFIX_HYPHEN, type: TYPE_NUMBER, val: 0, isTransition: false, }; -function tokenize(versionStr) { - let buf = []; - let result = []; +function tokenize(versionStr: string) { + let buf: Token[] = []; + let result: Token[] = []; let leadingZero = true; iterateTokens(versionStr.toLowerCase().replace(/^v/i, ''), token => { if (token.prefix === PREFIX_HYPHEN) { @@ -107,15 +126,21 @@ function tokenize(versionStr) { return result.length ? result : [zeroToken]; } -function nullFor(token) { - return { - prefix: token.prefix, - type: token.prefix === PREFIX_DOT ? TYPE_NUMBER : TYPE_QUALIFIER, - val: token.prefix === PREFIX_DOT ? 0 : '', - }; +function nullFor(token: Token): Token { + return token.prefix === PREFIX_DOT + ? { + prefix: token.prefix, + type: TYPE_NUMBER, + val: 0, + } + : { + prefix: token.prefix, + type: TYPE_QUALIFIER, + val: '', + }; } -function commonOrder(token) { +function commonOrder(token: Token) { if (token.prefix === PREFIX_DOT && token.type === TYPE_QUALIFIER) { return 0; } @@ -128,7 +153,7 @@ function commonOrder(token) { return 3; } -function qualifierOrder(token) { +function qualifierOrder(token: Token) { const val = token.val; if (val === 'alpha' || (token.isTransition && val === 'a')) { return 1; @@ -154,7 +179,7 @@ function qualifierOrder(token) { return null; } -function qualifierCmp(left, right) { +function qualifierCmp(left: Token, right: Token) { const leftOrder = qualifierOrder(left); const rightOrder = qualifierOrder(right); if (leftOrder && rightOrder) { @@ -176,7 +201,7 @@ function qualifierCmp(left, right) { return 1; } -function tokenCmp(left, right) { +function tokenCmp(left: Token, right: Token) { if (left.prefix === right.prefix) { if (left.type === TYPE_NUMBER && right.type === TYPE_NUMBER) { if (left.val === right.val) { @@ -207,7 +232,7 @@ function tokenCmp(left, right) { return 1; } -function compare(left, right) { +function compare(left: string, right: string) { const leftTokens = tokenize(left); const rightTokens = tokenize(right); const length = Math.max(leftTokens.length, rightTokens.length); @@ -220,8 +245,7 @@ function compare(left, right) { return 0; } -function isVersion(version) { - // istanbul ignore if +function isVersion(version: string) { if (!version) return false; if (!/^[a-z0-9.-]+$/i.test(version)) return false; if (/^[.-]/.test(version)) return false; @@ -230,7 +254,7 @@ function isVersion(version) { return !!tokens.length; } -function isValid(str) { +function isValid(str: string) { if (!str) { return false; } @@ -240,8 +264,17 @@ function isValid(str) { const INCLUDING_POINT = 'INCLUDING_POINT'; const EXCLUDING_POINT = 'EXCLUDING_POINT'; -function parseRange(rangeStr) { - function emptyInterval() { +export interface Range { + leftType: typeof INCLUDING_POINT | typeof EXCLUDING_POINT; + leftValue: string; + leftBracket: string; + rightType: typeof INCLUDING_POINT | typeof EXCLUDING_POINT; + rightValue: string; + rightBracket: string; +} + +function parseRange(rangeStr: string) { + function emptyInterval(): Range { return { leftType: null, leftValue: null, @@ -253,7 +286,7 @@ function parseRange(rangeStr) { } const commaSplit = rangeStr.split(','); - let result = []; + let result: Range[] = []; let interval = emptyInterval(); commaSplit.forEach(subStr => { @@ -306,38 +339,41 @@ function parseRange(rangeStr) { if (!result || !result.length) return null; const lastIdx = result.length - 1; - let prevValue = null; - return result.reduce((acc, range, idx) => { - const { leftType, leftValue, rightType, rightValue } = range; - - if (idx === 0 && leftValue === '') { - if (leftType === EXCLUDING_POINT && isVersion(rightValue)) { - prevValue = rightValue; - return [...acc, { ...range, leftValue: null }]; + let prevValue: string = null; + return result.reduce( + (acc, range, idx) => { + const { leftType, leftValue, rightType, rightValue } = range; + + if (idx === 0 && leftValue === '') { + if (leftType === EXCLUDING_POINT && isVersion(rightValue)) { + prevValue = rightValue; + return [...acc, { ...range, leftValue: null }]; + } + return null; } - return null; - } - if (idx === lastIdx && rightValue === '') { - if (rightType === EXCLUDING_POINT && isVersion(leftValue)) { + if (idx === lastIdx && rightValue === '') { + if (rightType === EXCLUDING_POINT && isVersion(leftValue)) { + if (prevValue && compare(prevValue, leftValue) === 1) return null; + return [...acc, { ...range, rightValue: null }]; + } + return null; + } + if (isVersion(leftValue) && isVersion(rightValue)) { + if (compare(leftValue, rightValue) === 1) return null; if (prevValue && compare(prevValue, leftValue) === 1) return null; - return [...acc, { ...range, rightValue: null }]; + prevValue = rightValue; + return [...acc, range]; } return null; - } - if (isVersion(leftValue) && isVersion(rightValue)) { - if (compare(leftValue, rightValue) === 1) return null; - if (prevValue && compare(prevValue, leftValue) === 1) return null; - prevValue = rightValue; - return [...acc, range]; - } - return null; - }, []); + }, + [] as Range[] + ); } -function rangeToStr(fullRange) { +function rangeToStr(fullRange: Range[]) { if (fullRange === null) return null; - const valToStr = val => (val === null ? '' : val); + const valToStr = (val: string) => (val === null ? '' : val); if (fullRange.length === 1) { const { leftBracket, rightBracket, leftValue, rightValue } = fullRange[0]; @@ -362,10 +398,10 @@ function rangeToStr(fullRange) { return intervals.join(','); } -function autoExtendMavenRange(currentRepresentation, newValue) { +function autoExtendMavenRange(currentRepresentation: string, newValue: string) { const range = parseRange(currentRepresentation); if (!range) return currentRepresentation; - const isPoint = vals => { + const isPoint = (vals: Range[]) => { if (vals.length !== 1) return false; const { leftType, leftValue, rightType, rightValue } = vals[0]; return ( @@ -402,7 +438,7 @@ function autoExtendMavenRange(currentRepresentation, newValue) { return rangeToStr(range); } -function isSubversion(majorVersion, minorVersion) { +function isSubversion(majorVersion: string, minorVersion: string) { const majorTokens = tokenize(majorVersion); const minorTokens = tokenize(minorVersion); @@ -420,7 +456,7 @@ function isSubversion(majorVersion, minorVersion) { return result; } -module.exports = { +export { PREFIX_DOT, PREFIX_HYPHEN, TYPE_NUMBER, @@ -429,7 +465,7 @@ module.exports = { isSubversion, compare, isVersion, - isSingleVersion: isVersion, + isVersion as isSingleVersion, isValid, parseRange, rangeToStr, diff --git a/lib/versioning/maven/index.js b/lib/versioning/maven/index.ts similarity index 70% rename from lib/versioning/maven/index.js rename to lib/versioning/maven/index.ts index 1ef505e3557414dd3dac14300f51cf4773e0cfbe..214271c76912043c86c3ae2c34a0c1e4fe43b1b3 100644 --- a/lib/versioning/maven/index.js +++ b/lib/versioning/maven/index.ts @@ -1,4 +1,4 @@ -const { +import { isVersion, isValid, tokenize, @@ -9,11 +9,12 @@ const { autoExtendMavenRange, parseRange, EXCLUDING_POINT, -} = require('./compare'); +} from './compare'; +import { RangeStrategy, VersioningApi } from '../common'; -const equals = (a, b) => compare(a, b) === 0; +const equals = (a: string, b: string) => compare(a, b) === 0; -function matches(a, b) { +function matches(a: string, b: string) { if (!b) return false; if (isVersion(b)) return equals(a, b); const ranges = parseRange(b); @@ -44,28 +45,28 @@ function matches(a, b) { }, false); } -const getMajor = version => { +const getMajor = (version: string) => { if (isVersion(version)) { const tokens = tokenize(version); const majorToken = tokens[0]; - return majorToken.val; + return +majorToken.val; } return null; }; -const getMinor = version => { +const getMinor = (version: string) => { if (isVersion(version)) { const tokens = tokenize(version); const minorToken = tokens[1]; if (minorToken && minorToken.type === TYPE_NUMBER) { - return minorToken.val; + return +minorToken.val; } return 0; } return null; }; -const getPatch = version => { +const getPatch = (version: string) => { if (isVersion(version)) { const tokens = tokenize(version); const minorToken = tokens[1]; @@ -75,26 +76,26 @@ const getPatch = version => { minorToken.type === TYPE_NUMBER && patchToken.type === TYPE_NUMBER ) { - return patchToken.val; + return +patchToken.val; } return 0; } return null; }; -const isGreaterThan = (a, b) => compare(a, b) === 1; +const isGreaterThan = (a: string, b: string) => compare(a, b) === 1; -const isStable = version => { +const isStable = (version: string) => { if (isVersion(version)) { const tokens = tokenize(version); const qualToken = tokens.find(token => token.type === TYPE_QUALIFIER); if (qualToken) { const val = qualToken.val; - if (val === 'final') return true; - if (val === 'ga') return true; - if (val === 'release') return true; - if (val === 'sp') return true; - // istanbul ignore next + // TODO: Can this if be removed, we never get here + // istanbul ignore if + if (val === 'final' || val === 'ga') return true; + + if (val === 'release' || val === 'sp') return true; return false; } return true; @@ -102,7 +103,7 @@ const isStable = version => { return null; }; -const maxSatisfyingVersion = (versions, range) => { +const maxSatisfyingVersion = (versions: string[], range: string) => { return versions.reduce((result, version) => { if (matches(version, range)) { if (!result) return version; @@ -112,14 +113,21 @@ const maxSatisfyingVersion = (versions, range) => { }, null); }; -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + _fromVersion: string, + toVersion: string +) { if (isVersion(currentValue) || rangeStrategy === 'pin') { return toVersion; } return autoExtendMavenRange(currentValue, toVersion); } -module.exports = { +export { isValid }; + +export const api: VersioningApi = { equals, getMajor, getMinor, @@ -136,3 +144,5 @@ module.exports = { getNewValue, sortVersions: compare, }; + +export default api; diff --git a/lib/versioning/node/index.js b/lib/versioning/node/index.js deleted file mode 100644 index b6d025ef9a755f3bc9262af2b93e57f451bfedb7..0000000000000000000000000000000000000000 --- a/lib/versioning/node/index.js +++ /dev/null @@ -1,20 +0,0 @@ -const npm = require('../npm'); - -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { - const res = npm.getNewValue( - currentValue, - rangeStrategy, - fromVersion, - toVersion - ); - if (npm.isVersion(res)) { - // normalize out any 'v' prefix - return npm.isVersion(res); - } - return res; -} - -module.exports = { - ...npm, - getNewValue, -}; diff --git a/lib/versioning/node/index.ts b/lib/versioning/node/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a150c5bbf13ad331b5d20181a229029941cb46c --- /dev/null +++ b/lib/versioning/node/index.ts @@ -0,0 +1,29 @@ +import npm, { isVersion, isValid } from '../npm'; +import { RangeStrategy, VersioningApi } from '../common'; + +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { + const res = npm.getNewValue( + currentValue, + rangeStrategy, + fromVersion, + toVersion + ); + if (isVersion(res)) { + // normalize out any 'v' prefix + return isVersion(res); + } + return res; +} + +export { isValid }; + +export const api: VersioningApi = { + ...npm, + getNewValue, +}; +export default api; diff --git a/lib/versioning/npm/index.js b/lib/versioning/npm/index.ts similarity index 64% rename from lib/versioning/npm/index.js rename to lib/versioning/npm/index.ts index 3c6cf9af120e25c4fcb717f00002ed079f35e967..7f60432699718b3e9a8e7fe279770480b46611fd 100644 --- a/lib/versioning/npm/index.js +++ b/lib/versioning/npm/index.ts @@ -1,8 +1,7 @@ -const semver = require('semver'); -const stable = require('semver-stable'); -const { getNewValue } = require('./range'); - -const { is: isStable } = stable; +import * as semver from 'semver'; +import { is as isStable } from 'semver-stable'; +import { getNewValue } from './range'; +import { VersioningApi } from '../common'; const { compare: sortVersions, @@ -20,14 +19,14 @@ const { } = semver; // If this is left as an alias, inputs like "17.04.0" throw errors -const isValid = input => validRange(input); -const isVersion = input => valid(input); +export const isValid = (input: string) => validRange(input); +export const isVersion = (input: string) => valid(input); -const isSingleVersion = constraint => +const isSingleVersion = (constraint: string) => isVersion(constraint) || (constraint.startsWith('=') && isVersion(constraint.substring(1).trim())); -module.exports = { +export const api: VersioningApi = { equals, getMajor, getMinor, @@ -45,3 +44,5 @@ module.exports = { minSatisfyingVersion, sortVersions, }; + +export default api; diff --git a/lib/versioning/npm/range.js b/lib/versioning/npm/range.ts similarity index 93% rename from lib/versioning/npm/range.js rename to lib/versioning/npm/range.ts index adfd7591c3ca059169ee03ef1e8ba67cad8399b4..4bf0bef94b885a8042af7a95966ab1e8a3c6ac2c 100644 --- a/lib/versioning/npm/range.js +++ b/lib/versioning/npm/range.ts @@ -1,20 +1,24 @@ -const { - inc: increment, +import { + inc as increment, major, minor, patch, prerelease, satisfies, - valid: isVersion, -} = require('semver'); -const { parseRange } = require('semver-utils'); -const { logger } = require('../../logger'); + valid as isVersion, +} from 'semver'; +import { parseRange } from 'semver-utils'; +import { logger } from '../../logger'; +import { RangeStrategy } from '../common'; -module.exports = { - getNewValue, -}; +export { getNewValue }; -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { if (rangeStrategy === 'pin' || isVersion(currentValue)) { return toVersion; } diff --git a/lib/versioning/pep440/index.js b/lib/versioning/pep440/index.ts similarity index 63% rename from lib/versioning/pep440/index.js rename to lib/versioning/pep440/index.ts index 9b128f514ee4c5519583c687d482c8468a538839..699fe4a48b6fb4a0d20b6786bcd76103196c9124 100644 --- a/lib/versioning/pep440/index.js +++ b/lib/versioning/pep440/index.ts @@ -1,6 +1,7 @@ -const pep440 = require('@renovate/pep440'); -const { filter } = require('@renovate/pep440/lib/specifier'); -const { getNewValue } = require('./range'); +import * as pep440 from '@renovate/pep440'; +import { filter } from '@renovate/pep440/lib/specifier'; +import { getNewValue } from './range'; +import { VersioningApi } from '../common'; const { compare: sortVersions, @@ -15,7 +16,7 @@ const { eq: equals, } = pep440; -const isStable = input => { +const isStable = (input: string) => { const version = explain(input); if (!version) { return false; @@ -24,23 +25,25 @@ const isStable = input => { }; // If this is left as an alias, inputs like "17.04.0" throw errors -const isValid = input => validRange(input); +export const isValid = (input: string) => validRange(input); -const maxSatisfyingVersion = (versions, range) => { +const maxSatisfyingVersion = (versions: string[], range: string) => { const found = filter(versions, range).sort(sortVersions); return found.length === 0 ? null : found[found.length - 1]; }; -const minSatisfyingVersion = (versions, range) => { +const minSatisfyingVersion = (versions: string[], range: string) => { const found = filter(versions, range).sort(sortVersions); return found.length === 0 ? null : found[0]; }; -const isSingleVersion = constraint => +export const isSingleVersion = (constraint: string) => isVersion(constraint) || (constraint.startsWith('==') && isVersion(constraint.substring(2).trim())); -module.exports = { +export { matches }; + +export const api: VersioningApi = { equals, getMajor, getMinor, @@ -57,3 +60,5 @@ module.exports = { getNewValue, sortVersions, }; + +export default api; diff --git a/lib/versioning/pep440/range.js b/lib/versioning/pep440/range.ts similarity index 81% rename from lib/versioning/pep440/range.js rename to lib/versioning/pep440/range.ts index 6d78fe39209d7bd601e044dbec3944ce78827865..0526b336b8a2e95b88ddfa4dffb32064af380deb 100644 --- a/lib/versioning/pep440/range.js +++ b/lib/versioning/pep440/range.ts @@ -1,16 +1,18 @@ -const { gte, lte, satisfies } = require('@renovate/pep440'); +import { gte, lte, satisfies } from '@renovate/pep440'; +import { parse as parseVersion } from '@renovate/pep440/lib/version'; +import { parse as parseRange } from '@renovate/pep440/lib/specifier'; +import { logger } from '../../logger'; +import { RangeStrategy } from '../common'; -const { parse: parseVersion } = require('@renovate/pep440/lib/version'); -const { parse: parseRange } = require('@renovate/pep440/lib/specifier'); -const { logger } = require('../../logger'); +export { getNewValue }; -module.exports = { - getNewValue, -}; - -function getFutureVersion(baseVersion, toVersion, step) { - const toRelease = parseVersion(toVersion).release; - const baseRelease = parseVersion(baseVersion).release; +function getFutureVersion( + baseVersion: string, + toVersion: string, + step: number +) { + const toRelease: number[] = parseVersion(toVersion).release; + const baseRelease: number[] = parseVersion(baseVersion).release; let found = false; const futureRelease = baseRelease.map((basePart, index) => { if (found) { @@ -29,12 +31,23 @@ function getFutureVersion(baseVersion, toVersion, step) { return futureRelease.join('.'); } -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +interface Range { + operator: string; + prefix: string; + version: string; +} + +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { // easy pin if (rangeStrategy === 'pin') { return '==' + toVersion; } - const ranges = parseRange(currentValue); + const ranges: Range[] = parseRange(currentValue); if (!ranges) { logger.warn('Invalid currentValue: ' + currentValue); return null; diff --git a/lib/versioning/poetry/index.js b/lib/versioning/poetry/index.ts similarity index 70% rename from lib/versioning/poetry/index.js rename to lib/versioning/poetry/index.ts index b073703b95dbd7d172cd14c7a945e41362508827..5ca4e130c1142b0278f3dc183974b980606eab9c 100644 --- a/lib/versioning/poetry/index.js +++ b/lib/versioning/poetry/index.ts @@ -1,15 +1,16 @@ -const { parseRange } = require('semver-utils'); -const { major, minor } = require('semver'); -const npm = require('../npm'); +import { parseRange } from 'semver-utils'; +import { major, minor } from 'semver'; +import { api as npm } from '../npm'; +import { RangeStrategy, VersioningApi } from '../common'; -function notEmpty(s) { +function notEmpty(s: string) { return s !== ''; } // This function works like cargo2npm, but it doesn't // add a '^', because poetry treats versions without operators as // exact versions. -function poetry2npm(input) { +function poetry2npm(input: string) { const versions = input .split(',') .map(str => str.trim()) @@ -20,7 +21,7 @@ function poetry2npm(input) { // NOTE: This function is copied from cargo versionsing code. // Poetry uses commas (like in cargo) instead of spaces (like in npm) // for AND operation. -function npm2poetry(input) { +function npm2poetry(input: string) { // Note: this doesn't remove the ^ const res = input .split(' ') @@ -36,22 +37,22 @@ function npm2poetry(input) { return res.join(', '); } -const isLessThanRange = (version, range) => +const isLessThanRange = (version: string, range: string) => npm.isLessThanRange(version, poetry2npm(range)); -const isValid = input => npm.isValid(poetry2npm(input)); +export const isValid = (input: string) => npm.isValid(poetry2npm(input)); -const isVersion = input => npm.isVersion(input); +const isVersion = (input: string) => npm.isVersion(input); +const matches = (version: string, range: string) => + npm.matches(version, poetry2npm(range)); -const matches = (version, range) => npm.matches(version, poetry2npm(range)); - -const maxSatisfyingVersion = (versions, range) => +const maxSatisfyingVersion = (versions: string[], range: string) => npm.maxSatisfyingVersion(versions, poetry2npm(range)); -const minSatisfyingVersion = (versions, range) => +const minSatisfyingVersion = (versions: string[], range: string) => npm.minSatisfyingVersion(versions, poetry2npm(range)); -const isSingleVersion = constraint => +const isSingleVersion = (constraint: string) => (constraint.trim().startsWith('=') && isVersion( constraint @@ -61,7 +62,12 @@ const isSingleVersion = constraint => )) || isVersion(constraint.trim()); -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + rangeStrategy: RangeStrategy, + fromVersion: string, + toVersion: string +) { if (rangeStrategy === 'replace') { const npmCurrentValue = poetry2npm(currentValue); const parsedRange = parseRange(npmCurrentValue); @@ -91,7 +97,11 @@ function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { return newPoetry; } -function handleShort(operator, currentValue, toVersion) { +function handleShort( + operator: string, + currentValue: string, + toVersion: string +) { const toVersionMajor = major(toVersion); const toVersionMinor = minor(toVersion); const split = currentValue.split('.'); @@ -106,7 +116,7 @@ function handleShort(operator, currentValue, toVersion) { return null; } -module.exports = { +export const api: VersioningApi = { ...npm, getNewValue, isLessThanRange, @@ -116,3 +126,4 @@ module.exports = { maxSatisfyingVersion, minSatisfyingVersion, }; +export default api; diff --git a/lib/versioning/ruby/index.js b/lib/versioning/ruby/index.js deleted file mode 100644 index 36036c9d4d8afa1969083b34511d762b94fd8645..0000000000000000000000000000000000000000 --- a/lib/versioning/ruby/index.js +++ /dev/null @@ -1,88 +0,0 @@ -const { - eq, - valid, - gt, - satisfies, - maxSatisfying, - minSatisfying, -} = require('@snyk/ruby-semver'); -const { logger } = require('../../logger'); -const { parse: parseVersion } = require('./version'); -const { parse: parseRange, ltr } = require('./range'); -const { isSingleOperator, isValidOperator } = require('./operator'); -const { pin, bump, replace } = require('./strategies'); - -const equals = (left, right) => eq(left, right); - -const getMajor = version => parseVersion(version).major; -const getMinor = version => parseVersion(version).minor; -const getPatch = version => parseVersion(version).patch; - -const isVersion = version => !!valid(version); -const isGreaterThan = (left, right) => gt(left, right); -const isLessThanRange = (version, range) => ltr(version, range); - -const isSingleVersion = range => { - const { version, operator } = parseRange(range); - - return operator - ? isVersion(version) && isSingleOperator(operator) - : isVersion(version); -}; - -const isStable = version => - parseVersion(version).prerelease ? false : isVersion(version); - -const isValid = input => - input - .split(',') - .map(piece => piece.trim()) - .every(range => { - const { version, operator } = parseRange(range); - - return operator - ? isVersion(version) && isValidOperator(operator) - : isVersion(version); - }); - -const matches = (version, range) => satisfies(version, range); -const maxSatisfyingVersion = (versions, range) => - maxSatisfying(versions, range); -const minSatisfyingVersion = (versions, range) => - minSatisfying(versions, range); - -const getNewValue = (currentValue, rangeStrategy, fromVersion, toVersion) => { - switch (rangeStrategy) { - case 'pin': - return pin({ to: toVersion }); - case 'bump': - return bump({ range: currentValue, to: toVersion }); - case 'replace': - return replace({ range: currentValue, to: toVersion }); - // istanbul ignore next - default: - logger.warn(`Unsupported strategy ${rangeStrategy}`); - return null; - } -}; - -const sortVersions = (left, right) => (gt(left, right) ? 1 : -1); - -module.exports = { - equals, - getMajor, - getMinor, - getPatch, - isCompatible: isVersion, - isGreaterThan, - isLessThanRange, - isSingleVersion, - isStable, - isValid, - isVersion, - matches, - maxSatisfyingVersion, - minSatisfyingVersion, - getNewValue, - sortVersions, -}; diff --git a/lib/versioning/ruby/index.ts b/lib/versioning/ruby/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..01c230b26bba6d4013fb13fd9be4552256fe8f5c --- /dev/null +++ b/lib/versioning/ruby/index.ts @@ -0,0 +1,97 @@ +import { + eq, + valid, + gt, + satisfies, + maxSatisfying, + minSatisfying, +} from '@snyk/ruby-semver'; +import { VersioningApi, RangeStrategy } from '../common'; +import { logger } from '../../logger'; +import { parse as parseVersion } from './version'; +import { parse as parseRange, ltr } from './range'; +import { isSingleOperator, isValidOperator } from './operator'; +import { pin, bump, replace } from './strategies'; + +const equals = (left: string, right: string) => eq(left, right); + +const getMajor = (version: string) => parseVersion(version).major; +const getMinor = (version: string) => parseVersion(version).minor; +const getPatch = (version: string) => parseVersion(version).patch; + +export const isVersion = (version: string) => !!valid(version); +const isGreaterThan = (left: string, right: string) => gt(left, right); +const isLessThanRange = (version: string, range: string) => ltr(version, range); + +const isSingleVersion = (range: string) => { + const { version, operator } = parseRange(range); + + return operator + ? isVersion(version) && isSingleOperator(operator) + : isVersion(version); +}; + +const isStable = (version: string) => + parseVersion(version).prerelease ? false : isVersion(version); + +export const isValid = (input: string) => + input + .split(',') + .map(piece => piece.trim()) + .every(range => { + const { version, operator } = parseRange(range); + + return operator + ? isVersion(version) && isValidOperator(operator) + : isVersion(version); + }); + +export const matches = (version: string, range: string) => + satisfies(version, range); +const maxSatisfyingVersion = (versions: string[], range: string) => + maxSatisfying(versions, range); +const minSatisfyingVersion = (versions: string[], range: string) => + minSatisfying(versions, range); + +const getNewValue = ( + currentValue: string, + rangeStrategy: RangeStrategy, + _fromVersion: string, + toVersion: string +) => { + switch (rangeStrategy) { + case 'pin': + return pin({ to: toVersion }); + case 'bump': + return bump({ range: currentValue, to: toVersion }); + case 'replace': + return replace({ range: currentValue, to: toVersion }); + // istanbul ignore next + default: + logger.warn(`Unsupported strategy ${rangeStrategy}`); + return null; + } +}; + +export const sortVersions = (left: string, right: string) => + gt(left, right) ? 1 : -1; + +export const api: VersioningApi = { + equals, + getMajor, + getMinor, + getPatch, + isCompatible: isVersion, + isGreaterThan, + isLessThanRange, + isSingleVersion, + isStable, + isValid, + isVersion, + matches, + maxSatisfyingVersion, + minSatisfyingVersion, + getNewValue, + sortVersions, +}; +export default api; diff --git a/lib/versioning/ruby/operator.js b/lib/versioning/ruby/operator.ts similarity index 67% rename from lib/versioning/ruby/operator.js rename to lib/versioning/ruby/operator.ts index 9a85b898bac58a1e12f06411e1e7c55aa6aecd7a..05e32a35e5beb5c1d3dd261bda448e42dc39e22a 100644 --- a/lib/versioning/ruby/operator.js +++ b/lib/versioning/ruby/operator.ts @@ -11,10 +11,10 @@ const PGTE = '~>'; const SINGLE = [EQUAL]; const ALL = [EQUAL, NOT_EQUAL, GT, LT, GTE, LTE, PGTE]; -const isValidOperator = operator => ALL.includes(operator); -const isSingleOperator = operator => SINGLE.includes(operator); +const isValidOperator = (operator: string) => ALL.includes(operator); +const isSingleOperator = (operator: string) => SINGLE.includes(operator); -module.exports = { +export { EQUAL, NOT_EQUAL, GT, diff --git a/lib/versioning/ruby/range.js b/lib/versioning/ruby/range.ts similarity index 54% rename from lib/versioning/ruby/range.js rename to lib/versioning/ruby/range.ts index bfcc1fcb2f4578c95768867eff7e9b8fba1230d1..e2f161f0e6daf5d020c4e53c0a94c1187c7fea4e 100644 --- a/lib/versioning/ruby/range.js +++ b/lib/versioning/ruby/range.ts @@ -1,9 +1,14 @@ -const GemVersion = require('@snyk/ruby-semver/lib/ruby/gem-version'); -const GemRequirement = require('@snyk/ruby-semver/lib/ruby/gem-requirement'); -const { logger } = require('../../logger'); -const { EQUAL, NOT_EQUAL, GT, LT, GTE, LTE, PGTE } = require('./operator'); +import { create } from '@snyk/ruby-semver/lib/ruby/gem-version'; +import { parse as _parse } from '@snyk/ruby-semver/lib/ruby/gem-requirement'; +import { logger } from '../../logger'; +import { EQUAL, NOT_EQUAL, GT, LT, GTE, LTE, PGTE } from './operator'; -const parse = range => { +export interface Range { + version: string; + operator: string; +} + +const parse = (range: string): Range => { const regExp = /^([^\d\s]+)?\s?([0-9a-zA-Z-.-]+)$/g; const value = (range || '').trim(); @@ -15,9 +20,16 @@ const parse = range => { }; }; -const ltr = (version, range) => { - const gemVersion = GemVersion.create(version); - const requirements = range.split(',').map(GemRequirement.parse); +interface GemVersion { + release(): GemVersion; + compare(ver: GemVersion): number; + bump(): GemVersion; +} +type GemRequirement = [string, GemVersion]; + +const ltr = (version: string, range: string) => { + const gemVersion: GemVersion = create(version); + const requirements: GemRequirement[] = range.split(',').map(_parse); const results = requirements.map(([operator, ver]) => { switch (operator) { @@ -44,7 +56,4 @@ const ltr = (version, range) => { return results.reduce((accumulator, value) => accumulator && value, true); }; -module.exports = { - parse, - ltr, -}; +export { parse, ltr }; diff --git a/lib/versioning/ruby/strategies/bump.js b/lib/versioning/ruby/strategies/bump.ts similarity index 68% rename from lib/versioning/ruby/strategies/bump.js rename to lib/versioning/ruby/strategies/bump.ts index 39b2549cd00703777967e0e835a084a72e185d31..723adedda7f1191db821b6e88e68f1e9a12c24f6 100644 --- a/lib/versioning/ruby/strategies/bump.js +++ b/lib/versioning/ruby/strategies/bump.ts @@ -1,10 +1,10 @@ -const { gte, lte } = require('@snyk/ruby-semver'); -const { logger } = require('../../../logger'); -const { EQUAL, NOT_EQUAL, GT, LT, GTE, LTE, PGTE } = require('../operator'); -const { floor, increment, decrement } = require('../version'); -const { parse: parseRange } = require('../range'); +import { gte, lte } from '@snyk/ruby-semver'; +import { logger } from '../../../logger'; +import { EQUAL, NOT_EQUAL, GT, LT, GTE, LTE, PGTE } from '../operator'; +import { floor, increment, decrement } from '../version'; +import { parse as parseRange } from '../range'; -module.exports = ({ range, to }) => { +export default ({ range, to }: { range: string; to: string }) => { const ranges = range.split(',').map(parseRange); const results = ranges.map(({ operator, version: ver }) => { switch (operator) { diff --git a/lib/versioning/ruby/strategies/index.js b/lib/versioning/ruby/strategies/index.js deleted file mode 100644 index a1bc4517ce57a63be970a010f13af6a44b83d18e..0000000000000000000000000000000000000000 --- a/lib/versioning/ruby/strategies/index.js +++ /dev/null @@ -1,9 +0,0 @@ -const pin = require('./pin'); -const bump = require('./bump'); -const replace = require('./replace'); - -module.exports = { - pin, - bump, - replace, -}; diff --git a/lib/versioning/ruby/strategies/index.ts b/lib/versioning/ruby/strategies/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a6736d5fd278ec9a770e342093f36db67fa8e76 --- /dev/null +++ b/lib/versioning/ruby/strategies/index.ts @@ -0,0 +1,5 @@ +import pin from './pin'; +import bump from './bump'; +import replace from './replace'; + +export { pin, bump, replace }; diff --git a/lib/versioning/ruby/strategies/pin.js b/lib/versioning/ruby/strategies/pin.js deleted file mode 100644 index 071862075c515995e6063bbaf1908249bea249ce..0000000000000000000000000000000000000000 --- a/lib/versioning/ruby/strategies/pin.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ({ to }) => to; diff --git a/lib/versioning/ruby/strategies/pin.ts b/lib/versioning/ruby/strategies/pin.ts new file mode 100644 index 0000000000000000000000000000000000000000..279563363617c24ceca21ebb980994a79f2fb440 --- /dev/null +++ b/lib/versioning/ruby/strategies/pin.ts @@ -0,0 +1 @@ +export default ({ to }: { to: string }) => to; diff --git a/lib/versioning/ruby/strategies/replace.js b/lib/versioning/ruby/strategies/replace.ts similarity index 65% rename from lib/versioning/ruby/strategies/replace.js rename to lib/versioning/ruby/strategies/replace.ts index 091013e39650a83479af173db2c699576a764651..927c9a7b1d8aab3cd238837c76b2645255384ccb 100644 --- a/lib/versioning/ruby/strategies/replace.js +++ b/lib/versioning/ruby/strategies/replace.ts @@ -1,7 +1,7 @@ -const { satisfies } = require('@snyk/ruby-semver'); -const bump = require('./bump'); +import { satisfies } from '@snyk/ruby-semver'; +import bump from './bump'; -module.exports = ({ to, range }) => { +export default ({ to, range }: { range: string; to: string }) => { if (satisfies(to, range)) { return range; } diff --git a/lib/versioning/ruby/version.js b/lib/versioning/ruby/version.ts similarity index 59% rename from lib/versioning/ruby/version.js rename to lib/versioning/ruby/version.ts index 5e3c2ff93dc5898d97051ca9e3f3b23f9f3eafd3..e46d249b0af6e8fe537295cfc3a76cd5d4de82aa 100644 --- a/lib/versioning/ruby/version.js +++ b/lib/versioning/ruby/version.ts @@ -1,23 +1,30 @@ -const last = require('lodash/last'); -const GemVersion = require('@snyk/ruby-semver/lib/ruby/gem-version'); -const { diff, major, minor, patch, prerelease } = require('@snyk/ruby-semver'); - -const parse = version => ({ +import last from 'lodash/last'; +import { create } from '@snyk/ruby-semver/lib/ruby/gem-version'; +import { diff, major, minor, patch, prerelease } from '@snyk/ruby-semver'; + +interface RubyVersion { + major: number; + minor: number; + patch: number; + prerelease: string[]; +} + +const parse = (version: string): RubyVersion => ({ major: major(version), minor: minor(version), patch: patch(version), prerelease: prerelease(version), }); -const adapt = (left, right) => +const adapt = (left: string, right: string) => left .split('.') .slice(0, right.split('.').length) .join('.'); -const floor = version => +const floor = (version: string) => [ - ...GemVersion.create(version) + ...create(version) .release() .getSegments() .slice(0, -1), @@ -25,8 +32,8 @@ const floor = version => ].join('.'); // istanbul ignore next -const incrementLastSegment = version => { - const segments = GemVersion.create(version) +const incrementLastSegment = (version: string) => { + const segments = create(version) .release() .getSegments(); const nextLast = parseInt(last(segments), 10) + 1; @@ -35,21 +42,26 @@ const incrementLastSegment = version => { }; // istanbul ignore next -const incrementMajor = (maj, min, ptch, pre) => - min === 0 || ptch === 0 || pre.length === 0 ? maj + 1 : maj; +const incrementMajor = ( + maj: number, + min: number, + ptch: number, + pre: string[] +) => (min === 0 || ptch === 0 || pre.length === 0 ? maj + 1 : maj); // istanbul ignore next -const incrementMinor = (min, ptch, pre) => +const incrementMinor = (min: number, ptch: number, pre: string[]) => ptch === 0 || pre.length === 0 ? min + 1 : min; // istanbul ignore next -const incrementPatch = (ptch, pre) => (pre.length === 0 ? ptch + 1 : ptch); +const incrementPatch = (ptch: number, pre: string[]) => + pre.length === 0 ? ptch + 1 : ptch; // istanbul ignore next -const increment = (from, to) => { +const increment = (from: string, to: string): string => { const { major: maj, minor: min, patch: ptch, prerelease: pre } = parse(from); - let nextVersion; + let nextVersion: string; switch (diff(from, adapt(to, from))) { case 'major': nextVersion = [incrementMajor(maj, min, ptch, pre || []), 0, 0].join('.'); @@ -71,13 +83,13 @@ const increment = (from, to) => { }; // istanbul ignore next -const decrement = version => { - const segments = GemVersion.create(version) +const decrement = (version: string): string => { + const segments = create(version) .release() .getSegments(); const nextSegments = segments .reverse() - .reduce((accumulator, segment, index) => { + .reduce((accumulator: number[], segment: number, index: number) => { if (index === 0) { return [segment - 1]; } @@ -92,9 +104,4 @@ const decrement = version => { return nextSegments.reverse().join('.'); }; -module.exports = { - parse, - floor, - increment, - decrement, -}; +export { parse, floor, increment, decrement }; diff --git a/lib/versioning/semver/index.js b/lib/versioning/semver/index.ts similarity index 61% rename from lib/versioning/semver/index.js rename to lib/versioning/semver/index.ts index d702f9abc1490d40c56bc119d6292c65efcbd509..cadf2f520af4aead283d43f34b0a568808e2dd9c 100644 --- a/lib/versioning/semver/index.js +++ b/lib/versioning/semver/index.ts @@ -1,5 +1,6 @@ -const semver = require('semver'); -const stable = require('semver-stable'); +import semver from 'semver'; +import stable from 'semver-stable'; +import { RangeStrategy, VersioningApi } from '../common'; const { is: isStable } = stable; @@ -18,13 +19,20 @@ const { } = semver; // If this is left as an alias, inputs like "17.04.0" throw errors -const isVersion = input => valid(input); +export const isVersion = (input: string) => valid(input); -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +export { isVersion as isValid, maxSatisfyingVersion }; + +function getNewValue( + _currentValue: string, + _rangeStrategy: RangeStrategy, + _fromVersion: string, + toVersion: string +) { return toVersion; } -module.exports = { +export const api: VersioningApi = { equals, getMajor, getMinor, @@ -42,3 +50,4 @@ module.exports = { getNewValue, sortVersions, }; +export default api; diff --git a/lib/versioning/swift/index.js b/lib/versioning/swift/index.js deleted file mode 100644 index 5d6934066cc9222fbba57a49c545755298ac705a..0000000000000000000000000000000000000000 --- a/lib/versioning/swift/index.js +++ /dev/null @@ -1,48 +0,0 @@ -const semver = require('semver'); -const stable = require('semver-stable'); -const { toSemverRange, getNewValue } = require('./range'); - -const { is: isStable } = stable; - -const { - compare: sortVersions, - maxSatisfying, - minSatisfying, - major: getMajor, - minor: getMinor, - patch: getPatch, - satisfies, - valid, - validRange, - ltr, - gt: isGreaterThan, - eq: equals, -} = semver; - -const isValid = input => !!valid(input) || !!validRange(toSemverRange(input)); -const isVersion = input => !!valid(input); -const maxSatisfyingVersion = (versions, range) => - maxSatisfying(versions, toSemverRange(range)); -const minSatisfyingVersion = (versions, range) => - minSatisfying(versions, toSemverRange(range)); -const isLessThanRange = (version, range) => ltr(version, toSemverRange(range)); -const matches = (version, range) => satisfies(version, toSemverRange(range)); - -module.exports = { - equals, - getMajor, - getMinor, - getNewValue, - getPatch, - isCompatible: isVersion, - isGreaterThan, - isLessThanRange, - isSingleVersion: isVersion, - isStable, - isValid, - isVersion, - matches, - maxSatisfyingVersion, - minSatisfyingVersion, - sortVersions, -}; diff --git a/lib/versioning/swift/index.ts b/lib/versioning/swift/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f0df3a22646e5a52ee9fdac1e41edb7818174bc --- /dev/null +++ b/lib/versioning/swift/index.ts @@ -0,0 +1,54 @@ +import semver from 'semver'; +import stable from 'semver-stable'; +import { toSemverRange, getNewValue } from './range'; +import { VersioningApi } from '../common'; + +const { is: isStable } = stable; + +const { + compare: sortVersions, + maxSatisfying, + minSatisfying, + major: getMajor, + minor: getMinor, + patch: getPatch, + satisfies, + valid, + validRange, + ltr, + gt: isGreaterThan, + eq: equals, +} = semver; + +export const isValid = (input: string) => + !!valid(input) || !!validRange(toSemverRange(input)); +export const isVersion = (input: string) => !!valid(input); +const maxSatisfyingVersion = (versions: string[], range: string) => + maxSatisfying(versions, toSemverRange(range)); +const minSatisfyingVersion = (versions: string[], range: string) => + minSatisfying(versions, toSemverRange(range)); +const isLessThanRange = (version: string, range: string) => + ltr(version, toSemverRange(range)); +const matches = (version: string, range: string) => + satisfies(version, toSemverRange(range)); + +export const api: VersioningApi = { + equals, + getMajor, + getMinor, + getNewValue, + getPatch, + isCompatible: isVersion, + isGreaterThan, + isLessThanRange, + isSingleVersion: isVersion, + isStable, + isValid, + isVersion, + matches, + maxSatisfyingVersion, + minSatisfyingVersion, + sortVersions, +}; + +export default api; diff --git a/lib/versioning/swift/range.js b/lib/versioning/swift/range.ts similarity index 85% rename from lib/versioning/swift/range.js rename to lib/versioning/swift/range.ts index 4b9e3f36b9743e1737f5d2ec0750c0f86a7bd14a..9552cf45758f60ab430d309ea6ed548566ffe23b 100644 --- a/lib/versioning/swift/range.js +++ b/lib/versioning/swift/range.ts @@ -1,11 +1,12 @@ -const semver = require('semver'); +import semver from 'semver'; +import { RangeStrategy } from '../common'; const fromParam = /^\s*from\s*:\s*"([^"]+)"\s*$/; const fromRange = /^\s*"([^"]+)"\s*\.\.\.\s*$/; const binaryRange = /^\s*"([^"]+)"\s*(\.\.[.<])\s*"([^"]+)"\s*$/; const toRange = /^\s*(\.\.[.<])\s*"([^"]+)"\s*$/; -function toSemverRange(range) { +function toSemverRange(range: string) { if (fromParam.test(range)) { const [, version] = range.match(fromParam); if (semver.valid(version)) { @@ -33,7 +34,12 @@ function toSemverRange(range) { return null; } -function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { +function getNewValue( + currentValue: string, + _rangeStrategy: RangeStrategy, + _fromVersion: string, + toVersion: string +) { if (fromParam.test(currentValue)) { return toVersion; } @@ -52,7 +58,4 @@ function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) { return currentValue; } -module.exports = { - toSemverRange, - getNewValue, -}; +export { toSemverRange, getNewValue }; diff --git a/test/.eslintrc.js b/test/.eslintrc.js index d0977a46a3276abb2fd3bd5141d7fcc6dcddfa64..ab1eca82ccbee6608ff40ca220237687f87007d6 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -8,7 +8,9 @@ module.exports = { rules: { 'prefer-destructuring': 0, 'prefer-promise-reject-errors': 0, + 'import/no-dynamic-require': 0, 'import/no-extraneous-dependencies': 0, + 'import/no-named-as-default-member': 0, 'global-require': 0, }, }; diff --git a/test/versioning/__snapshots__/index.spec.ts.snap b/test/versioning/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..17ac4c295a332306369cdb2674221eea49a6edfb --- /dev/null +++ b/test/versioning/__snapshots__/index.spec.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`versioning.get(versionScheme) has api 1`] = ` +Array [ + "equals", + "getMajor", + "getMinor", + "getNewValue", + "getPatch", + "isCompatible", + "isGreaterThan", + "isLessThanRange", + "isSingleVersion", + "isStable", + "isValid", + "isVersion", + "matches", + "maxSatisfyingVersion", + "minSatisfyingVersion", + "sortVersions", +] +`; diff --git a/test/versioning/__snapshots__/loose.spec.ts.snap b/test/versioning/__snapshots__/loose.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..197f6bb52e49b9fd3ddde23d1075992639502f46 --- /dev/null +++ b/test/versioning/__snapshots__/loose.spec.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`loose. isVersion 1.1 1`] = `"1.1"`; + +exports[`loose. isVersion 1.3.RC2 1`] = `"1.3.RC2"`; + +exports[`loose. isVersion 2.1-rc2 1`] = `"2.1-rc2"`; diff --git a/test/versioning/__snapshots__/ruby.spec.js.snap b/test/versioning/__snapshots__/ruby.spec.ts.snap similarity index 100% rename from test/versioning/__snapshots__/ruby.spec.js.snap rename to test/versioning/__snapshots__/ruby.spec.ts.snap diff --git a/test/versioning/cargo.spec.js b/test/versioning/cargo.spec.ts similarity index 99% rename from test/versioning/cargo.spec.js rename to test/versioning/cargo.spec.ts index d54a7790c14128fcde2ee2ca92072ae20bc091c5..a1a1524477da39c2d8df4bebc7c5e963a9fd405b 100644 --- a/test/versioning/cargo.spec.js +++ b/test/versioning/cargo.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/cargo'); +import { api as semver } from '../../lib/versioning/cargo'; describe('semver.matches()', () => { it('handles comma', () => { diff --git a/test/versioning/composer.spec.js b/test/versioning/composer.spec.ts similarity index 98% rename from test/versioning/composer.spec.js rename to test/versioning/composer.spec.ts index 33932f5c938630478d1a3754790bdd06a6757eca..6cee2aebadc23300e05fbbb82e8a60bc60299cb9 100644 --- a/test/versioning/composer.spec.js +++ b/test/versioning/composer.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/composer'); +import { api as semver } from '../../lib/versioning/composer'; describe('semver.getPatch(input)', () => { it('gets patch', () => { diff --git a/test/versioning/docker.spec.js b/test/versioning/docker.spec.ts similarity index 97% rename from test/versioning/docker.spec.js rename to test/versioning/docker.spec.ts index 345aa31c0b7b914c6494002f48aee8521da9de6d..0db20ffe5f65bbc963a02d894b1af563f41d140c 100644 --- a/test/versioning/docker.spec.js +++ b/test/versioning/docker.spec.ts @@ -1,6 +1,5 @@ -/** @type any */ -const docker = require('../../lib/versioning/docker'); -const semver = require('../../lib/versioning/semver'); +import docker from '../../lib/versioning/docker'; +import semver from '../../lib/versioning/semver'; describe('docker.', () => { describe('isValid(version)', () => { diff --git a/test/versioning/hashicorp.spec.js b/test/versioning/hashicorp.spec.ts similarity index 96% rename from test/versioning/hashicorp.spec.js rename to test/versioning/hashicorp.spec.ts index bdaac2550b41eafa5d541797cddb44e3a89aa286..72e6d50396abe4a3341bb5eb5d41ef808abab58e 100644 --- a/test/versioning/hashicorp.spec.js +++ b/test/versioning/hashicorp.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/hashicorp'); +import { api as semver } from '../../lib/versioning/hashicorp'; describe('semver.matches()', () => { it('handles tilde greater than', () => { diff --git a/test/versioning/hex.spec.js b/test/versioning/hex.spec.ts similarity index 98% rename from test/versioning/hex.spec.js rename to test/versioning/hex.spec.ts index 8aca8e573e70ad7fd43285e80344e3bb4c4a79bc..c86d93f4e35db3fedf1860931c3a9af2e53c115e 100644 --- a/test/versioning/hex.spec.js +++ b/test/versioning/hex.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/hex'); +import { api as semver } from '../../lib/versioning/hex'; describe('lib/versioning/hex', () => { describe('semver.matches()', () => { diff --git a/test/versioning/index.spec.js b/test/versioning/index.spec.js deleted file mode 100644 index c2dc5c00cc68fdb28150e006c33d4520e3177ce8..0000000000000000000000000000000000000000 --- a/test/versioning/index.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -const versioning = require('../../lib/versioning'); -const supportedSchemes = require('../../lib/config/definitions') - .getOptions() - .find(option => option.name === 'versionScheme').allowedValues; - -describe('versioning.get(versionScheme)', () => { - it('should fallback to semver', () => { - expect(versioning.get(undefined)).toBe(versioning.get('semver')); - expect(versioning.get('unknown')).toBe(versioning.get('semver')); - }); - - it('should return the same interface', () => { - const optionalFunctions = ['isLessThanRange', 'valueToVersion']; - const npmApi = Object.keys(versioning.get('semver')) - .filter(val => !optionalFunctions.includes(val)) - .sort(); - for (const supportedScheme of supportedSchemes.filter( - scheme => scheme !== 'npm' - )) { - const schemeKeys = Object.keys(versioning.get(supportedScheme)) - .filter(val => !optionalFunctions.includes(val)) - .sort(); - expect(schemeKeys).toEqual(npmApi); - } - }); -}); diff --git a/test/versioning/index.spec.ts b/test/versioning/index.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..df061824e5aeb24b7adb328ec1137ccb36a9f95f --- /dev/null +++ b/test/versioning/index.spec.ts @@ -0,0 +1,37 @@ +import * as versioning from '../../lib/versioning'; +import { getOptions } from '../../lib/config/definitions'; + +const supportedSchemes = getOptions().find( + option => option.name === 'versionScheme' +).allowedValues; + +describe('versioning.get(versionScheme)', () => { + it('has api', () => { + expect(Object.keys(versioning.get('semver')).sort()).toMatchSnapshot(); + }); + + it('should fallback to semver', () => { + expect(versioning.get(undefined)).toBe(versioning.get('semver')); + expect(versioning.get('unknown')).toBe(versioning.get('semver')); + }); + + describe('should return the same interface', () => { + const optionalFunctions = ['isLessThanRange', 'valueToVersion']; + const npmApi = Object.keys(versioning.get('semver')) + .filter(val => !optionalFunctions.includes(val)) + .sort(); + for (const supportedScheme of supportedSchemes) { + it(supportedScheme, () => { + const schemeKeys = Object.keys(versioning.get(supportedScheme)) + .filter(val => !optionalFunctions.includes(val)) + .sort(); + expect(schemeKeys).toEqual(npmApi); + expect( + Object.keys( + require('../../lib/versioning/' + supportedScheme).api + ).sort() + ).toEqual(Object.keys(versioning.get(supportedScheme)).sort()); + }); + } + }); +}); diff --git a/test/versioning/ivy.spec.js b/test/versioning/ivy.spec.ts similarity index 97% rename from test/versioning/ivy.spec.js rename to test/versioning/ivy.spec.ts index b9e86e05ce42d3389749f0019f0741b6dd710e18..23ebff9cfc09a67fdcb80e37847cb992ee724b5f 100644 --- a/test/versioning/ivy.spec.js +++ b/test/versioning/ivy.spec.ts @@ -1,11 +1,12 @@ -const { +import { REV_TYPE_LATEST, REV_TYPE_SUBREV, REV_TYPE_RANGE, parseDynamicRevision, -} = require('../../lib/versioning/ivy/parse'); +} from '../../lib/versioning/ivy/parse'; +import ivy from '../../lib/versioning/ivy'; -const { isVersion, matches } = require('../../lib/versioning/ivy/index'); +const { isVersion, matches } = ivy; describe('versioning/ivy/match', () => { it('parses dynamic revisions', () => { diff --git a/test/versioning/loose.spec.js b/test/versioning/loose.spec.ts similarity index 81% rename from test/versioning/loose.spec.js rename to test/versioning/loose.spec.ts index 073a591b91cac841b449fd2a502e61f4a8575660..5b04940364582728e5d976ac389719722e6f1aa2 100644 --- a/test/versioning/loose.spec.js +++ b/test/versioning/loose.spec.ts @@ -1,6 +1,13 @@ -const loose = require('../../lib/versioning/loose'); +import loose from '../../lib/versioning/loose'; describe('loose.', () => { + describe('isVersion', () => { + ['1.1', '1.3.RC2', '2.1-rc2'].forEach(version => { + it(version, () => { + expect(loose.isVersion(version)).toMatchSnapshot(); + }); + }); + }); describe('isValid(version)', () => { it('it must support varied precision, from 1 to 6 sections', () => { [ diff --git a/test/versioning/maven.spec.js b/test/versioning/maven.spec.ts similarity index 93% rename from test/versioning/maven.spec.js rename to test/versioning/maven.spec.ts index 9537ea58039c35c1a083ffdfcfae6966da946bcf..d82fd4b859463ee305ec1fc2f4a544cf99bbd6e9 100644 --- a/test/versioning/maven.spec.js +++ b/test/versioning/maven.spec.ts @@ -1,9 +1,11 @@ -const { +import { compare, parseRange, rangeToStr, autoExtendMavenRange, -} = require('../../lib/versioning/maven/compare'); +} from '../../lib/versioning/maven/compare'; +import maven from '../../lib/versioning/maven'; + const { isValid, isVersion, @@ -13,7 +15,7 @@ const { getPatch, matches, getNewValue, -} = require('../../lib/versioning/maven/index'); +} = maven; describe('versioning/maven/compare', () => { it('returns equality', () => { @@ -251,7 +253,7 @@ describe('versioning/maven/index', () => { it('returns valid', () => { expect(isValid('1.0.0')).toBe(true); expect(isValid('[1.12.6,1.18.6]')).toBe(true); - expect(isValid()).toBe(false); + expect(isValid(undefined)).toBe(false); }); it('validates version string', () => { expect(isVersion('')).toBe(false); @@ -259,6 +261,7 @@ describe('versioning/maven/index', () => { expect(isVersion('0')).toBe(true); expect(isVersion('0.1-2-sp')).toBe(true); expect(isVersion('1-final')).toBe(true); + expect(isVersion('1-foo')).toBe(true); expect(isVersion('v1.0.0')).toBe(true); expect(isVersion('x1.0.0')).toBe(true); expect(isVersion('2.1.1.RELEASE')).toBe(true); @@ -272,6 +275,7 @@ describe('versioning/maven/index', () => { it('checks if version is stable', () => { expect(isStable('')).toBeNull(); expect(isStable('foobar')).toBe(false); + expect(isStable('final')).toBe(true); expect(isStable('1')).toBe(true); expect(isStable('1.2')).toBe(true); expect(isStable('1.2.3')).toBe(true); @@ -279,6 +283,9 @@ describe('versioning/maven/index', () => { expect(isStable('v1.2.3.4')).toBe(true); expect(isStable('1-alpha-1')).toBe(false); expect(isStable('1-b1')).toBe(false); + expect(isStable('1-foo')).toBe(false); + expect(isStable('1-final-1.0.0')).toBe(true); + expect(isStable('1-release')).toBe(true); expect(isStable('1.final')).toBe(true); expect(isStable('1.0milestone1')).toBe(false); expect(isStable('1-sp')).toBe(true); @@ -326,6 +333,16 @@ describe('versioning/maven/index', () => { expect(matches('1', '(0,1),(1,2)')).toBe(false); expect(matches('1.0.0.RC9.2', '(,1.0.0.RC9.2),(1.0.0.RC9.2,)')).toBe(false); expect(matches('1.0.0-RC14', '(,1.0.0.RC9.2),(1.0.0.RC9.2,)')).toBe(true); + expect(matches('0', '')).toBe(false); + expect(matches('1', '1')).toBe(true); + expect(matches('1', '(1')).toBe(false); + }); + + it('api', () => { + expect(maven.isGreaterThan('1.1', '1')).toBe(true); + expect(maven.maxSatisfyingVersion(['1'], '1')).toBe('1'); + expect(maven.getNewValue('1', null, null, '1.1')).toBe('1.1'); + expect(maven.getNewValue('[1.2.3,]', null, null, '1.2.4')).toBe('[1.2.3,]'); }); it('pins maven ranges', () => { const sample = [ diff --git a/test/versioning/node.spec.js b/test/versioning/node.spec.ts similarity index 84% rename from test/versioning/node.spec.js rename to test/versioning/node.spec.ts index e5ca0930a045a19a85417db9b315507f37657a18..22923ffa18017d8d3261524bb12987ad30a50f6e 100644 --- a/test/versioning/node.spec.js +++ b/test/versioning/node.spec.ts @@ -1,4 +1,4 @@ -const nodever = require('../../lib/versioning/node'); +import { api as nodever } from '../../lib/versioning/node'; describe('semver.getNewValue()', () => { it('returns normalized toVersion', () => { diff --git a/test/versioning/npm.spec.js b/test/versioning/npm.spec.ts similarity index 98% rename from test/versioning/npm.spec.js rename to test/versioning/npm.spec.ts index 5b18351809b6f6019671a51c433b7d44ef7b309d..5df9076edeed0928bf3d0c19c713b16d486e4a23 100644 --- a/test/versioning/npm.spec.js +++ b/test/versioning/npm.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/npm'); +import { api as semver } from '../../lib/versioning/npm'; describe('semver.isValid(input)', () => { it('should return null for irregular versions', () => { diff --git a/test/versioning/pep440.spec.js b/test/versioning/pep440.spec.ts similarity index 98% rename from test/versioning/pep440.spec.js rename to test/versioning/pep440.spec.ts index 261d3d28f73d1e5ff328464ddba4099b6838d6ea..b35ab1758424484bc87ed5a74891eb46336a4941 100644 --- a/test/versioning/pep440.spec.js +++ b/test/versioning/pep440.spec.ts @@ -1,4 +1,4 @@ -const pep440 = require('../../lib/versioning/pep440'); +import pep440 from '../../lib/versioning/pep440'; describe('pep440.isValid(input)', () => { it('should return null for irregular versions', () => { diff --git a/test/versioning/poetry.spec.js b/test/versioning/poetry.spec.ts similarity index 99% rename from test/versioning/poetry.spec.js rename to test/versioning/poetry.spec.ts index be99521c5e62a78dd08f20f43e717c9dae845b5a..b0e792c79ae816a343cc9c4459424f8b09456d2e 100644 --- a/test/versioning/poetry.spec.js +++ b/test/versioning/poetry.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/poetry'); +import { api as semver } from '../../lib/versioning/poetry'; describe('semver.isValid(input)', () => { it('should return null for irregular versions', () => { diff --git a/test/versioning/ruby.spec.js b/test/versioning/ruby.spec.ts similarity index 97% rename from test/versioning/ruby.spec.js rename to test/versioning/ruby.spec.ts index 65384b64ccbfc35b589009eed95a09f02341a19a..5d8d47cd6a690deced94e1b7e5d1a65bb5c0fc8e 100644 --- a/test/versioning/ruby.spec.js +++ b/test/versioning/ruby.spec.ts @@ -1,4 +1,4 @@ -const semverRuby = require('../../lib/versioning/ruby'); +import { api as semverRuby } from '../../lib/versioning/ruby'; describe('semverRuby', () => { describe('.equals', () => { @@ -61,7 +61,7 @@ describe('semverRuby', () => { }); it('returns false when version is invalid', () => { - expect(semverRuby.isVersion()).toBe(false); + expect(semverRuby.isVersion(undefined)).toBe(false); expect(semverRuby.isVersion('')).toBe(false); expect(semverRuby.isVersion(null)).toBe(false); expect(semverRuby.isVersion('tottally-not-a-version')).toBe(false); @@ -137,7 +137,7 @@ describe('semverRuby', () => { }); it('returns false when version is invalid', () => { - expect(semverRuby.isStable()).toBe(false); + expect(semverRuby.isStable(undefined)).toBe(false); expect(semverRuby.isStable('')).toBe(false); expect(semverRuby.isStable(null)).toBe(false); expect(semverRuby.isStable('tottally-not-a-version')).toBe(false); @@ -294,7 +294,7 @@ describe('semverRuby', () => { }); it('returns false when version is invalid', () => { - expect(semverRuby.isVersion()).toBe(false); + expect(semverRuby.isVersion(undefined)).toBe(false); expect(semverRuby.isVersion('')).toBe(false); expect(semverRuby.isVersion(null)).toBe(false); expect(semverRuby.isVersion('tottally-not-a-version')).toBe(false); @@ -338,7 +338,7 @@ describe('semverRuby', () => { }); it('returns false when version is invalid', () => { - expect(semverRuby.isSingleVersion()).toBe(false); + expect(semverRuby.isSingleVersion(undefined)).toBe(false); expect(semverRuby.isSingleVersion('')).toBe(false); expect(semverRuby.isSingleVersion(null)).toBe(false); expect(semverRuby.isSingleVersion('tottally-not-a-version')).toBe(false); @@ -358,6 +358,7 @@ describe('semverRuby', () => { ['1.2.3', '~> 1.0.3', 'pin', '1.0.4', '1.2.3'], ['4.7.8', '~> 4.7, >= 4.7.4', 'pin', '4.7.5', '4.7.8'], ].forEach(([expected, ...params]) => { + // @ts-ignore expect(semverRuby.getNewValue(...params)).toEqual(expected); }); }); @@ -379,6 +380,7 @@ describe('semverRuby', () => { ['~> 1.0.0', '~> 1.0.3', 'bump', '1.0.3', '1.0.4'], ['~> 4.7.0, >= 4.7.9', '~> 4.7, >= 4.7.4', 'bump', '4.7.5', '4.7.9'], ].forEach(([expected, ...params]) => { + // @ts-ignore expect(semverRuby.getNewValue(...params)).toEqual(expected); }); }); @@ -414,6 +416,7 @@ describe('semverRuby', () => { '2.20.0', ], ].forEach(([expected, ...params]) => { + // @ts-ignore expect(semverRuby.getNewValue(...params)).toEqual(expected); }); }); diff --git a/test/versioning/semver.spec.js b/test/versioning/semver.spec.ts similarity index 96% rename from test/versioning/semver.spec.js rename to test/versioning/semver.spec.ts index 30d80432bfeeec6e871fdfa6e1971aa88bd5ae34..c6493f8fec55ca15a966f793f1b9fc0f2138bb92 100644 --- a/test/versioning/semver.spec.js +++ b/test/versioning/semver.spec.ts @@ -1,4 +1,4 @@ -const semver = require('../../lib/versioning/semver'); +import semver from '../../lib/versioning/semver'; describe('semver.isValid(input)', () => { it('should return null for irregular versions', () => { diff --git a/test/versioning/swift.spec.js b/test/versioning/swift.spec.ts similarity index 97% rename from test/versioning/swift.spec.js rename to test/versioning/swift.spec.ts index ad86d2009041b872bc26e43f28ac8de3180d4f27..e9c33ed0ccc7c4850753c51df6fae63bc7122b40 100644 --- a/test/versioning/swift.spec.js +++ b/test/versioning/swift.spec.ts @@ -1,3 +1,5 @@ +import swift from '../../lib/versioning/swift'; + const { getNewValue, isValid, @@ -5,7 +7,7 @@ const { maxSatisfyingVersion, isLessThanRange, matches, -} = require('../../lib/versioning/swift'); +} = swift; describe('isValid(input)', () => { it('understands Swift version ranges', () => { @@ -78,6 +80,7 @@ describe('getNewValue()', () => { ['..."1.2.4"', 'auto', '1.2.3', '1.2.5', '..."1.2.5"'], ['..<"1.2.4"', 'auto', '1.2.3', '1.2.5', '..<"1.2.5"'], ].forEach(([range, strategy, fromVersion, toVersion, result]) => { + // @ts-ignore const newValue = getNewValue(range, strategy, fromVersion, toVersion); expect(newValue).toEqual(result); }); diff --git a/test/versioning/versioning-readmes.spec.js b/test/versioning/versioning-readmes.spec.ts similarity index 75% rename from test/versioning/versioning-readmes.spec.js rename to test/versioning/versioning-readmes.spec.ts index 6cc98995da753d2280f91ad93ecbc1505f87f866..4d7afafe337f1e6233562d7326f8377af8a9d5f2 100644 --- a/test/versioning/versioning-readmes.spec.js +++ b/test/versioning/versioning-readmes.spec.ts @@ -1,15 +1,15 @@ -const fs = require('fs-extra'); +import { readdir, readFile } from 'fs-extra'; describe('versioning readmes', () => { it('has same questions for all version schemes', async () => { - const managers = (await fs.readdir('lib/versioning')).filter( + const managers = (await readdir('lib/versioning')).filter( item => !item.includes('.') ); - let expectedHeaders; + let expectedHeaders: string[]; for (const manager of managers) { - let readme; + let readme: string; try { - readme = await fs.readFile( + readme = await readFile( 'lib/versioning/' + manager + '/readme.md', 'utf8' );