2017-01-07 07:22:48 +00:00
|
|
|
const got = require('got');
|
|
|
|
const semver = require('semver');
|
|
|
|
const stable = require('semver-stable');
|
|
|
|
|
2017-01-11 09:05:42 +00:00
|
|
|
let logger = null;
|
2017-01-07 07:22:48 +00:00
|
|
|
|
2017-01-11 13:33:32 +00:00
|
|
|
module.exports = {
|
|
|
|
setLogger,
|
|
|
|
getAllDependencyUpgrades,
|
2017-01-11 12:19:59 +00:00
|
|
|
};
|
|
|
|
|
2017-01-11 13:33:32 +00:00
|
|
|
function setLogger(l) {
|
|
|
|
logger = l;
|
|
|
|
}
|
|
|
|
|
2017-01-11 12:19:59 +00:00
|
|
|
function getAllDependencyUpgrades(packageContents) {
|
|
|
|
const allDependencyChecks = [];
|
|
|
|
const allDependencyUpgrades = [];
|
|
|
|
const dependencyTypes = ['dependencies', 'devDependencies'];
|
|
|
|
dependencyTypes.forEach((depType) => {
|
|
|
|
if (!packageContents[depType]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Object.keys(packageContents[depType]).forEach((depName) => {
|
|
|
|
const currentVersion = packageContents[depType][depName];
|
|
|
|
if (!isValidVersion(currentVersion)) {
|
|
|
|
logger.verbose(`${depName}: Skipping invalid version ${currentVersion}`);
|
2017-01-07 20:01:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-01-11 12:19:59 +00:00
|
|
|
allDependencyChecks.push(
|
|
|
|
getDependencyUpgrades(depName, currentVersion).then((res) => {
|
|
|
|
if (res.length > 0) {
|
|
|
|
logger.verbose(`${depName}: Upgrades = ${JSON.stringify(res)}`);
|
|
|
|
res.forEach((upgrade) => {
|
|
|
|
allDependencyUpgrades.push({
|
|
|
|
depType,
|
|
|
|
depName,
|
|
|
|
currentVersion,
|
|
|
|
upgradeType: upgrade.type,
|
|
|
|
newVersion: upgrade.version,
|
2017-01-11 13:33:32 +00:00
|
|
|
newVersionMajor: semver.major(upgrade.version),
|
2017-01-11 18:32:45 +00:00
|
|
|
workingVersion: upgrade.workingVersion,
|
2017-01-07 20:01:32 +00:00
|
|
|
});
|
2017-01-11 12:19:59 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
logger.verbose(`${depName}: No upgrades required`);
|
|
|
|
}
|
|
|
|
return Promise.resolve();
|
|
|
|
}));
|
2017-01-07 20:01:32 +00:00
|
|
|
});
|
2017-01-11 12:19:59 +00:00
|
|
|
});
|
|
|
|
return Promise.all(allDependencyChecks).then(() => allDependencyUpgrades);
|
|
|
|
}
|
2017-01-07 20:01:32 +00:00
|
|
|
|
|
|
|
function getDependency(depName) {
|
|
|
|
// supports scoped packages, e.g. @user/package
|
2017-01-10 22:06:25 +00:00
|
|
|
return got(`https://registry.npmjs.org/${depName.replace('/', '%2F')}`, {
|
|
|
|
json: true,
|
|
|
|
});
|
2017-01-07 20:01:32 +00:00
|
|
|
}
|
2017-01-07 21:00:32 +00:00
|
|
|
|
|
|
|
function getDependencyUpgrades(depName, currentVersion) {
|
2017-01-10 22:06:25 +00:00
|
|
|
return getDependency(depName).then((res) => {
|
2017-01-10 21:28:22 +00:00
|
|
|
if (!res.body.versions) {
|
2017-01-11 09:05:42 +00:00
|
|
|
logger.error(`${depName} versions is null`);
|
2017-01-07 21:00:32 +00:00
|
|
|
}
|
2017-01-10 08:33:54 +00:00
|
|
|
const allUpgrades = {};
|
2017-01-10 21:28:22 +00:00
|
|
|
let workingVersion = currentVersion;
|
2017-01-09 21:44:36 +00:00
|
|
|
if (isRange(currentVersion)) {
|
|
|
|
// Pin ranges to their maximum satisfying version
|
2017-01-10 22:06:25 +00:00
|
|
|
const maxSatisfying = semver.maxSatisfying(
|
|
|
|
Object.keys(res.body.versions),
|
2017-01-11 06:14:14 +00:00
|
|
|
currentVersion);
|
2017-01-10 22:06:25 +00:00
|
|
|
allUpgrades.pin = { type: 'pin', version: maxSatisfying };
|
2017-01-10 21:28:22 +00:00
|
|
|
workingVersion = maxSatisfying;
|
2017-01-09 21:44:36 +00:00
|
|
|
}
|
2017-01-10 21:28:22 +00:00
|
|
|
const currentMajor = semver.major(workingVersion);
|
|
|
|
Object.keys(res.body.versions).forEach((version) => {
|
|
|
|
if (stable.is(workingVersion) && !stable.is(version)) {
|
2017-01-07 21:00:32 +00:00
|
|
|
// Ignore unstable versions, unless the current version is unstable
|
|
|
|
return;
|
|
|
|
}
|
2017-01-10 21:28:22 +00:00
|
|
|
if (semver.gt(version, workingVersion)) {
|
2017-01-07 21:00:32 +00:00
|
|
|
// Group by major versions
|
2017-01-10 21:28:22 +00:00
|
|
|
const thisMajor = semver.major(version);
|
2017-01-10 22:06:25 +00:00
|
|
|
if (
|
|
|
|
!allUpgrades[thisMajor] ||
|
|
|
|
semver.gt(version, allUpgrades[thisMajor].version)
|
|
|
|
) {
|
2017-01-10 08:33:54 +00:00
|
|
|
allUpgrades[thisMajor] = {
|
2017-01-10 22:06:25 +00:00
|
|
|
type: thisMajor > currentMajor ? 'major' : 'minor',
|
2017-01-10 21:28:22 +00:00
|
|
|
version,
|
2017-01-11 18:32:45 +00:00
|
|
|
workingVersion,
|
2017-01-10 08:33:54 +00:00
|
|
|
};
|
2017-01-07 21:00:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2017-01-10 21:28:22 +00:00
|
|
|
if (allUpgrades.pin && Object.keys(allUpgrades).length > 1) {
|
2017-01-10 08:33:54 +00:00
|
|
|
// Remove the pin
|
2017-01-10 21:28:22 +00:00
|
|
|
delete allUpgrades.pin;
|
2017-01-10 08:33:54 +00:00
|
|
|
}
|
|
|
|
// Return only the values
|
|
|
|
return Object.keys(allUpgrades).map(key => allUpgrades[key]);
|
2017-01-07 21:00:32 +00:00
|
|
|
});
|
|
|
|
}
|
2017-01-09 21:44:36 +00:00
|
|
|
|
|
|
|
function isRange(input) {
|
|
|
|
// Pinned versions also return true for semver.validRange
|
2017-01-10 22:06:25 +00:00
|
|
|
// We need to check first that they're not 'valid' to get only ranges
|
2017-01-12 16:00:54 +00:00
|
|
|
return (semver.valid(input) === null && semver.validRange(input) !== null);
|
2017-01-09 21:44:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isValidVersion(input) {
|
2017-01-12 16:00:54 +00:00
|
|
|
return (semver.valid(input) || semver.validRange(input)) !== null;
|
2017-01-09 21:44:36 +00:00
|
|
|
}
|