mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
6.9 KiB
244 lines
6.9 KiB
/*
|
|
|
|
npm outdated [pkg]
|
|
|
|
Does the following:
|
|
|
|
1. check for a new version of pkg
|
|
|
|
If no packages are specified, then run for all installed
|
|
packages.
|
|
|
|
*/
|
|
|
|
module.exports = outdated
|
|
|
|
outdated.usage = "npm outdated [<pkg> [<pkg> ...]]"
|
|
|
|
outdated.completion = require("./utils/completion/installed-deep.js")
|
|
|
|
|
|
var path = require("path")
|
|
, fs = require("graceful-fs")
|
|
, readJson = require("read-package-json")
|
|
, cache = require("./cache.js")
|
|
, asyncMap = require("slide").asyncMap
|
|
, npm = require("./npm.js")
|
|
, url = require("url")
|
|
, isGitUrl = require("./utils/is-git-url.js")
|
|
, color = require("ansicolors")
|
|
, styles = require("ansistyles")
|
|
, table = require("text-table")
|
|
|
|
function outdated (args, silent, cb) {
|
|
if (typeof cb !== "function") cb = silent, silent = false
|
|
var dir = path.resolve(npm.dir, "..")
|
|
outdated_(args, dir, {}, 0, function (er, list) {
|
|
if (er || silent) return cb(er, list)
|
|
if (npm.config.get("json")) {
|
|
console.log(makeJSON(list))
|
|
} else {
|
|
var outList = list.map(makePretty)
|
|
var outTable = [[ styles.underline("Package")
|
|
, styles.underline("Current")
|
|
, styles.underline("Wanted")
|
|
, styles.underline("Latest")
|
|
, styles.underline("Location")
|
|
]].concat(outList)
|
|
var tableOpts = { align: ["l", "r", "r", "r", "l"]
|
|
, stringLength: function(s) { return ansiTrim(s).length }
|
|
}
|
|
console.log(table(outTable, tableOpts))
|
|
}
|
|
cb(null, list)
|
|
})
|
|
}
|
|
|
|
// [[ dir, dep, has, want ]]
|
|
function makePretty (p) {
|
|
var parseable = npm.config.get("parseable")
|
|
, long = npm.config.get("long")
|
|
, dep = p[1]
|
|
, dir = path.resolve(p[0], "node_modules", dep)
|
|
, has = p[2]
|
|
, want = p[3]
|
|
, latest = p[4]
|
|
|
|
// XXX add --json support
|
|
// Should match (more or less) the output of ls --json
|
|
|
|
if (parseable) {
|
|
var str = dir
|
|
if (npm.config.get("long")) {
|
|
str += ":" + dep + "@" + want
|
|
+ ":" + (has ? (dep + "@" + has) : "MISSING")
|
|
}
|
|
return str
|
|
}
|
|
|
|
if (!npm.config.get("global")) {
|
|
dir = path.relative(process.cwd(), dir)
|
|
}
|
|
return [ has === want ? color.yellow(dep) : color.red(dep)
|
|
, (has || "MISSING")
|
|
, color.green(want)
|
|
, color.magenta(latest)
|
|
, color.brightBlack(dirToPrettyLocation(dir))
|
|
]
|
|
}
|
|
|
|
function ansiTrim (str) {
|
|
var r = new RegExp("\x1b(?:\\[(?:\\d+[ABCDEFGJKSTm]|\\d+;\\d+[Hfm]|" +
|
|
"\\d+;\\d+;\\d+m|6n|s|u|\\?25[lh])|\\w)", "g");
|
|
return str.replace(r, "")
|
|
}
|
|
|
|
function dirToPrettyLocation (dir) {
|
|
return dir.replace(/^node_modules[/\\]/, "")
|
|
.replace(/[[/\\]node_modules[/\\]/g, " > ")
|
|
}
|
|
|
|
function makeJSON (list) {
|
|
var out = {}
|
|
list.forEach(function (p) {
|
|
var dir = path.resolve(p[0], "node_modules", p[1])
|
|
if (!npm.config.get("global")) {
|
|
dir = path.relative(process.cwd(), dir)
|
|
}
|
|
out[p[1]] = { current: p[2]
|
|
, wanted: p[3]
|
|
, latest: p[4]
|
|
, location: dir
|
|
}
|
|
})
|
|
return JSON.stringify(out, null, 2)
|
|
}
|
|
|
|
function outdated_ (args, dir, parentHas, depth, cb) {
|
|
// get the deps from package.json, or {<dir/node_modules/*>:"*"}
|
|
// asyncMap over deps:
|
|
// shouldHave = cache.add(dep, req).version
|
|
// if has === shouldHave then
|
|
// return outdated(args, dir/node_modules/dep, parentHas + has)
|
|
// else if dep in args or args is empty
|
|
// return [dir, dep, has, shouldHave]
|
|
|
|
if (depth > npm.config.get("depth")) {
|
|
return cb(null, [])
|
|
}
|
|
var deps = null
|
|
readJson(path.resolve(dir, "package.json"), function (er, d) {
|
|
if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
|
|
deps = (er) ? true : (d.dependencies || {})
|
|
var doUpdate = npm.config.get("dev") ||
|
|
(!npm.config.get("production") &&
|
|
!Object.keys(parentHas).length &&
|
|
!npm.config.get("global"))
|
|
if (!er && d && doUpdate) {
|
|
Object.keys(d.devDependencies || {}).forEach(function (k) {
|
|
if (!(k in parentHas)) {
|
|
deps[k] = d.devDependencies[k]
|
|
}
|
|
})
|
|
}
|
|
return next()
|
|
})
|
|
|
|
var has = null
|
|
fs.readdir(path.resolve(dir, "node_modules"), function (er, pkgs) {
|
|
if (er) {
|
|
has = Object.create(parentHas)
|
|
return next()
|
|
}
|
|
pkgs = pkgs.filter(function (p) {
|
|
return !p.match(/^[\._-]/)
|
|
})
|
|
asyncMap(pkgs, function (pkg, cb) {
|
|
var jsonFile = path.resolve(dir, "node_modules", pkg, "package.json")
|
|
readJson(jsonFile, function (er, d) {
|
|
if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
|
|
cb(null, er ? [] : [[d.name, d.version, d._from]])
|
|
})
|
|
}, function (er, pvs) {
|
|
if (er) return cb(er)
|
|
has = Object.create(parentHas)
|
|
pvs.forEach(function (pv) {
|
|
has[pv[0]] = {
|
|
version: pv[1],
|
|
from: pv[2]
|
|
}
|
|
})
|
|
|
|
next()
|
|
})
|
|
})
|
|
|
|
function next () {
|
|
if (!has || !deps) return
|
|
if (deps === true) {
|
|
deps = Object.keys(has).reduce(function (l, r) {
|
|
l[r] = "*"
|
|
return l
|
|
}, {})
|
|
}
|
|
|
|
// now get what we should have, based on the dep.
|
|
// if has[dep] !== shouldHave[dep], then cb with the data
|
|
// otherwise dive into the folder
|
|
asyncMap(Object.keys(deps), function (dep, cb) {
|
|
shouldUpdate(args, dir, dep, has, deps[dep], depth, cb)
|
|
}, cb)
|
|
}
|
|
}
|
|
|
|
function shouldUpdate (args, dir, dep, has, req, depth, cb) {
|
|
// look up the most recent version.
|
|
// if that's what we already have, or if it's not on the args list,
|
|
// then dive into it. Otherwise, cb() with the data.
|
|
|
|
// { version: , from: }
|
|
var curr = has[dep]
|
|
|
|
function skip () {
|
|
outdated_( args
|
|
, path.resolve(dir, "node_modules", dep)
|
|
, has
|
|
, depth + 1
|
|
, cb )
|
|
}
|
|
|
|
function doIt (wanted, latest) {
|
|
cb(null, [[ dir, dep, curr && curr.version, wanted, latest, req ]])
|
|
}
|
|
|
|
if (args.length && args.indexOf(dep) === -1) {
|
|
return skip()
|
|
}
|
|
|
|
if (isGitUrl(url.parse(req)))
|
|
return doIt("git", "git")
|
|
|
|
var registry = npm.registry
|
|
// search for the latest package
|
|
registry.get(dep + "/latest", function (er, l) {
|
|
if (er) return cb()
|
|
// so, we can conceivably update this. find out if we need to.
|
|
cache.add(dep, req, function (er, d) {
|
|
// if this fails, then it means we can't update this thing.
|
|
// it's probably a thing that isn't published.
|
|
if (er) return skip()
|
|
|
|
// check that the url origin hasn't changed (#1727) and that
|
|
// there is no newer version available
|
|
var dFromUrl = d._from && url.parse(d._from).protocol
|
|
var cFromUrl = curr && curr.from && url.parse(curr.from).protocol
|
|
|
|
if (!curr || dFromUrl && cFromUrl && d._from !== curr.from
|
|
|| d.version !== curr.version
|
|
|| d.version !== l.version)
|
|
doIt(d.version, l.version)
|
|
else
|
|
skip()
|
|
})
|
|
})
|
|
}
|
|
|