npm-check-updates
Version:
Find newer versions of dependencies than what your package.json allows
518 lines • 22.8 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.upgradeGithubUrl = exports.upgradeDependencyDeclaration = exports.getGithubUrlTag = exports.isGithubUrl = exports.upgradeNpmAlias = exports.isNpmAlias = exports.parseNpmAlias = exports.createNpmAlias = exports.v = exports.fixPseudoVersion = exports.isPre = exports.findGreatestByLevel = exports.compareVersions = exports.isComparable = exports.colorizeDiff = exports.getDependencyGroups = exports.partChanged = exports.isWildPart = exports.isWildCard = exports.addWildCard = exports.setPrecision = exports.getPrecision = exports.stringify = exports.precisionAdd = exports.numParts = exports.WILDCARDS = exports.DEFAULT_WILDCARD = void 0;
const ary_1 = __importDefault(require("lodash/ary"));
const curry_1 = __importDefault(require("lodash/curry"));
const flow_1 = __importDefault(require("lodash/flow"));
const intersection_1 = __importDefault(require("lodash/intersection"));
const last_1 = __importDefault(require("lodash/last"));
const propertyOf_1 = __importDefault(require("lodash/propertyOf"));
const reject_1 = __importDefault(require("lodash/reject"));
const sortBy_1 = __importDefault(require("lodash/sortBy"));
const uniq_1 = __importDefault(require("lodash/uniq"));
const parse_github_url_1 = __importDefault(require("parse-github-url"));
const semver_1 = __importDefault(require("semver"));
const semver_utils_1 = __importStar(require("semver-utils"));
const util_1 = __importDefault(require("util"));
const chalk_1 = __importDefault(require("./chalk"));
const keyValueBy_1 = require("./keyValueBy");
const VERSION_BASE_PARTS = ['major', 'minor', 'patch'];
const VERSION_ADDED_PARTS = ['release', 'build'];
const VERSION_PARTS = [...VERSION_BASE_PARTS, ...VERSION_ADDED_PARTS];
const VERSION_PART_DELIM = {
major: '',
minor: '.',
patch: '.',
release: '-',
build: '+',
};
exports.DEFAULT_WILDCARD = '^';
exports.WILDCARDS = ['^', '~', '.*', '.x'];
const WILDCARDS_PURE = ['^', '~', '^*', '*', 'x', 'x.x', 'x.x.x'];
const WILDCARD_PURE_REGEX = new RegExp(`^(${WILDCARDS_PURE.join('|').replace(/\^/g, '\\^').replace(/\*/g, '\\*')})$`);
/** Matches an npm alias version declaration. */
const NPM_ALIAS_REGEX = /^npm:(.*)@(.*)/;
/**
* @param version
* @returns The number of parts in the version
*/
function numParts(version) {
const [semver] = semver_utils_1.default.parseRange(version);
if (!semver) {
throw new Error(util_1.default.format('semverutils.parseRange returned null when trying to parse "%s". This is probably a problem with the "semver-utils" dependency. Please report an issue at https://github.com/raineorshine/npm-check-updates/issues.', version));
}
return (0, intersection_1.default)(VERSION_PARTS, Object.keys(semver)).length;
}
exports.numParts = numParts;
/**
* Increases or decreases the a precision by the given amount, e.g. major+1 -> minor
*
* @param precision
* @param n
* @returns
*/
function precisionAdd(precision, n) {
if (n === 0)
return precision;
const index = VERSION_BASE_PARTS.includes(precision)
? VERSION_BASE_PARTS.indexOf(precision) + n
: VERSION_ADDED_PARTS.includes(precision)
? VERSION_BASE_PARTS.length + n
: null;
if (index === null || !VERSION_PARTS[index]) {
throw new Error(`Invalid precision: ${precision}`);
}
return VERSION_PARTS[index];
}
exports.precisionAdd = precisionAdd;
/**
* Joins the major, minor, patch, release, and build parts (controlled by an
* optional precision arg) of a semver object into a dot-delimited string.
*
* @param semver
* @param [precision]
* @returns
*/
function stringify(semver, precision) {
// get a list of the parts up until (and including) the given precision
// or all of them, if no precision is specified
const parts = precision ? VERSION_PARTS.slice(0, VERSION_PARTS.indexOf(precision) + 1) : VERSION_PARTS;
// pair each part with its delimiter and join together
return parts
.filter(part => (precision && VERSION_BASE_PARTS.includes(precision)) || semver[part])
.map(part => VERSION_PART_DELIM[part] + (semver[part] || '0'))
.join('');
}
exports.stringify = stringify;
/**
* Gets how precise this version number is (major, minor, patch, release, or build)
*
* @param version
* @returns
*/
function getPrecision(version) {
const [semver] = semver_utils_1.default.parseRange(version);
// expects VERSION_PARTS to be in correct order
// eslint-disable-next-line fp/no-mutating-methods
return VERSION_PARTS.slice().reverse().find((0, propertyOf_1.default)(semver));
}
exports.getPrecision = getPrecision;
/**
* Sets the precision of a (loose) semver to the specified level: major, minor, etc.
*
* @param version
* @param [precision]
* @returns
*/
function setPrecision(version, precision) {
const [semver] = semver_utils_1.default.parseRange(version);
return stringify(semver, precision);
}
exports.setPrecision = setPrecision;
/**
* Adds a given wildcard (^,~,.*,.x) to a version number. Adds ^ and ~ to the
* beginning. Replaces everything after the major version number with .* or .x
*
* @param version
* @param wildcard
* @returns
*/
function addWildCard(version, wildcard) {
return wildcard === '^' || wildcard === '~' ? wildcard + version : setPrecision(version, 'major') + wildcard;
}
exports.addWildCard = addWildCard;
/**
* Returns true if the given string is one of the wild cards.
*
* @param version
* @returns
*/
function isWildCard(version) {
return WILDCARD_PURE_REGEX.test(version);
}
exports.isWildCard = isWildCard;
/**
* Returns true if the given digit is a wildcard for a part of a version.
*
* @param versionPart
* @returns
*/
function isWildPart(versionPartValue) {
return versionPartValue === '*' || versionPartValue === 'x';
}
exports.isWildPart = isWildPart;
/**
* Determines the part of a version string that has changed when comparing two versions. Assumes that the two version strings are in the same format. Returns null if no parts have changed.
*
* @param from
* @param to
*/
function partChanged(from, to) {
if (from === to)
return 'none';
// separate out leading ^ or ~
if (/^[~^]/.test(to) && to[0] === from[0]) {
to = to.slice(1);
from = from.slice(1);
}
// split into parts
const partsTo = to.split('.');
const partsFrom = from.split('.');
let i = partsTo.findIndex((partto, i) => partto !== partsFrom[i]);
i = i >= 0 ? i : partsTo.length;
// major = red (or any change before 1.0.0)
// minor = cyan
// patch = green
return partsTo[0] === '0' ? 'majorVersionZero' : i === 0 ? 'major' : i === 1 ? 'minor' : 'patch';
}
exports.partChanged = partChanged;
/**
* Returns a list of group heading and a map of package names and versions.
* Used with --format group and takes into account the custom --group function.
*/
function getDependencyGroups(newDependencies, oldDependencies, options) {
const groups = (0, keyValueBy_1.keyValueBy)(newDependencies, (dep, to, accum) => {
var _a, _b;
const from = oldDependencies[dep];
const defaultGroup = partChanged(from, to);
const userDefinedUpgradeGroup = (_b = (_a = options.groupFunction) === null || _a === void 0 ? void 0 : _a.call(options, dep, defaultGroup, (0, semver_utils_1.parseRange)(from), (0, semver_utils_1.parseRange)(to), (0, semver_utils_1.parse)(newDependencies[dep]))) !== null && _b !== void 0 ? _b : defaultGroup;
if (userDefinedUpgradeGroup === 'none') {
return accum;
}
return {
...accum,
[userDefinedUpgradeGroup]: {
...accum[userDefinedUpgradeGroup],
[dep]: to,
},
};
});
// get the the text for the default group headings
const headings = {
patch: chalk_1.default.green(chalk_1.default.bold('Patch') + ' Backwards-compatible bug fixes'),
minor: chalk_1.default.cyan(chalk_1.default.bold('Minor') + ' Backwards-compatible features'),
major: chalk_1.default.red(chalk_1.default.bold('Major') + ' Potentially breaking API changes'),
majorVersionZero: chalk_1.default.magenta(chalk_1.default.bold('Major version zero') + ' Anything may change'),
};
const groupOrder = (0, uniq_1.default)(['patch', 'minor', 'major', 'majorVersionZero', ...(0, sortBy_1.default)(Object.keys(groups))]);
return groupOrder
.filter(groupName => {
return groupName in groups;
})
.map(groupName => {
return {
groupName,
heading: groupName in headings ? headings[groupName] : groupName,
packages: groups[groupName],
};
});
}
exports.getDependencyGroups = getDependencyGroups;
/**
* Colorize the parts of a version string (to) that are different than
* another (from). Assumes that the two version strings are in the same format.
*
* @param from
* @param to
* @returns
*/
function colorizeDiff(from, to) {
let leadingWildcard = '';
// separate out leading ^ or ~
if (/^[~^]/.test(to) && to[0] === from[0]) {
leadingWildcard = to[0];
to = to.slice(1);
from = from.slice(1);
}
// split into parts
const partsToColor = to.split('.');
const partsToCompare = from.split('.');
let i = partsToColor.findIndex((part, i) => part !== partsToCompare[i]);
i = i >= 0 ? i : partsToColor.length;
// major = red (or any change before 1.0.0)
// minor = cyan
// patch = green
const color = i === 0 || partsToColor[0] === '0' ? 'red' : i === 1 ? 'cyan' : 'green';
// if we are colorizing only part of the word, add a dot in the middle
const middot = i > 0 && i < partsToColor.length ? '.' : '';
return leadingWildcard + partsToColor.slice(0, i).join('.') + middot + chalk_1.default[color](partsToColor.slice(i).join('.'));
}
exports.colorizeDiff = colorizeDiff;
/**
* Extract prerelease tag, omitting build number
* Example: 1.0.0-next.alpha.2 -> next.alpha
*
* @param version
*/
const getPre = (version) => {
const pre = semver_1.default.prerelease(version);
return pre && pre.slice(0, -1).join('.');
};
/**
* Check if it is allowed to compare two versions based on their prerelease tag
*
* SemVer both states that different prerelease versions can`t be compared
* and at the same time compares them as part of the version via strcmp
*
* @param a
* @param b
* @returns True if two versions can be compared by the means of SemVer
*/
function isComparable(a, b) {
const preA = getPre(a);
const preB = getPre(b);
return typeof preA !== 'string' || typeof preB !== 'string' || preA === preB;
}
exports.isComparable = isComparable;
/** Comparator used to sort semver versions */
function compareVersions(a, b) {
const isValid = semver_1.default.valid(a) && semver_1.default.valid(b);
const isGreater = isValid ? semver_1.default.gt(a, b) : a > b;
return isGreater ? 1 : a === b ? 0 : -1;
}
exports.compareVersions = compareVersions;
/**
* Finds the greatest version at the given level (minor|patch).
*
* @param versions Unsorted array of all available versions
* @param current Current version or range
* @param level major|minor
* @returns String representation of the suggested version.
*/
function findGreatestByLevel(versions, current, level) {
if (!semver_1.default.validRange(current)) {
return null;
}
const cur = semver_1.default.minVersion(current);
const versionsSorted = [...versions] // eslint-disable-line fp/no-mutating-methods
.sort(compareVersions)
.filter(v => {
const parsed = semver_1.default.parse(v);
return (parsed &&
(level === 'major' || parsed.major === (cur === null || cur === void 0 ? void 0 : cur.major)) &&
(level === 'major' || level === 'minor' || parsed.minor === (cur === null || cur === void 0 ? void 0 : cur.minor)));
});
return (0, last_1.default)(versionsSorted) || null;
}
exports.findGreatestByLevel = findGreatestByLevel;
/**
* @param version
* @returns True if the version is any kind of prerelease: alpha, beta, rc, pre
*/
function isPre(version) {
return getPrecision(version) === 'release';
}
exports.isPre = isPre;
/** Checks if a string is a simple version in the format "v1". */
const isMissingMinorAndPatch = (s) => /^[vV]?\d+$/.test(s);
/** Checks if a version string is missing its match component, e.g. "1.0". */
const isMissingPatch = (s) => /^[vV]?\d+\.\d+$/.test(s);
/** Removes a leading 'v' or 'V' from a pseudo version.. */
const fixLeadingV = (s) => s.replace(/^[vV]/, '');
/** Converts a pseudo version that is missing its minor and patch components into a valid semver version. NOOP for valid semver versions. */
const fixMissingMinorAndPatch = (s) => (isMissingMinorAndPatch(s) ? s + '.0.0' : s);
/** Converts a pseudo version that is missing its patch component into a valid semver version. NOOP for valid semver versions. */
const fixMissingPatch = (s) => (isMissingPatch(s) ? s + '.0' : s);
/** Converts a pseudo version into a valid semver version. NOOP for valid semver versions. */
exports.fixPseudoVersion = (0, flow_1.default)(fixLeadingV, fixMissingMinorAndPatch, fixMissingPatch);
/**
* Returns 'v' if the string starts with a v, otherwise returns empty string.
*
* @param str
* @returns
*/
function v(str) {
return str && (str[0] === 'v' || str[1] === 'v') ? 'v' : '';
}
exports.v = v;
/**
* Constructs an npm alias from the name and version of the actual package.
*
* @param name Name of the actual package.
* @param version Version of the actual package.
* @returns "npm:package@x.y.z"
* @example createNpmAlias('chalk', '2.0.0') -> 'npm:chalk@2.0.0'
*/
const createNpmAlias = (name, version) => `npm:${name}@${version}`;
exports.createNpmAlias = createNpmAlias;
/**
* Parses an npm alias into a [name, version] 2-tuple.
*
* @returns [name, version] or null if the input is not an npm alias
* @example 'npm:chalk@1.0.0' -> ['chalk', '1.0.0']
*/
const parseNpmAlias = (alias) => {
const match = alias && alias.match && alias.match(NPM_ALIAS_REGEX);
return match && match.slice(1);
};
exports.parseNpmAlias = parseNpmAlias;
/**
* Returns true if a version declaration is an npm alias.
*/
const isNpmAlias = (declaration) => declaration && !!declaration.match(NPM_ALIAS_REGEX);
exports.isNpmAlias = isNpmAlias;
/**
* Replaces the version number embedded in an npm alias.
*/
const upgradeNpmAlias = (declaration, upgraded) => {
const npmAlias = (0, exports.parseNpmAlias)(declaration);
if (!npmAlias)
return null;
return (0, exports.createNpmAlias)(npmAlias[0], upgraded);
};
exports.upgradeNpmAlias = upgradeNpmAlias;
/**
* Returns true if a version declaration is a Github URL with a valid semver version.
*/
const isGithubUrl = (declaration) => {
if (!declaration)
return false;
let parsed = null;
try {
parsed = (0, parse_github_url_1.default)(declaration);
}
catch {
// Strings like `npm:postman-request@2.88.1-postman.33` can throw errors instead of simply returning null
// In node 18.17+ due to url.parse regressison: https://github.com/nodejs/node/issues/49330
// So if this throws, we can assume it's not a valid Github URL.
}
if (!parsed || !parsed.branch)
return false;
const version = decodeURIComponent(parsed.branch).replace(/^semver:/, '');
return !!semver_1.default.validRange(version);
};
exports.isGithubUrl = isGithubUrl;
/**
* Returns the embedded tag in a Github URL.
*/
const getGithubUrlTag = (declaration) => {
if (!declaration)
return null;
const parsed = (0, parse_github_url_1.default)(declaration);
if (!parsed || !parsed.branch)
return null;
const version = decodeURIComponent(parsed.branch).replace(/^semver:/, '');
return parsed && parsed.branch && semver_1.default.validRange(version) ? version : null;
};
exports.getGithubUrlTag = getGithubUrlTag;
/**
* Upgrade an existing dependency declaration to satisfy the latest version.
*
* @param declaration Current version declaration (e.g. "1.2.x")
* @param latestVersion Latest version (e.g "1.3.2")
* @param [options={}]
* @returns The upgraded dependency declaration (e.g. "1.3.x")
*/
function upgradeDependencyDeclaration(declaration, latestVersion, options = {}) {
options.wildcard = options.wildcard || exports.DEFAULT_WILDCARD;
if (!latestVersion) {
return declaration;
}
// parse the latestVersion
// return original declaration if latestSemver is invalid
const [latestSemver] = semver_utils_1.default.parseRange(latestVersion);
if (!latestSemver) {
return declaration;
}
// return global wildcards immediately
if (options.removeRange) {
return latestVersion;
}
else if (isWildCard(declaration)) {
return declaration;
}
// parse the declaration
// if multiple ranges, use the semver with the least number of parts
const parsedRange = (0, flow_1.default)([
// semver-utils includes empty entries for the || and - operators. We can remove them completely
ranges => (0, reject_1.default)(ranges, { operator: '||' }),
ranges => (0, reject_1.default)(ranges, { operator: '-' }),
ranges => (0, sortBy_1.default)(ranges, (0, ary_1.default)((0, flow_1.default)(stringify, numParts), 1)),
])(semver_utils_1.default.parseRange(declaration));
const [declaredSemver] = parsedRange;
/**
* Chooses version parts between the declared version and the latest.
* Base parts (major, minor, patch) are only included if they are in the original declaration.
* Added parts (release, build) are always included. They are only present if we are checking --greatest versions
* anyway.
*/
function chooseVersion(part) {
return ((isWildPart(declaredSemver[part])
? declaredSemver[part]
: VERSION_BASE_PARTS.includes(part) && declaredSemver[part]
? latestSemver[part]
: VERSION_ADDED_PARTS.includes(part)
? latestSemver[part]
: null) || null);
}
// create a new semver object with major, minor, patch, build, and release parts
const newSemver = (0, keyValueBy_1.keyValueBy)(VERSION_PARTS, (part) => ({
[part]: chooseVersion(part),
}));
const newSemverString = stringify(newSemver);
const version = v(declaredSemver.semver) + newSemverString;
// determine the operator
// do not compact, because [undefined, '<'] must be differentiated from ['<']
const uniqueOperators = (0, uniq_1.default)(parsedRange.map(range => range.operator));
const operator = uniqueOperators[0] || '';
const hasWildCard = exports.WILDCARDS.some(wildcard => newSemverString.includes(wildcard));
const isLessThanOrEqual = uniqueOperators[0] === '<' || uniqueOperators[0] === '<=';
const isGreaterThan = uniqueOperators[0] === '>';
const isMixed = uniqueOperators.length > 1;
// convert versions with </<= or mixed operators into the preferred wildcard
// only do so if the new version does not already contain a wildcard
return !hasWildCard && (isLessThanOrEqual || isMixed)
? addWildCard(version, options.wildcard)
: // convert > to >= since there are likely no available versions > latest
// https://github.com/raineorshine/npm-check-updates/issues/957
(isGreaterThan ? '>=' : operator) + version;
}
exports.upgradeDependencyDeclaration = upgradeDependencyDeclaration;
/** Reverts a valid semver version to a pseudo version that is missing its minor and patch components. NOOP If the original version was a valid semver version. */
const revertMissingMinorAndPatch = (0, curry_1.default)((current, latest) => isMissingMinorAndPatch(current) ? latest.slice(0, latest.length - '.0.0'.length) : latest);
/** Reverts a valid semver version to a pseudo version that is missing its patch components. NOOP If the original version was a valid semver version. */
const revertMissingPatch = (0, curry_1.default)((current, latest) => isMissingPatch(current) ? latest.slice(0, latest.length - '.0'.length) : latest);
/** Reverts a valid semver version to a pseudo version with a leading 'v'. NOOP If the original version was a valid semver version. */
const revertLeadingV = (0, curry_1.default)((current, latest) => (v(current) ? v(current) + latest : latest));
/** Reverts a valid semver version to a pseudo version. NOOP If the original version was a valid semver version. */
const revertPseudoVersion = (current, latest) => (0, flow_1.default)(revertLeadingV(current), revertMissingMinorAndPatch(current), revertMissingPatch(current))(latest);
/**
* Replaces the version number embedded in a Github URL.
*/
const upgradeGithubUrl = (declaration, upgraded) => {
// convert upgraded to a proper semver version if it is a pseudo version, otherwise revertPseudoVersion will return an empty string
const upgradedNormalized = (0, exports.fixPseudoVersion)(upgraded);
const parsedUrl = (0, parse_github_url_1.default)(declaration);
if (!parsedUrl)
return declaration;
const tag = decodeURIComponent(parsedUrl.branch).replace(/^semver:/, '');
return declaration.replace(tag, upgradeDependencyDeclaration(tag, revertPseudoVersion(tag, upgradedNormalized)));
};
exports.upgradeGithubUrl = upgradeGithubUrl;
//# sourceMappingURL=version-util.js.map
;