updates/updates.js

162 lines
4.1 KiB
JavaScript
Raw Normal View History

2017-12-03 11:15:02 +00:00
#!/usr/bin/env node
"use strict";
const cli = require("meow")(`
Options:
2017-12-03 11:18:28 +00:00
--update, -u Also update package.json
2017-12-03 12:17:31 +00:00
--json, -j Output JSON
2017-12-03 11:15:02 +00:00
Examples:
$ updates
2017-12-03 11:35:25 +00:00
$ updates -u
2017-12-03 14:16:59 +00:00
$ updates -j
2017-12-03 11:15:02 +00:00
`);
const fs = require("fs");
2017-12-03 12:17:31 +00:00
const os = require("os");
2017-12-03 11:15:02 +00:00
const got = require("got");
const path = require("path");
const semver = require("semver");
const columnify = require("columnify");
const chalk = require("chalk");
const esc = require("escape-string-regexp");
const pkgStr = fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8");
const pkg = JSON.parse(pkgStr);
const url = "https://registry.npmjs.org/";
const deps = [];
[
"dependencies",
"devDependencies",
"peerDependencies",
"bundledDependencies",
"optionalDependencies"
].forEach(function(key) {
if (pkg[key]) {
Object.keys(pkg[key]).forEach(function(name) {
const range = pkg[key][name];
if (isValidSemverRange(range)) {
deps.push({name, range});
}
});
}
});
Promise.all(deps.map(dep => got(`${url}${dep.name}`))).then(function(responses) {
return responses.map(function(response, i) {
const dep = Object.keys(deps)[i];
const name = deps[dep].name;
const range = deps[dep].range;
const newVersion = JSON.parse(response.body)["dist-tags"]["latest"];
const newRange = updateRange(range, newVersion);
return {name, range, newRange};
});
}).then(function(results) {
2017-12-03 12:17:31 +00:00
results = results.filter(function(result) {
2017-12-03 11:15:02 +00:00
return result.range !== result.newRange;
});
2017-12-03 12:17:31 +00:00
if (!results.length) {
print("All packages are up to date.");
2017-12-03 13:57:48 +00:00
process.exit(0);
2017-12-03 11:18:28 +00:00
} else if (!cli.flags.u && !cli.flags.update) {
2017-12-03 12:17:31 +00:00
if (cli.flags.j || cli.flags.json) {
print(results);
} else {
print(formatResults(results));
}
2017-12-03 11:15:02 +00:00
process.exit(0);
}
return results;
}).then(function(results) {
2017-12-03 11:18:28 +00:00
fs.writeFile("package.json", updatePkg(results), "utf8", function(err) {
2017-12-03 11:15:02 +00:00
if (err) {
2017-12-03 12:17:31 +00:00
print(err);
2017-12-03 11:15:02 +00:00
process.exit(1);
} else {
2017-12-03 12:17:31 +00:00
print("package.json updated!");
2017-12-03 11:15:02 +00:00
process.exit(0);
}
});
});
2017-12-03 12:17:31 +00:00
function print(obj) {
if (cli.flags.j || cli.flags.json) {
if (typeof obj === "string") {
obj = {message: obj};
} else if (obj instanceof Error) {
obj = {error: obj.message};
}
process.stdout.write(JSON.stringify(obj, null, 2) + os.EOL);
} else {
if (obj instanceof Error) {
process.stderr.write(obj + os.EOL);
} else {
process.stdout.write(obj + os.EOL);
}
}
}
2017-12-03 13:12:30 +00:00
function highlightDiff(a, b, added) {
const aParts = a.split(/\./);
const bParts = b.split(/\./);
let res = "";
for (let i = 0; i < aParts.length; i++) {
if (aParts[i] !== bParts[i]) {
if (/^[0-9]+$/.test(aParts[i])) {
res += chalk[added ? "green" : "red"](aParts.slice(i).join("."));
} else {
res += aParts[i].split("").map(function(char) {
if (/^[0-9]+$/.test(char)) {
return chalk[added ? "green" : "red"](char + ".");
} else {
return char;
}
2017-12-03 13:25:59 +00:00
}).join("") + chalk[added ? "green" : "red"](aParts.slice(i + 1).join("."));
2017-12-03 13:12:30 +00:00
}
break;
} else res += aParts[i] + ".";
}
return res;
}
2017-12-03 11:15:02 +00:00
function formatResults(results) {
return columnify(results.map(r => Object.assign({}, r)).map(function(output) {
if (output.newRange !== output.range) {
return {
2017-12-03 13:12:30 +00:00
"package": output.name,
2017-12-03 13:29:40 +00:00
"old": highlightDiff(output.range, output.newRange, false),
"new": highlightDiff(output.newRange, output.range, true),
2017-12-03 11:15:02 +00:00
};
}
2017-12-03 12:17:31 +00:00
}), {
2017-12-03 11:15:02 +00:00
columnSplitter: " ",
});
}
2017-12-03 11:18:28 +00:00
function updatePkg(results) {
2017-12-03 11:15:02 +00:00
let newPkgStr = pkgStr;
results.forEach(function(result) {
const re = new RegExp(`"${esc(result.name)}": +"${esc(result.range)}"`, "g");
newPkgStr = newPkgStr.replace(re, `"${result.name}": "${result.newRange}"`);
});
return newPkgStr;
}
// naive regex replace
function updateRange(range, version) {
2017-12-03 13:42:11 +00:00
return range.replace(/[0-9]+\.[0-9]+\.[0-9]+(-.+)?/, 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;
}