2017-12-09 07:45:06 +00:00
|
|
|
#!/usr/bin/env node
|
2017-12-03 11:15:02 +00:00
|
|
|
"use strict";
|
|
|
|
|
2018-08-23 20:28:33 +00:00
|
|
|
process.env.NODE_ENV = "production";
|
|
|
|
|
2019-02-11 18:39:40 +00:00
|
|
|
const npmRegisty = "https://registry.npmjs.org/";
|
|
|
|
|
2017-12-08 13:39:52 +00:00
|
|
|
const args = require("minimist")(process.argv.slice(2), {
|
2018-02-21 19:44:13 +00:00
|
|
|
boolean: [
|
2018-09-02 19:43:16 +00:00
|
|
|
"c", "color",
|
2018-10-15 06:05:54 +00:00
|
|
|
"E", "error-on-outdated",
|
2018-09-02 19:43:16 +00:00
|
|
|
"h", "help",
|
|
|
|
"j", "json",
|
|
|
|
"n", "no-color",
|
2018-10-04 05:58:20 +00:00
|
|
|
"u", "update",
|
2018-09-02 19:43:16 +00:00
|
|
|
"v", "version",
|
2018-02-21 19:44:13 +00:00
|
|
|
],
|
2018-07-24 07:40:47 +00:00
|
|
|
string: [
|
2018-09-02 19:43:16 +00:00
|
|
|
"f", "file",
|
|
|
|
"g", "greatest",
|
2018-12-21 22:17:39 +00:00
|
|
|
"m", "minor",
|
|
|
|
"P", "patch",
|
2018-09-02 19:43:16 +00:00
|
|
|
"p", "prerelease",
|
2019-02-25 21:46:39 +00:00
|
|
|
"R", "release",
|
2018-09-02 19:43:16 +00:00
|
|
|
"r", "registry",
|
2018-10-16 15:18:06 +00:00
|
|
|
"t", "types",
|
2018-07-24 07:40:47 +00:00
|
|
|
],
|
2018-02-21 19:44:13 +00:00
|
|
|
alias: {
|
2018-07-10 15:32:19 +00:00
|
|
|
c: "color",
|
2018-10-15 06:05:54 +00:00
|
|
|
E: "error-on-outdated",
|
2018-10-16 15:18:06 +00:00
|
|
|
e: "exclude",
|
2018-08-23 20:40:19 +00:00
|
|
|
f: "file",
|
2018-07-19 21:28:41 +00:00
|
|
|
g: "greatest",
|
2018-05-31 17:33:22 +00:00
|
|
|
h: "help",
|
|
|
|
i: "include",
|
2018-02-21 19:44:13 +00:00
|
|
|
j: "json",
|
2018-12-21 22:17:39 +00:00
|
|
|
m: "minor",
|
2018-07-10 15:32:19 +00:00
|
|
|
n: "no-color",
|
2018-12-21 22:17:39 +00:00
|
|
|
P: "patch",
|
2018-05-31 17:33:22 +00:00
|
|
|
p: "prerelease",
|
2018-07-24 07:40:47 +00:00
|
|
|
r: "registry",
|
2019-02-25 21:46:39 +00:00
|
|
|
R: "release",
|
2018-11-19 09:18:32 +00:00
|
|
|
s: "semver",
|
2018-10-16 15:18:06 +00:00
|
|
|
t: "types",
|
2018-05-31 17:33:22 +00:00
|
|
|
u: "update",
|
2018-02-21 19:44:13 +00:00
|
|
|
v: "version",
|
2018-04-30 06:31:42 +00:00
|
|
|
},
|
2017-12-08 13:39:52 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (args.help) {
|
2017-12-06 18:05:53 +00:00
|
|
|
process.stdout.write(`usage: updates [options]
|
|
|
|
|
2017-12-03 11:15:02 +00:00
|
|
|
Options:
|
2018-10-16 15:24:05 +00:00
|
|
|
-u, --update Update versions and write package.json
|
2018-09-02 19:43:16 +00:00
|
|
|
-p, --prerelease [<pkg,...>] Consider prerelease versions
|
2019-02-25 21:46:39 +00:00
|
|
|
-R, --release [<pkg,...>] Only use release versions, may downgrade
|
2018-09-02 19:43:16 +00:00
|
|
|
-g, --greatest [<pkg,...>] Prefer greatest over latest version
|
2018-10-16 15:24:05 +00:00
|
|
|
-i, --include <pkg,...> Include only given packages
|
2018-09-02 19:43:16 +00:00
|
|
|
-e, --exclude <pkg,...> Exclude given packages
|
2018-10-16 15:24:05 +00:00
|
|
|
-t, --types <type,...> Check only given dependency types
|
2018-12-21 22:17:39 +00:00
|
|
|
-P, --patch [<pkg,...>] Consider only up to semver-patch
|
|
|
|
-m, --minor [<pkg,...>] Consider only up to semver-minor
|
2018-10-15 06:05:54 +00:00
|
|
|
-E, --error-on-outdated Exit with error code 2 on outdated packages
|
2018-10-16 15:24:05 +00:00
|
|
|
-r, --registry <url> Use given registry URL
|
2018-11-27 22:42:10 +00:00
|
|
|
-f, --file <path> Use given package.json file or module directory
|
2018-09-02 19:44:11 +00:00
|
|
|
-j, --json Output a JSON object
|
2018-09-02 19:43:16 +00:00
|
|
|
-c, --color Force-enable color output
|
|
|
|
-n, --no-color Disable color output
|
|
|
|
-v, --version Print the version
|
|
|
|
-h, --help Print this help
|
2018-03-03 19:39:39 +00:00
|
|
|
|
2017-12-03 11:15:02 +00:00
|
|
|
Examples:
|
|
|
|
$ updates
|
2017-12-03 11:35:25 +00:00
|
|
|
$ updates -u
|
2019-02-06 06:35:03 +00:00
|
|
|
$ updates -u -m
|
2018-11-19 14:53:48 +00:00
|
|
|
$ updates -u -e chalk
|
2018-10-16 15:18:06 +00:00
|
|
|
$ updates -u -t devDependencies
|
2018-03-03 19:39:39 +00:00
|
|
|
`);
|
2017-12-06 18:05:53 +00:00
|
|
|
process.exit(0);
|
|
|
|
}
|
2017-12-03 11:15:02 +00:00
|
|
|
|
|
|
|
const path = require("path");
|
2017-12-06 18:11:40 +00:00
|
|
|
|
2017-12-08 13:39:52 +00:00
|
|
|
if (args.version) {
|
2018-03-03 19:47:48 +00:00
|
|
|
console.info(require(path.join(__dirname, "package.json")).version);
|
2017-12-06 18:11:40 +00:00
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:54:17 +00:00
|
|
|
if (args["no-color"]) {
|
|
|
|
process.env.FORCE_COLOR = "0";
|
|
|
|
} else if (args["color"] || process.stdout.isTTY === undefined) { // winpty compat
|
|
|
|
process.env.FORCE_COLOR = "1";
|
|
|
|
}
|
2017-12-09 05:49:35 +00:00
|
|
|
|
2018-09-02 19:43:16 +00:00
|
|
|
const greatest = parseMixedArg(args.greatest);
|
2018-10-04 05:09:13 +00:00
|
|
|
const prerelease = parseMixedArg(args.prerelease);
|
2019-02-25 21:46:39 +00:00
|
|
|
const release = parseMixedArg(args.release);
|
2018-12-21 22:17:39 +00:00
|
|
|
const patch = parseMixedArg(args.patch);
|
|
|
|
const minor = parseMixedArg(args.minor);
|
2017-12-03 11:15:02 +00:00
|
|
|
|
2019-02-11 18:39:40 +00:00
|
|
|
let registry;
|
|
|
|
if (args.registry) {
|
|
|
|
registry = args.registry;
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
registry = require("npm-conf")().get("registry");
|
|
|
|
} catch (err) {
|
|
|
|
registry = npmRegisty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!registry.endsWith("/")) {
|
|
|
|
registry += "/";
|
|
|
|
}
|
2018-11-27 22:42:10 +00:00
|
|
|
|
|
|
|
let packageFile;
|
2019-01-19 17:45:23 +00:00
|
|
|
const deps = {};
|
|
|
|
|
2018-11-27 22:42:10 +00:00
|
|
|
if (args.file) {
|
2019-01-19 17:45:23 +00:00
|
|
|
let stat;
|
|
|
|
try {
|
|
|
|
stat = require("fs").lstatSync(args.file);
|
|
|
|
} catch (err) {
|
|
|
|
finish(new Error(`Unable to open ${args.file}: ${err.message}`));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat && stat.isFile()) {
|
2018-11-27 22:42:10 +00:00
|
|
|
packageFile = args.file;
|
2019-01-19 17:45:23 +00:00
|
|
|
} else if (stat && stat.isDirectory()) {
|
2018-11-27 22:42:10 +00:00
|
|
|
packageFile = path.join(args.file, "package.json");
|
2019-01-19 17:45:23 +00:00
|
|
|
} else {
|
|
|
|
finish(new Error(`${args.file} is neither a file nor directory`));
|
2018-11-27 22:42:10 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
packageFile = require("find-up").sync("package.json");
|
|
|
|
}
|
2017-12-03 11:15:02 +00:00
|
|
|
|
2018-10-16 15:18:06 +00:00
|
|
|
let dependencyTypes;
|
|
|
|
if (args.types) {
|
|
|
|
dependencyTypes = Array.isArray(args.types) ? args.types : args.types.split(",");
|
|
|
|
} else {
|
2018-10-16 21:30:11 +00:00
|
|
|
dependencyTypes = [
|
|
|
|
"dependencies",
|
|
|
|
"devDependencies",
|
|
|
|
"peerDependencies",
|
|
|
|
"optionalDependencies",
|
|
|
|
];
|
2018-10-16 15:18:06 +00:00
|
|
|
}
|
|
|
|
|
2018-09-02 19:43:16 +00:00
|
|
|
const fs = require("fs");
|
2017-12-06 18:05:53 +00:00
|
|
|
let pkg, pkgStr;
|
|
|
|
|
|
|
|
try {
|
2017-12-08 13:39:52 +00:00
|
|
|
pkgStr = fs.readFileSync(packageFile, "utf8");
|
2017-12-06 18:05:53 +00:00
|
|
|
} catch (err) {
|
2018-05-31 17:33:22 +00:00
|
|
|
finish(new Error(`Unable to open package.json: ${err.message}`));
|
2017-12-06 18:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
pkg = JSON.parse(pkgStr);
|
|
|
|
} catch (err) {
|
2018-05-31 17:33:22 +00:00
|
|
|
finish(new Error(`Error parsing package.json: ${err.message}`));
|
2017-12-06 18:05:53 +00:00
|
|
|
}
|
|
|
|
|
2018-07-30 16:55:32 +00:00
|
|
|
const semver = require("semver");
|
|
|
|
|
2018-04-30 07:11:31 +00:00
|
|
|
let include, exclude;
|
2018-11-19 09:18:32 +00:00
|
|
|
if (args.include && args.include !== true) include = args.include.split(",");
|
|
|
|
if (args.exclude && args.exclude !== true) exclude = args.exclude.split(",");
|
2018-04-30 06:31:42 +00:00
|
|
|
|
2018-08-23 20:28:33 +00:00
|
|
|
for (const key of dependencyTypes) {
|
2017-12-03 11:15:02 +00:00
|
|
|
if (pkg[key]) {
|
2018-04-30 08:42:39 +00:00
|
|
|
const names = Object.keys(pkg[key])
|
|
|
|
.filter(name => !include ? true : include.includes(name))
|
|
|
|
.filter(name => !exclude ? true : !exclude.includes(name));
|
|
|
|
|
2018-08-23 20:28:33 +00:00
|
|
|
for (const name of names) {
|
2017-12-09 05:47:15 +00:00
|
|
|
const old = pkg[key][name];
|
|
|
|
if (isValidSemverRange(old)) {
|
|
|
|
deps[name] = {old};
|
2017-12-03 11:15:02 +00:00
|
|
|
}
|
2018-08-23 20:28:33 +00:00
|
|
|
}
|
2017-12-03 11:15:02 +00:00
|
|
|
}
|
2018-08-23 20:28:33 +00:00
|
|
|
}
|
2017-12-03 11:15:02 +00:00
|
|
|
|
2018-04-30 07:05:52 +00:00
|
|
|
if (!Object.keys(deps).length) {
|
2018-10-04 05:58:20 +00:00
|
|
|
if (include || exclude) {
|
2018-10-04 05:27:11 +00:00
|
|
|
finish(new Error("No packages match the given filters"));
|
2018-08-25 07:43:37 +00:00
|
|
|
} else {
|
|
|
|
finish(new Error("No packages found"));
|
|
|
|
}
|
2018-04-30 07:05:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-10 15:32:19 +00:00
|
|
|
const fetch = require("make-fetch-happen");
|
2018-11-28 00:00:17 +00:00
|
|
|
const chalk = require("chalk");
|
2018-11-27 22:37:24 +00:00
|
|
|
const hostedGitInfo = require("hosted-git-info");
|
2019-02-11 18:39:40 +00:00
|
|
|
const registryAuthToken = require("registry-auth-token");
|
2018-07-10 15:32:19 +00:00
|
|
|
|
2019-02-11 18:39:40 +00:00
|
|
|
function fetchFromRegistry(name, registry) {
|
|
|
|
let auth;
|
|
|
|
try {
|
|
|
|
auth = registryAuthToken(registry);
|
|
|
|
} catch (err) {
|
|
|
|
finish(err);
|
|
|
|
}
|
2019-02-07 18:21:17 +00:00
|
|
|
|
2018-10-05 21:20:41 +00:00
|
|
|
// on scoped packages replace "/" with "%2f"
|
2018-10-05 21:04:52 +00:00
|
|
|
if (/@[a-z0-9][\w-.]+\/[a-z0-9][\w-.]*/gi.test(name)) {
|
|
|
|
name = name.replace(/\//g, "%2f");
|
2018-07-30 17:18:53 +00:00
|
|
|
}
|
2018-10-05 21:20:41 +00:00
|
|
|
|
2019-02-11 18:39:40 +00:00
|
|
|
const opts = (auth && auth.token) ? {headers: {Authorization: `Bearer ${auth.token}`}} : undefined;
|
|
|
|
return fetch(registry + name, opts);
|
|
|
|
}
|
2019-02-07 18:21:17 +00:00
|
|
|
|
2019-02-11 18:39:40 +00:00
|
|
|
const get = async name => {
|
|
|
|
let res = await fetchFromRegistry(name, registry);
|
|
|
|
if (registry === npmRegisty || (res.status >= 200 && res.status < 300)) {
|
|
|
|
return await res.json();
|
|
|
|
} else { // retry on official registry if custom registry fails
|
|
|
|
res = await fetchFromRegistry(name, npmRegisty);
|
|
|
|
if (res.status >= 200 && res.status < 300) {
|
|
|
|
return await res.json();
|
|
|
|
} else {
|
|
|
|
throw new Error(`Received ${res.status} ${res.statusText} for ${name}`);
|
|
|
|
}
|
|
|
|
}
|
2018-07-29 20:41:46 +00:00
|
|
|
};
|
|
|
|
|
2018-11-20 12:17:19 +00:00
|
|
|
const getInfoUrl = ({repository, homepage}) => {
|
|
|
|
if (repository) {
|
|
|
|
const gitUrl = typeof repository === "string" ? repository : repository.url;
|
2018-11-27 22:37:24 +00:00
|
|
|
const info = hostedGitInfo.fromUrl(gitUrl);
|
2018-11-27 22:34:57 +00:00
|
|
|
if (info && info.browse) return info.browse();
|
2018-11-20 12:17:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return homepage || "";
|
|
|
|
};
|
|
|
|
|
2018-10-05 21:20:41 +00:00
|
|
|
Promise.all(Object.keys(deps).map(name => get(name))).then(dati => {
|
2018-08-23 20:28:33 +00:00
|
|
|
for (const data of dati) {
|
2019-02-07 18:29:36 +00:00
|
|
|
if (data && data.error) {
|
2019-02-11 18:39:40 +00:00
|
|
|
throw new Error(data.error);
|
2019-02-07 18:29:36 +00:00
|
|
|
}
|
|
|
|
|
2018-09-02 19:43:16 +00:00
|
|
|
const useGreatest = typeof greatest === "boolean" ? greatest : greatest.includes(data.name);
|
|
|
|
const usePre = typeof prerelease === "boolean" ? prerelease : prerelease.includes(data.name);
|
2019-02-25 21:46:39 +00:00
|
|
|
const useRel = typeof release === "boolean" ? release : release.includes(data.name);
|
2018-12-21 22:17:39 +00:00
|
|
|
|
|
|
|
let semvers;
|
|
|
|
if (patch === true || Array.isArray(patch) && patch.includes(data.name)) {
|
|
|
|
semvers = ["patch"];
|
|
|
|
} else if (minor === true || Array.isArray(minor) && minor.includes(data.name)) {
|
|
|
|
semvers = ["patch", "minor"];
|
|
|
|
} else {
|
|
|
|
semvers = ["patch", "minor", "major"];
|
|
|
|
}
|
|
|
|
|
2018-04-30 08:42:39 +00:00
|
|
|
const oldRange = deps[data.name].old;
|
2019-02-25 21:46:39 +00:00
|
|
|
const newVersion = findNewVersion(data, {usePre, useRel, useGreatest, semvers, range: oldRange});
|
2018-07-10 15:19:11 +00:00
|
|
|
const newRange = updateRange(oldRange, newVersion);
|
2018-02-21 19:44:13 +00:00
|
|
|
|
2018-07-10 15:19:11 +00:00
|
|
|
if (!newVersion || oldRange === newRange) {
|
2018-04-30 08:42:39 +00:00
|
|
|
delete deps[data.name];
|
2017-12-03 12:17:31 +00:00
|
|
|
} else {
|
2018-04-30 08:42:39 +00:00
|
|
|
deps[data.name].new = newRange;
|
2019-02-03 15:21:00 +00:00
|
|
|
deps[data.name].info = getInfoUrl(data.versions[newVersion] || data);
|
2017-12-03 12:17:31 +00:00
|
|
|
}
|
2018-08-23 20:28:33 +00:00
|
|
|
}
|
2017-12-08 13:39:52 +00:00
|
|
|
|
|
|
|
if (!Object.keys(deps).length) {
|
2018-05-31 17:33:22 +00:00
|
|
|
finish("All packages are up to date.");
|
2017-12-03 15:30:59 +00:00
|
|
|
}
|
|
|
|
|
2018-10-04 05:58:20 +00:00
|
|
|
if (!args.update) {
|
2018-08-23 20:28:33 +00:00
|
|
|
finish();
|
2017-12-03 11:15:02 +00:00
|
|
|
}
|
2017-12-08 13:39:52 +00:00
|
|
|
|
2018-08-23 20:28:33 +00:00
|
|
|
try {
|
|
|
|
fs.writeFileSync(packageFile, updatePkg(), "utf8");
|
|
|
|
} catch (err) {
|
|
|
|
finish(new Error(`Error writing package.json: ${err.message}`));
|
|
|
|
}
|
|
|
|
|
2019-05-09 18:27:11 +00:00
|
|
|
finish(chalk.green(`
|
2018-07-21 07:47:37 +00:00
|
|
|
╭────────────────────────╮
|
|
|
|
│ package.json updated │
|
2019-05-09 18:27:11 +00:00
|
|
|
╰────────────────────────╯`.substring(1)));
|
2018-07-24 17:41:44 +00:00
|
|
|
}).catch(finish);
|
2017-12-03 11:15:02 +00:00
|
|
|
|
2019-05-09 18:27:11 +00:00
|
|
|
function finish(obj, opts = {}) {
|
2017-12-08 13:39:52 +00:00
|
|
|
const output = {};
|
2018-07-24 17:41:44 +00:00
|
|
|
const hadError = obj instanceof Error;
|
|
|
|
|
2017-12-08 13:39:52 +00:00
|
|
|
if (typeof obj === "string") {
|
|
|
|
output.message = obj;
|
2018-07-24 17:41:44 +00:00
|
|
|
} else if (hadError) {
|
2018-11-19 09:18:32 +00:00
|
|
|
output.error = obj.stack;
|
2017-12-08 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (args.json) {
|
2018-07-24 17:41:44 +00:00
|
|
|
if (!hadError) {
|
|
|
|
output.results = deps;
|
|
|
|
}
|
2018-10-25 20:36:41 +00:00
|
|
|
console.info(JSON.stringify(output));
|
2017-12-03 12:17:31 +00:00
|
|
|
} else {
|
2018-07-24 17:41:44 +00:00
|
|
|
if (Object.keys(deps).length && !hadError) {
|
2018-03-03 19:47:48 +00:00
|
|
|
console.info(formatDeps(deps));
|
2017-12-08 13:39:52 +00:00
|
|
|
}
|
|
|
|
if (output.message || output.error) {
|
2018-03-03 19:47:48 +00:00
|
|
|
console.info(output.message || output.error);
|
2017-12-03 12:17:31 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-08 13:39:52 +00:00
|
|
|
|
2018-10-15 06:05:54 +00:00
|
|
|
if (args["error-on-outdated"]) {
|
2018-10-15 04:52:37 +00:00
|
|
|
process.exit(Object.keys(deps).length ? 2 : 0);
|
|
|
|
} else {
|
|
|
|
process.exit(opts.exitCode || (output.error ? 1 : 0));
|
|
|
|
}
|
2017-12-08 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2017-12-03 13:12:30 +00:00
|
|
|
function highlightDiff(a, b, added) {
|
|
|
|
const aParts = a.split(/\./);
|
|
|
|
const bParts = b.split(/\./);
|
2018-11-28 00:00:17 +00:00
|
|
|
const color = chalk[added ? "green" : "red"];
|
2018-07-30 16:55:32 +00:00
|
|
|
const versionPartRe = /^[0-9a-zA-Z-.]+$/;
|
2017-12-03 13:12:30 +00:00
|
|
|
let res = "";
|
|
|
|
|
|
|
|
for (let i = 0; i < aParts.length; i++) {
|
|
|
|
if (aParts[i] !== bParts[i]) {
|
2018-07-10 21:04:08 +00:00
|
|
|
if (versionPartRe.test(aParts[i])) {
|
2018-07-25 09:53:50 +00:00
|
|
|
res += color(aParts.slice(i).join("."));
|
2017-12-03 13:12:30 +00:00
|
|
|
} else {
|
2018-04-30 08:42:39 +00:00
|
|
|
res += aParts[i].split("").map(char => {
|
2018-08-23 20:28:33 +00:00
|
|
|
return versionPartRe.test(char) ? color(char) : char;
|
2018-07-25 09:53:50 +00:00
|
|
|
}).join("") + color("." + aParts.slice(i + 1).join("."));
|
2017-12-03 13:12:30 +00:00
|
|
|
}
|
|
|
|
break;
|
2018-07-10 15:19:11 +00:00
|
|
|
} else {
|
|
|
|
res += aParts[i] + ".";
|
|
|
|
}
|
2017-12-03 13:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-12-08 13:39:52 +00:00
|
|
|
function formatDeps() {
|
2018-11-20 12:17:19 +00:00
|
|
|
const arr = [["NAME", "OLD", "NEW", "INFO"]];
|
2019-02-04 18:12:15 +00:00
|
|
|
|
2018-11-20 12:17:19 +00:00
|
|
|
for (const [name, data] of Object.entries(deps)) arr.push([
|
2018-10-04 05:41:31 +00:00
|
|
|
name,
|
2018-11-20 12:17:19 +00:00
|
|
|
highlightDiff(data.old, data.new, false),
|
|
|
|
highlightDiff(data.new, data.old, true),
|
2019-02-04 18:12:15 +00:00
|
|
|
data.info,
|
2018-10-04 05:41:31 +00:00
|
|
|
]);
|
2019-02-04 18:12:15 +00:00
|
|
|
|
2018-10-04 05:41:31 +00:00
|
|
|
return require("text-table")(arr, {
|
|
|
|
hsep: " ".repeat(4),
|
|
|
|
stringLength: require("string-width"),
|
2017-12-03 11:15:02 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-08 13:39:52 +00:00
|
|
|
function updatePkg() {
|
2019-02-04 18:12:15 +00:00
|
|
|
const esc = require("escape-string-regexp");
|
2017-12-03 11:15:02 +00:00
|
|
|
let newPkgStr = pkgStr;
|
2019-02-04 18:12:15 +00:00
|
|
|
|
2018-08-23 20:28:33 +00:00
|
|
|
for (const dep of Object.keys(deps)) {
|
2017-12-08 13:39:52 +00:00
|
|
|
const re = new RegExp(`"${esc(dep)}": +"${esc(deps[dep].old)}"`, "g");
|
|
|
|
newPkgStr = newPkgStr.replace(re, `"${dep}": "${deps[dep].new}"`);
|
2018-08-23 20:28:33 +00:00
|
|
|
}
|
2019-02-04 18:12:15 +00:00
|
|
|
|
2017-12-03 11:15:02 +00:00
|
|
|
return newPkgStr;
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateRange(range, version) {
|
2017-12-10 17:27:29 +00:00
|
|
|
return range.replace(/[0-9]+\.[0-9]+\.[0-9]+(-.+)?/g, version);
|
2017-12-03 11:15:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isValidSemverRange(range) {
|
|
|
|
let valid = false;
|
|
|
|
try {
|
|
|
|
semver.Range(range);
|
|
|
|
valid = true;
|
|
|
|
} catch (err) {}
|
|
|
|
return valid;
|
|
|
|
}
|
2018-02-21 19:44:13 +00:00
|
|
|
|
2019-01-19 19:05:26 +00:00
|
|
|
function isVersionPrerelease(version) {
|
2019-03-28 20:22:49 +00:00
|
|
|
const parsed = semver.parse(version);
|
|
|
|
if (!parsed) return false;
|
|
|
|
return Boolean(parsed.prerelease.length);
|
2019-01-19 19:05:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function isRangePrerelease(range) {
|
|
|
|
// can not use semver.coerce here because it ignores prerelease tags
|
|
|
|
return /[0-9]+\.[0-9]+\.[0-9]+-.+/.test(range);
|
|
|
|
}
|
|
|
|
|
|
|
|
function rangeToVersion(range) {
|
|
|
|
try {
|
|
|
|
return semver.coerce(range).version;
|
|
|
|
} catch (err) {
|
2019-03-28 20:22:49 +00:00
|
|
|
return null;
|
2019-01-19 19:05:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function findVersion(data, versions, opts) {
|
|
|
|
let tempVersion = rangeToVersion(opts.range);
|
2018-12-22 15:24:24 +00:00
|
|
|
let tempDate = 0;
|
2019-02-25 21:46:39 +00:00
|
|
|
|
2019-04-25 17:50:03 +00:00
|
|
|
const semvers = opts.semvers.slice();
|
2019-01-20 21:26:30 +00:00
|
|
|
const usePre = isRangePrerelease(opts.range) || opts.usePre;
|
|
|
|
|
|
|
|
if (usePre) {
|
2019-04-25 17:50:03 +00:00
|
|
|
semvers.push("prerelease");
|
|
|
|
if (semvers.includes("patch")) semvers.push("prepatch");
|
|
|
|
if (semvers.includes("minor")) semvers.push("preminor");
|
|
|
|
if (semvers.includes("major")) semvers.push("premajor");
|
2019-01-20 21:26:30 +00:00
|
|
|
}
|
2018-11-19 09:18:32 +00:00
|
|
|
|
|
|
|
for (const version of versions) {
|
|
|
|
const parsed = semver.parse(version);
|
2019-02-25 21:46:39 +00:00
|
|
|
if (parsed.prerelease.length && (!usePre || opts.useRel)) continue;
|
2018-11-19 09:18:32 +00:00
|
|
|
|
2019-01-20 21:26:30 +00:00
|
|
|
const diff = semver.diff(tempVersion, parsed.version);
|
|
|
|
if (!diff || !semvers.includes(diff)) continue;
|
2018-12-22 15:24:24 +00:00
|
|
|
|
2019-01-19 15:22:06 +00:00
|
|
|
if (opts.useGreatest) {
|
2019-01-20 21:26:30 +00:00
|
|
|
if (semver.gte(semver.coerce(parsed.version).version, tempVersion)) {
|
2019-01-19 15:22:06 +00:00
|
|
|
tempVersion = parsed.version;
|
|
|
|
}
|
2018-12-22 15:24:24 +00:00
|
|
|
} else {
|
|
|
|
const date = (new Date(data.time[version])).getTime();
|
|
|
|
if (date >= 0 && date > tempDate) {
|
|
|
|
tempVersion = parsed.version;
|
|
|
|
tempDate = date;
|
2018-11-19 09:18:32 +00:00
|
|
|
}
|
2018-02-21 19:44:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-19 09:18:32 +00:00
|
|
|
|
2019-03-28 20:22:49 +00:00
|
|
|
return tempVersion || null;
|
2018-02-21 19:44:13 +00:00
|
|
|
}
|
2018-09-02 19:43:16 +00:00
|
|
|
|
2019-01-19 19:05:26 +00:00
|
|
|
function findNewVersion(data, opts) {
|
2019-02-15 20:16:57 +00:00
|
|
|
if (opts.range === "*") return "*";
|
2019-01-19 19:05:26 +00:00
|
|
|
const versions = Object.keys(data.time).filter(version => semver.valid(version));
|
|
|
|
const version = findVersion(data, versions, opts);
|
|
|
|
|
|
|
|
if (opts.useGreatest) {
|
|
|
|
return version;
|
|
|
|
} else {
|
|
|
|
const latestTag = data["dist-tags"].latest;
|
2019-01-20 21:26:30 +00:00
|
|
|
const oldVersion = semver.coerce(opts.range).version;
|
2019-01-20 21:34:40 +00:00
|
|
|
const oldIsPre = isRangePrerelease(opts.range);
|
2019-01-19 19:05:26 +00:00
|
|
|
const newIsPre = isVersionPrerelease(version);
|
2019-02-25 21:46:39 +00:00
|
|
|
const isGreater = semver.gt(version, oldVersion);
|
2019-01-19 19:05:26 +00:00
|
|
|
|
|
|
|
// update to new prerelease
|
2019-02-25 21:46:39 +00:00
|
|
|
if (!opts.useRel && opts.usePre || (oldIsPre && newIsPre)) {
|
2019-01-19 19:05:26 +00:00
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
2019-02-25 21:46:39 +00:00
|
|
|
// downgrade from prerelease to release on --release-only
|
|
|
|
if (opts.useRel && !isGreater && oldIsPre && !newIsPre) {
|
|
|
|
return version;
|
|
|
|
}
|
2019-02-25 20:50:12 +00:00
|
|
|
|
2019-01-19 19:05:26 +00:00
|
|
|
// update from prerelease to release
|
|
|
|
if (oldIsPre && !newIsPre && isGreater) {
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not downgrade from prerelease to release
|
|
|
|
if (oldIsPre && !newIsPre && !isGreater) {
|
2019-01-20 21:26:30 +00:00
|
|
|
return null;
|
2019-01-19 19:05:26 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 21:46:39 +00:00
|
|
|
// check if latestTag is allowed by semvers
|
|
|
|
const diff = semver.diff(oldVersion, latestTag);
|
|
|
|
if (diff && diff !== "prerelease" && !opts.semvers.includes(diff.replace(/^pre/, ""))) {
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prevent upgrading to prerelease with --release-only
|
|
|
|
if (opts.useRel && isVersionPrerelease(latestTag)) {
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
2019-01-19 19:05:26 +00:00
|
|
|
// in all other cases, return latest dist-tag
|
|
|
|
return latestTag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-02 19:43:16 +00:00
|
|
|
function parseMixedArg(arg) {
|
|
|
|
if (arg === "") {
|
|
|
|
return true;
|
|
|
|
} else if (typeof arg === "string") {
|
|
|
|
return arg.includes(",") ? arg.split(",") : [arg];
|
|
|
|
} else if (Array.isArray(arg)) {
|
|
|
|
return arg;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|