diff options
Diffstat (limited to 'deps/npm/lib')
41 files changed, 1244 insertions, 888 deletions
diff --git a/deps/npm/lib/adduser.js b/deps/npm/lib/adduser.js index 579ecb0a9..7e933ea7d 100644 --- a/deps/npm/lib/adduser.js +++ b/deps/npm/lib/adduser.js @@ -19,9 +19,10 @@ function adduser (args, cb) { if (!crypto) return cb(new Error( "You must compile node with ssl support to use the adduser feature")) - var c = { u : npm.config.get("username") || "" - , p : npm.config.get("_password") || "" - , e : npm.config.get("email") || "" + var creds = npm.config.getCredentialsByURI(npm.config.get("registry")) + var c = { u : creds.username || "" + , p : creds.password || "" + , e : creds.email || "" } , u = {} , fns = [readUsername, readPassword, readEmail, save] @@ -94,7 +95,7 @@ function readPassword (c, u, cb) { return readPassword(c, u, cb) } - c.changed = c.changed || c.p != pw + c.changed = c.changed || c.p !== pw u.p = pw cb(er) }) @@ -132,17 +133,45 @@ function save (c, u, cb) { registry.password = u.p } npm.spinner.start() + // save existing configs, but yank off for this PUT - registry.adduser(npm.config.get("registry"), u.u, u.p, u.e, function (er) { + var uri = npm.config.get("registry") + var scope = npm.config.get("scope") + + // there may be a saved scope and no --registry (for login) + if (scope) { + if (scope.charAt(0) !== "@") scope = "@" + scope + + var scopedRegistry = npm.config.get(scope + ":registry") + if (scopedRegistry) uri = scopedRegistry + } + + registry.adduser(uri, u.u, u.p, u.e, function (er, doc) { npm.spinner.stop() if (er) return cb(er) + registry.username = u.u registry.password = u.p registry.email = u.e - npm.config.set("username", u.u, "user") - npm.config.set("_password", u.p, "user") - npm.config.set("email", u.e, "user") + + // don't want this polluting the configuration npm.config.del("_token", "user") + + if (scope) npm.config.set(scope + ":registry", uri, "user") + + if (doc && doc.token) { + npm.config.setCredentialsByURI(uri, { + token : doc.token + }) + } + else { + npm.config.setCredentialsByURI(uri, { + username : u.u, + password : u.p, + email : u.e + }) + } + log.info("adduser", "Authorized user %s", u.u) npm.config.save("user", cb) }) diff --git a/deps/npm/lib/bugs.js b/deps/npm/lib/bugs.js index b3022bf2a..16744cd5c 100644 --- a/deps/npm/lib/bugs.js +++ b/deps/npm/lib/bugs.js @@ -9,19 +9,23 @@ var npm = require("./npm.js") , opener = require("opener") , path = require("path") , readJson = require("read-package-json") + , npa = require("npm-package-arg") , fs = require("fs") - , url = require("url") + , mapToRegistry = require("./utils/map-to-registry.js") bugs.completion = function (opts, cb) { if (opts.conf.argv.remain.length > 2) return cb() - var uri = url.resolve(npm.config.get("registry"), "-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } function bugs (args, cb) { - var n = args.length && args[0].split("@").shift() || '.' + var n = args.length && npa(args[0]).name || '.' fs.stat(n, function (er, s) { if (er && er.code === "ENOENT") return callRegistry(n, cb) else if (er) return cb (er) @@ -56,9 +60,13 @@ function getUrlAndOpen (d, cb) { } function callRegistry (n, cb) { - var uri = url.resolve(npm.config.get("registry"), n + "/latest") - registry.get(uri, { timeout : 3600 }, function (er, d) { + mapToRegistry(n, npm.config, function (er, uri) { if (er) return cb(er) - getUrlAndOpen (d, cb) + + registry.get(uri + "/latest", { timeout : 3600 }, function (er, d) { + if (er) return cb(er) + + getUrlAndOpen (d, cb) + }) }) } diff --git a/deps/npm/lib/build.js b/deps/npm/lib/build.js index 350774a45..f1c61bdb8 100644 --- a/deps/npm/lib/build.js +++ b/deps/npm/lib/build.js @@ -75,7 +75,7 @@ function linkStuff (pkg, folder, global, didRB, cb) { // if it's global, and folder is in {prefix}/node_modules, // then bins are in {prefix}/bin // otherwise, then bins are in folder/../.bin - var parent = path.dirname(folder) + var parent = pkg.name[0] === "@" ? path.dirname(path.dirname(folder)) : path.dirname(folder) , gnm = global && npm.globalDir , gtop = parent === gnm @@ -95,7 +95,7 @@ function linkStuff (pkg, folder, global, didRB, cb) { function shouldWarn(pkg, folder, global, cb) { var parent = path.dirname(folder) , top = parent === npm.dir - , cwd = process.cwd() + , cwd = npm.localPrefix readJson(path.resolve(cwd, "package.json"), function(er, topPkg) { if (er) return cb(er) diff --git a/deps/npm/lib/cache.js b/deps/npm/lib/cache.js index 37bba5a06..281d6100a 100644 --- a/deps/npm/lib/cache.js +++ b/deps/npm/lib/cache.js @@ -47,9 +47,15 @@ adding a name@range: adding a local tarball: 1. untar to tmp/random/{blah} 2. goto folder(2) + +adding a namespaced package: +1. lookup registry for @namespace +2. namespace_registry.get('name') +3. add url(namespace/latest.tarball) */ exports = module.exports = cache + cache.unpack = unpack cache.clean = clean cache.read = read @@ -61,17 +67,18 @@ var npm = require("./npm.js") , readJson = require("read-package-json") , log = require("npmlog") , path = require("path") - , url = require("url") , asyncMap = require("slide").asyncMap , tar = require("./utils/tar.js") , fileCompletion = require("./utils/completion/file-completion.js") - , isGitUrl = require("./utils/is-git-url.js") , deprCheck = require("./utils/depr-check.js") , addNamed = require("./cache/add-named.js") , addLocal = require("./cache/add-local.js") , addRemoteTarball = require("./cache/add-remote-tarball.js") , addRemoteGit = require("./cache/add-remote-git.js") + , maybeGithub = require("./cache/maybe-github.js") , inflight = require("inflight") + , npa = require("npm-package-arg") + , getStat = require("./cache/get-stat.js") cache.usage = "npm cache add <tarball file>" + "\nnpm cache add <folder>" @@ -108,9 +115,8 @@ function cache (args, cb) { switch (cmd) { case "rm": case "clear": case "clean": return clean(args, cb) case "list": case "sl": case "ls": return ls(args, cb) - case "add": return add(args, cb) - default: return cb(new Error( - "Invalid cache action: "+cmd)) + case "add": return add(args, npm.prefix, cb) + default: return cb("Usage: "+cache.usage) } } @@ -147,15 +153,30 @@ function read (name, ver, forceBypass, cb) { }) } +function normalize (args) { + var normalized = "" + if (args.length > 0) { + var a = npa(args[0]) + if (a.name) normalized = a.name + if (a.rawSpec) normalized = [normalized, a.rawSpec].join("/") + if (args.length > 1) normalized = [normalized].concat(args.slice(1)).join("/") + } + + if (normalized.substr(-1) === "/") { + normalized = normalized.substr(0, normalized.length - 1) + } + log.silly("ls", "normalized", normalized) + + return normalized +} + // npm cache ls [<path>] function ls (args, cb) { - args = args.join("/").split("@").join("/") - if (args.substr(-1) === "/") args = args.substr(0, args.length - 1) var prefix = npm.config.get("cache") - if (0 === prefix.indexOf(process.env.HOME)) { + if (prefix.indexOf(process.env.HOME) === 0) { prefix = "~" + prefix.substr(process.env.HOME.length) } - ls_(args, npm.config.get("depth"), function (er, files) { + ls_(normalize(args), npm.config.get("depth"), function (er, files) { console.log(files.map(function (f) { return path.join(prefix, f) }).join("\n").trim()) @@ -174,9 +195,7 @@ function clean (args, cb) { if (!args) args = [] - args = args.join("/").split("@").join("/") - if (args.substr(-1) === "/") args = args.substr(0, args.length - 1) - var f = path.join(npm.cache, path.normalize(args)) + var f = path.join(npm.cache, path.normalize(normalize(args))) if (f === npm.cache) { fs.readdir(npm.cache, function (er, files) { if (er) return cb() @@ -187,30 +206,30 @@ function clean (args, cb) { }) , rm, cb ) }) - } else rm(path.join(npm.cache, path.normalize(args)), cb) + } else rm(path.join(npm.cache, path.normalize(normalize(args))), cb) } // npm cache add <tarball-url> // npm cache add <pkg> <ver> // npm cache add <tarball> // npm cache add <folder> -cache.add = function (pkg, ver, scrub, cb) { +cache.add = function (pkg, ver, where, scrub, cb) { assert(typeof pkg === "string", "must include name of package to install") assert(typeof cb === "function", "must include callback") if (scrub) { return clean([], function (er) { if (er) return cb(er) - add([pkg, ver], cb) + add([pkg, ver], where, cb) }) } log.verbose("cache add", [pkg, ver]) - return add([pkg, ver], cb) + return add([pkg, ver], where, cb) } var adding = 0 -function add (args, cb) { +function add (args, where, cb) { // this is hot code. almost everything passes through here. // the args can be any of: // ["url"] @@ -226,60 +245,69 @@ function add (args, cb) { + " npm cache add <pkg>@<ver>\n" + " npm cache add <tarball>\n" + " npm cache add <folder>\n" - , name , spec + , p if (args[1] === undefined) args[1] = null // at this point the args length must ==2 if (args[1] !== null) { - name = args[0] - spec = args[1] + spec = args[0]+"@"+args[1] } else if (args.length === 2) { spec = args[0] } - log.verbose("cache add", "name=%j spec=%j args=%j", name, spec, args) + log.verbose("cache add", "spec=%j args=%j", spec, args) - if (!name && !spec) return cb(usage) + if (!spec) return cb(usage) if (adding <= 0) { npm.spinner.start() } adding ++ - cb = afterAdd([name, spec], cb) - - // see if the spec is a url - // otherwise, treat as name@version - var p = url.parse(spec) || {} - log.verbose("parsed url", p) - - // If there's a /, and it's a path, then install the path. - // If not, and there's a @, it could be that we got name@http://blah - // in that case, we will not have a protocol now, but if we - // split and check, we will. - if (!name && !p.protocol) { - return maybeFile(spec, cb) - } - else { - switch (p.protocol) { - case "http:": - case "https:": - return addRemoteTarball(spec, { name: name }, null, cb) + cb = afterAdd(cb) + + // package.json can have local URI ("file:") dependencies which require + // normalization + p = npa(spec) + if (p.type === "local" && where) spec = path.resolve(where, p.spec) + log.verbose("parsed spec", p) + + // short-circuit local installs + fs.stat(spec, function (er, s) { + if (er) return addNonLocal(spec, cb) + if (!s.isDirectory()) return addAndLogLocal(spec, cb) + fs.stat(path.join(spec, "package.json"), function (er) { + if (er) return addNonLocal(spec, cb) + addAndLogLocal(spec, cb) + }) + }) +} + +function addAndLogLocal (spec, cb) { + log.verbose("cache add", "local package", path.resolve(spec)) + return addLocal(spec, null, cb) +} +function addNonLocal (spec, cb) { + var p = npa(spec) + log.verbose("parsed spec", p) + + switch (p.type) { + case "remote": + addRemoteTarball(p.spec, {name : p.name}, null, cb) + break + case "git": + addRemoteGit(p.spec, false, cb) + break + case "github": + maybeGithub(p.spec, cb) + break default: - if (isGitUrl(p)) return addRemoteGit(spec, p, false, cb) + if (p.name) return addNamed(p.name, p.spec, null, cb) - // if we have a name and a spec, then try name@spec - if (name) { - addNamed(name, spec, null, cb) - } - // if not, then try just spec (which may try name@"" if not found) - else { - addLocal(spec, {}, cb) - } + cb(new Error("couldn't figure out how to install " + spec)) } - } } function unpack (pkg, ver, unpackTarget, dMode, fMode, uid, gid, cb) { @@ -304,7 +332,7 @@ function unpack (pkg, ver, unpackTarget, dMode, fMode, uid, gid, cb) { }) } -function afterAdd (arg, cb) { return function (er, data) { +function afterAdd (cb) { return function (er, data) { adding -- if (adding <= 0) { npm.spinner.stop() @@ -322,49 +350,32 @@ function afterAdd (arg, cb) { return function (er, data) { var done = inflight(pj, cb) - if (!done) return + if (!done) return undefined fs.writeFile(tmp, JSON.stringify(data), "utf8", function (er) { if (er) return done(er) - fs.rename(tmp, pj, function (er) { - done(er, data) + getStat(function (er, cs) { + if (er) return done(er) + fs.rename(tmp, pj, function (er) { + if (cs.uid && cs.gid) { + fs.chown(pj, cs.uid, cs.gid, function (er) { + return done(er, data) + }) + } else { + done(er, data) + } + }) }) }) }} -function maybeFile (spec, cb) { - // split name@2.3.4 only if name is a valid package name, - // don't split in case of "./test@example.com/" (local path) - fs.stat(spec, function (er) { - if (!er) { - // definitely a local thing - return addLocal(spec, {}, cb) - } - - maybeAt(spec, cb) - }) -} - -function maybeAt (spec, cb) { - if (spec.indexOf("@") !== -1) { - var tmp = spec.split("@") - - var name = tmp.shift() - spec = tmp.join("@") - add([name, spec], cb) - } else { - // already know it's not a url, so must be local - addLocal(spec, {}, cb) - } -} - -function needName(er, data) { +function needName (er, data) { return er ? er : (data && !data.name) ? new Error("No name provided") : null } -function needVersion(er, data) { +function needVersion (er, data) { return er ? er : (data && !data.version) ? new Error("No version provided") : null diff --git a/deps/npm/lib/cache/add-local-tarball.js b/deps/npm/lib/cache/add-local-tarball.js index bcb938fa9..f7cd76103 100644 --- a/deps/npm/lib/cache/add-local-tarball.js +++ b/deps/npm/lib/cache/add-local-tarball.js @@ -191,7 +191,15 @@ function addTmpTarball_ (tgz, data, shasum, cb) { var target = path.resolve(root, "package.tgz") getCacheStat(function (er, cs) { if (er) return cb(er) - mkdir(pkg, function (er) { + mkdir(pkg, function (er, created) { + + // chown starting from the first dir created by mkdirp, + // or the root dir, if none had to be created, so that + // we know that we get all the children. + function chown (er) { + chownr(created || root, cs.uid, cs.gid, done) + } + if (er) return cb(er) var read = fs.createReadStream(tgz) var write = fs.createWriteStream(target) @@ -199,9 +207,6 @@ function addTmpTarball_ (tgz, data, shasum, cb) { read.on("error", cb).pipe(write).on("error", cb).on("close", fin) }) - function chown () { - chownr(root, cs.uid, cs.gid, done) - } }) function done() { diff --git a/deps/npm/lib/cache/add-local.js b/deps/npm/lib/cache/add-local.js index 2a6d8cf88..bedf34bac 100644 --- a/deps/npm/lib/cache/add-local.js +++ b/deps/npm/lib/cache/add-local.js @@ -13,9 +13,7 @@ var fs = require("graceful-fs") , lock = locker.lock , unlock = locker.unlock , getCacheStat = require("./get-stat.js") - , addNamed = require("./add-named.js") , addLocalTarball = require("./add-local-tarball.js") - , maybeGithub = require("./maybe-github.js") , sha = require("sha") module.exports = addLocal @@ -29,16 +27,12 @@ function addLocal (p, pkgData, cb_) { function cb (er, data) { unlock(p, function () { if (er) { - // if it doesn't have a / in it, it might be a - // remote thing. - if (p.indexOf("/") === -1 && p.charAt(0) !== "." - && (process.platform !== "win32" || p.indexOf("\\") === -1)) { - return addNamed(p, "", null, cb_) - } log.error("addLocal", "Could not install %s", p) return cb_(er) } - if (data && !data._fromGithub) data._from = p + if (data && !data._fromGithub) { + data._from = path.relative(npm.prefix, p) || "." + } return cb_(er, data) }) } @@ -47,14 +41,8 @@ function addLocal (p, pkgData, cb_) { if (er) return cb(er) // figure out if this is a folder or file. fs.stat(p, function (er, s) { - if (er) { - // might be username/project - // in that case, try it as a github url. - if (p.split("/").length === 2) { - return maybeGithub(p, er, cb) - } - return cb(er) - } + if (er) return cb(er) + if (s.isDirectory()) addLocalDirectory(p, pkgData, null, cb) else addLocalTarball(p, pkgData, null, cb) }) diff --git a/deps/npm/lib/cache/add-named.js b/deps/npm/lib/cache/add-named.js index 7137cc9b5..091d43c01 100644 --- a/deps/npm/lib/cache/add-named.js +++ b/deps/npm/lib/cache/add-named.js @@ -13,8 +13,8 @@ var path = require("path") , locker = require("../utils/locker.js") , lock = locker.lock , unlock = locker.unlock - , maybeGithub = require("./maybe-github.js") , addRemoteTarball = require("./add-remote-tarball.js") + , mapToRegistry = require("../utils/map-to-registry.js") module.exports = addNamed @@ -48,7 +48,7 @@ function addNamed (name, version, data, cb_) { }) } -function addNameTag (name, tag, data, cb_) { +function addNameTag (name, tag, data, cb) { log.info("addNameTag", [name, tag]) var explicit = true if (!tag) { @@ -56,17 +56,13 @@ function addNameTag (name, tag, data, cb_) { tag = npm.config.get("tag") } - function cb(er, data) { - // might be username/project - // in that case, try it as a github url. - if (er && tag.split("/").length === 2) { - return maybeGithub(tag, er, cb_) - } - return cb_(er, data) - } + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) - var uri = url.resolve(npm.config.get("registry"), name) - registry.get(uri, null, function (er, data, json, resp) { + registry.get(uri, null, next) + }) + + function next (er, data, json, resp) { if (!er) { er = errorResponse(name, resp) } @@ -83,7 +79,7 @@ function addNameTag (name, tag, data, cb_) { er = installTargetsError(tag, data) return cb(er) - }) + } } function engineFilter (data) { @@ -114,22 +110,28 @@ function addNameVersion (name, v, data, cb) { response = null return next() } - var uri = url.resolve(npm.config.get("registry"), name) - registry.get(uri, null, function (er, d, json, resp) { + + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, setData) + }) + + function setData (er, d, json, resp) { if (!er) { er = errorResponse(name, resp) } if (er) return cb(er) data = d && d.versions[ver] if (!data) { - er = new Error('version not found: ' + name + '@' + ver) + er = new Error("version not found: "+name+"@"+ver) er.package = name er.statusCode = 404 return cb(er) } response = resp next() - }) + } function next () { deprCheck(data) @@ -166,10 +168,9 @@ function addNameVersion (name, v, data, cb) { return cb(new Error("Cannot fetch: "+dist.tarball)) } - // use the same protocol as the registry. - // https registry --> https tarballs, but - // only if they're the same hostname, or else - // detached tarballs may not work. + // Use the same protocol as the registry. https registry --> https + // tarballs, but only if they're the same hostname, or else detached + // tarballs may not work. var tb = url.parse(dist.tarball) var rp = url.parse(npm.config.get("registry")) if (tb.hostname === rp.hostname @@ -179,8 +180,8 @@ function addNameVersion (name, v, data, cb) { } tb = url.format(tb) - // only add non-shasum'ed packages if --forced. - // only ancient things would lack this for good reasons nowadays. + // Only add non-shasum'ed packages if --forced. Only ancient things + // would lack this for good reasons nowadays. if (!dist.shasum && !npm.config.get("force")) { return cb(new Error("package lacks shasum: " + data._id)) } @@ -197,15 +198,21 @@ function addNameRange (name, range, data, cb) { log.silly("addNameRange", {name:name, range:range, hasData:!!data}) if (data) return next() - var uri = url.resolve(npm.config.get("registry"), name) - registry.get(uri, null, function (er, d, json, resp) { + + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, setData) + }) + + function setData (er, d, json, resp) { if (!er) { er = errorResponse(name, resp) } if (er) return cb(er) data = d next() - }) + } function next () { log.silly( "addNameRange", "number 2" diff --git a/deps/npm/lib/cache/add-remote-git.js b/deps/npm/lib/cache/add-remote-git.js index 7743aa4a4..304d2f3f0 100644 --- a/deps/npm/lib/cache/add-remote-git.js +++ b/deps/npm/lib/cache/add-remote-git.js @@ -8,9 +8,7 @@ var mkdir = require("mkdirp") , url = require("url") , chownr = require("chownr") , zlib = require("zlib") - , which = require("which") , crypto = require("crypto") - , chmodr = require("chmodr") , npm = require("../npm.js") , rm = require("../utils/gently-rm.js") , inflight = require("inflight") @@ -28,9 +26,8 @@ var mkdir = require("mkdirp") // 5. git archive /tmp/random.tgz // 6. addLocalTarball(/tmp/random.tgz) <gitref> --format=tar --prefix=package/ // silent flag is used if this should error quietly -module.exports = function addRemoteGit (u, parsed, silent, cb_) { +module.exports = function addRemoteGit (u, silent, cb_) { assert(typeof u === "string", "must have git URL") - assert(typeof parsed === "object", "must have parsed query") assert(typeof cb_ === "function", "must have callback") function cb (er, data) { @@ -41,6 +38,10 @@ module.exports = function addRemoteGit (u, parsed, silent, cb_) { if (!cb_) return + log.verbose("addRemoteGit", "u=%j silent=%j", u, silent) + var parsed = url.parse(u, true) + log.silly("addRemoteGit", "parsed", parsed) + // git is so tricky! // if the path is like ssh://foo:22/some/path then it works, but // it needs the ssh:// @@ -62,16 +63,16 @@ module.exports = function addRemoteGit (u, parsed, silent, cb_) { var co = parsed.hash && parsed.hash.substr(1) || "master" var v = crypto.createHash("sha1").update(u).digest("hex").slice(0, 8) - v = u.replace(/[^a-zA-Z0-9]+/g, '-') + '-' + v + v = u.replace(/[^a-zA-Z0-9]+/g, "-")+"-"+v log.verbose("addRemoteGit", [u, co]) var p = path.join(npm.config.get("cache"), "_git-remotes", v) checkGitDir(p, u, co, origUrl, silent, function(er, data) { - chmodr(p, npm.modes.file, function(erChmod) { + addModeRecursive(p, npm.modes.file, function(erAddMode) { if (er) return cb(er, data) - return cb(erChmod, data) + return cb(erAddMode, data) }) }) }) @@ -181,16 +182,20 @@ function archiveGitRemote (p, u, co, origUrl, cb) { parsed.hash = stdout resolved = url.format(parsed) + if (parsed.protocol !== "git:") { + resolved = "git+" + resolved + } + // https://github.com/npm/npm/issues/3224 // node incorrectly sticks a / at the start of the path // We know that the host won't change, so split and detect this var spo = origUrl.split(parsed.host) var spr = resolved.split(parsed.host) - if (spo[1].charAt(0) === ':' && spr[1].charAt(0) === '/') + if (spo[1].charAt(0) === ":" && spr[1].charAt(0) === "/") spr[1] = spr[1].slice(1) resolved = spr.join(parsed.host) - log.verbose('resolved git url', resolved) + log.verbose("resolved git url", resolved) next() }) } @@ -226,8 +231,48 @@ function gitEnv () { if (gitEnv_) return gitEnv_ gitEnv_ = {} for (var k in process.env) { - if (!~['GIT_PROXY_COMMAND','GIT_SSH','GIT_SSL_NO_VERIFY'].indexOf(k) && k.match(/^GIT/)) continue + if (!~["GIT_PROXY_COMMAND","GIT_SSH","GIT_SSL_NO_VERIFY"].indexOf(k) && k.match(/^GIT/)) continue gitEnv_[k] = process.env[k] } return gitEnv_ } + +// similar to chmodr except it add permissions rather than overwriting them +// adapted from https://github.com/isaacs/chmodr/blob/master/chmodr.js +function addModeRecursive(p, mode, cb) { + fs.readdir(p, function (er, children) { + // Any error other than ENOTDIR means it's not readable, or doesn't exist. + // Give up. + if (er && er.code !== "ENOTDIR") return cb(er) + if (er || !children.length) return addMode(p, mode, cb) + + var len = children.length + var errState = null + children.forEach(function (child) { + addModeRecursive(path.resolve(p, child), mode, then) + }) + + function then (er) { + if (errState) return undefined + if (er) return cb(errState = er) + if (--len === 0) return addMode(p, dirMode(mode), cb) + } + }) +} + +function addMode(p, mode, cb) { + fs.stat(p, function (er, stats) { + if (er) return cb(er) + mode = stats.mode | mode + fs.chmod(p, mode, cb) + }) +} + +// taken from https://github.com/isaacs/chmodr/blob/master/chmodr.js +function dirMode(mode) { + if (mode & parseInt("0400", 8)) mode |= parseInt("0100", 8) + if (mode & parseInt( "040", 8)) mode |= parseInt( "010", 8) + if (mode & parseInt( "04", 8)) mode |= parseInt( "01", 8) + return mode +} + diff --git a/deps/npm/lib/cache/add-remote-tarball.js b/deps/npm/lib/cache/add-remote-tarball.js index db9a05d82..2c7d01303 100644 --- a/deps/npm/lib/cache/add-remote-tarball.js +++ b/deps/npm/lib/cache/add-remote-tarball.js @@ -4,8 +4,9 @@ var mkdir = require("mkdirp") , path = require("path") , sha = require("sha") , retry = require("retry") + , createWriteStream = require("graceful-fs").createWriteStream , npm = require("../npm.js") - , fetch = require("../utils/fetch.js") + , registry = npm.registry , inflight = require("inflight") , locker = require("../utils/locker.js") , lock = locker.lock @@ -80,27 +81,39 @@ function addRemoteTarball_(u, tmp, shasum, cb) { } function fetchAndShaCheck (u, tmp, shasum, cb) { - fetch(u, tmp, function (er, response) { + registry.fetch(u, null, function (er, response) { if (er) { log.error("fetch failed", u) return cb(er, response) } - if (!shasum) { - // Well, we weren't given a shasum, so at least sha what we have - // in case we want to compare it to something else later - return sha.get(tmp, function (er, shasum) { - cb(er, response, shasum) - }) - } + var tarball = createWriteStream(tmp, { mode : npm.modes.file }) + tarball.on("error", function (er) { + cb(er) + tarball.destroy() + }) - // validate that the url we just downloaded matches the expected shasum. - sha.check(tmp, shasum, function (er) { - if (er && er.message) { - // add original filename for better debuggability - er.message = er.message + '\n' + 'From: ' + u + tarball.on("finish", function () { + if (!shasum) { + // Well, we weren't given a shasum, so at least sha what we have + // in case we want to compare it to something else later + return sha.get(tmp, function (er, shasum) { + log.silly("fetchAndShaCheck", "shasum", shasum) + cb(er, response, shasum) + }) } - return cb(er, response, shasum) + + // validate that the url we just downloaded matches the expected shasum. + log.silly("fetchAndShaCheck", "shasum", shasum) + sha.check(tmp, shasum, function (er) { + if (er && er.message) { + // add original filename for better debuggability + er.message = er.message + "\n" + "From: " + u + } + return cb(er, response, shasum) + }) }) + + response.pipe(tarball) }) } diff --git a/deps/npm/lib/cache/get-stat.js b/deps/npm/lib/cache/get-stat.js index 913f5af85..372a86d61 100644 --- a/deps/npm/lib/cache/get-stat.js +++ b/deps/npm/lib/cache/get-stat.js @@ -24,7 +24,9 @@ module.exports = function getCacheStat (cb) { } function makeCacheDir (cb) { - if (!process.getuid) return mkdir(npm.cache, cb) + if (!process.getuid) return mkdir(npm.cache, function (er) { + return cb(er, {}) + }) var uid = +process.getuid() , gid = +process.getgid() diff --git a/deps/npm/lib/cache/maybe-github.js b/deps/npm/lib/cache/maybe-github.js index fee64c5df..5ecdb6915 100644 --- a/deps/npm/lib/cache/maybe-github.js +++ b/deps/npm/lib/cache/maybe-github.js @@ -1,29 +1,26 @@ -var url = require("url") - , assert = require("assert") +var assert = require("assert") , log = require("npmlog") , addRemoteGit = require("./add-remote-git.js") -module.exports = function maybeGithub (p, er, cb) { +module.exports = function maybeGithub (p, cb) { assert(typeof p === "string", "must pass package name") - assert(er instanceof Error, "must include error") assert(typeof cb === "function", "must pass callback") var u = "git://github.com/" + p - , up = url.parse(u) log.info("maybeGithub", "Attempting %s from %s", p, u) - return addRemoteGit(u, up, true, function (er2, data) { - if (er2) { + return addRemoteGit(u, true, function (er, data) { + if (er) { var upriv = "git+ssh://git@github.com:" + p - , uppriv = url.parse(upriv) - log.info("maybeGithub", "Attempting %s from %s", p, upriv) - return addRemoteGit(upriv, uppriv, false, function (er3, data) { - if (er3) return cb(er) + return addRemoteGit(upriv, false, function (er, data) { + if (er) return cb(er) + success(upriv, data) }) } + success(u, data) }) diff --git a/deps/npm/lib/dedupe.js b/deps/npm/lib/dedupe.js index e6762e15b..74397d0cb 100644 --- a/deps/npm/lib/dedupe.js +++ b/deps/npm/lib/dedupe.js @@ -7,7 +7,6 @@ // much better "put pkg X at folder Y" abstraction. Oh well, // whatever. Perfect enemy of the good, and all that. -var url = require("url") var fs = require("fs") var asyncMap = require("slide").asyncMap var path = require("path") @@ -16,6 +15,7 @@ var semver = require("semver") var rm = require("./utils/gently-rm.js") var log = require("npmlog") var npm = require("./npm.js") +var mapToRegistry = require("./utils/map-to-registry.js") module.exports = dedupe @@ -61,7 +61,7 @@ function dedupe_ (dir, filter, unavoidable, dryrun, silent, cb) { Object.keys(obj.children).forEach(function (k) { U(obj.children[k]) }) - }) + })(data) // then collect them up and figure out who needs them ;(function C (obj) { @@ -240,13 +240,19 @@ function findVersions (npm, summary, cb) { var versions = data.versions var ranges = data.ranges - var uri = url.resolve(npm.config.get("registry"), name) - npm.registry.get(uri, null, function (er, data) { + mapToRegistry(name, npm.config, function (er, uri) { + if (er) return cb(er) + + npm.registry.get(uri, null, next) + }) + + function next (er, data) { var regVersions = er ? [] : Object.keys(data.versions) var locMatch = bestMatch(versions, ranges) - var regMatch; var tag = npm.config.get("tag") var distTag = data["dist-tags"] && data["dist-tags"][tag] + + var regMatch if (distTag && data.versions[distTag] && matches(distTag, ranges)) { regMatch = distTag } else { @@ -254,7 +260,7 @@ function findVersions (npm, summary, cb) { } cb(null, [[name, has, loc, locMatch, regMatch, locs]]) - }) + } }, cb) } diff --git a/deps/npm/lib/deprecate.js b/deps/npm/lib/deprecate.js index 175b69ceb..17dd4eab0 100644 --- a/deps/npm/lib/deprecate.js +++ b/deps/npm/lib/deprecate.js @@ -1,5 +1,6 @@ -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") module.exports = deprecate @@ -8,16 +9,20 @@ deprecate.usage = "npm deprecate <pkg>[@<version>] <message>" deprecate.completion = function (opts, cb) { // first, get a list of remote packages this user owns. // once we have a user account, then don't complete anything. - var un = npm.config.get("username") - if (!npm.config.get("username")) return cb() if (opts.conf.argv.remain.length > 2) return cb() // get the list of packages by user - var path = "/-/by-user/"+encodeURIComponent(un) - , uri = url.resolve(npm.config.get("registry"), path) - npm.registry.get(uri, { timeout : 60000 }, function (er, list) { - if (er) return cb() - console.error(list) - return cb(null, list[un]) + var path = "/-/by-user/" + mapToRegistry(path, npm.config, function (er, uri) { + if (er) return cb(er) + + var c = npm.config.getCredentialsByURI(uri) + if (!(c && c.username)) return cb() + + npm.registry.get(uri + c.username, { timeout : 60000 }, function (er, list) { + if (er) return cb() + console.error(list) + return cb(null, list[c.username]) + }) }) } @@ -25,11 +30,15 @@ function deprecate (args, cb) { var pkg = args[0] , msg = args[1] if (msg === undefined) return cb("Usage: " + deprecate.usage) + // fetch the data and make sure it exists. - pkg = pkg.split(/@/) - var name = pkg.shift() - , ver = pkg.join("@") - , uri = url.resolve(npm.config.get("registry"), name) + var p = npa(pkg) + + mapToRegistry(p.name, npm.config, next) + + function next (er, uri) { + if (er) return cb(er) - npm.registry.deprecate(uri, ver, msg, cb) + npm.registry.deprecate(uri, p.spec, msg, cb) + } } diff --git a/deps/npm/lib/docs.js b/deps/npm/lib/docs.js index 77073fbb9..dead3f755 100644 --- a/deps/npm/lib/docs.js +++ b/deps/npm/lib/docs.js @@ -5,18 +5,21 @@ docs.usage += "\n" docs.usage += "npm docs ." docs.completion = function (opts, cb) { - var uri = url_.resolve(npm.config.get("registry"), "/-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("/-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } -var url_ = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , opener = require("opener") , path = require("path") , log = require("npmlog") + , mapToRegistry = require("./utils/map-to-registry.js") function url (json) { return json.homepage ? json.homepage : "https://npmjs.org/package/" + json.name @@ -38,7 +41,7 @@ function docs (args, cb) { function getDoc (project, cb) { project = project || '.' - var package = path.resolve(process.cwd(), "package.json") + var package = path.resolve(npm.localPrefix, "package.json") if (project === '.' || project === './') { var json @@ -54,8 +57,13 @@ function getDoc (project, cb) { return opener(url(json), { command: npm.config.get("browser") }, cb) } - var uri = url_.resolve(npm.config.get("registry"), project + "/latest") - registry.get(uri, { timeout : 3600 }, function (er, json) { + mapToRegistry(project, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri + "/latest", { timeout : 3600 }, next) + }) + + function next (er, json) { var github = "https://github.com/" + project + "#readme" if (er) { @@ -64,5 +72,5 @@ function getDoc (project, cb) { } return opener(url(json), { command: npm.config.get("browser") }, cb) - }) + } } diff --git a/deps/npm/lib/install.js b/deps/npm/lib/install.js index 9d2c2cfa2..82d872525 100644 --- a/deps/npm/lib/install.js +++ b/deps/npm/lib/install.js @@ -34,28 +34,34 @@ install.completion = function (opts, cb) { // if it starts with https?://, then just give up, because it's a url // for now, not yet implemented. var registry = npm.registry - , uri = url.resolve(npm.config.get("registry"), "-/short") - registry.get(uri, null, function (er, pkgs) { - if (er) return cb() - if (!opts.partialWord) return cb(null, pkgs) + mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) - var name = opts.partialWord.split("@").shift() - pkgs = pkgs.filter(function (p) { - return p.indexOf(name) === 0 - }) + registry.get(uri, null, function (er, pkgs) { + if (er) return cb() + if (!opts.partialWord) return cb(null, pkgs) - if (pkgs.length !== 1 && opts.partialWord === name) { - return cb(null, pkgs) - } + var name = npa(opts.partialWord).name + pkgs = pkgs.filter(function (p) { + return p.indexOf(name) === 0 + }) - uri = url.resolve(npm.config.get("registry"), pkgs[0]) - registry.get(uri, null, function (er, d) { - if (er) return cb() - return cb(null, Object.keys(d["dist-tags"] || {}) - .concat(Object.keys(d.versions || {})) - .map(function (t) { - return pkgs[0] + "@" + t - })) + if (pkgs.length !== 1 && opts.partialWord === name) { + return cb(null, pkgs) + } + + mapToRegistry(pkgs[0], npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, d) { + if (er) return cb() + return cb(null, Object.keys(d["dist-tags"] || {}) + .concat(Object.keys(d.versions || {})) + .map(function (t) { + return pkgs[0] + "@" + t + })) + }) + }) }) }) } @@ -74,9 +80,10 @@ var npm = require("./npm.js") , mkdir = require("mkdirp") , lifecycle = require("./utils/lifecycle.js") , archy = require("archy") - , isGitUrl = require("./utils/is-git-url.js") , npmInstallChecks = require("npm-install-checks") , sortedObject = require("sorted-object") + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") function install (args, cb_) { var hasArguments = !!args.length @@ -112,7 +119,7 @@ function install (args, cb_) { where = args args = [].concat(cb_) // pass in [] to do default dep-install cb_ = arguments[2] - log.verbose("install", "where,what", [where, args]) + log.verbose("install", "where, what", [where, args]) } if (!npm.config.get("global")) { @@ -206,7 +213,7 @@ function findPeerInvalid_ (packageMap, fpiList) { var pkg = packageMap[packageName] if (pkg.peerInvalid) { - var peersDepending = {}; + var peersDepending = {} for (var peerName in packageMap) { var peer = packageMap[peerName] if (peer.peerDependencies && peer.peerDependencies[packageName]) { @@ -338,21 +345,33 @@ function save (where, installed, tree, pretty, hasArguments, cb) { return cb(null, installed, tree, pretty) } - var saveBundle = npm.config.get('save-bundle') - var savePrefix = npm.config.get('save-prefix') || "^"; + var saveBundle = npm.config.get("save-bundle") + var savePrefix = npm.config.get("save-prefix") || "^" // each item in the tree is a top-level thing that should be saved // to the package.json file. // The relevant tree shape is { <folder>: {what:<pkg>} } var saveTarget = path.resolve(where, "package.json") - , things = Object.keys(tree).map(function (k) { - // if "what" was a url, then save that instead. - var t = tree[k] - , u = url.parse(t.from) - , w = t.what.split("@") - if (u && u.protocol) w[1] = t.from - return w - }).reduce(function (set, k) { + + asyncMap(Object.keys(tree), function (k, cb) { + // if "what" was a url, then save that instead. + var t = tree[k] + , u = url.parse(t.from) + , a = npa(t.what) + , w = [a.name, a.spec] + + + fs.stat(t.from, function (er){ + if (!er) { + w[1] = "file:" + t.from + } else if (u && u.protocol) { + w[1] = t.from + } + cb(null, [w]) + }) + } + , function (er, arr) { + var things = arr.reduce(function (set, k) { var rangeDescriptor = semver.valid(k[1], true) && semver.gte(k[1], "0.1.0", true) && !npm.config.get("save-exact") @@ -361,47 +380,49 @@ function save (where, installed, tree, pretty, hasArguments, cb) { return set }, {}) - // don't use readJson, because we don't want to do all the other - // tricky npm-specific stuff that's in there. - fs.readFile(saveTarget, function (er, data) { - // ignore errors here, just don't save it. - try { - data = JSON.parse(data.toString("utf8")) - } catch (ex) { - er = ex - } - if (er) { - return cb(null, installed, tree, pretty) - } + // don't use readJson, because we don't want to do all the other + // tricky npm-specific stuff that's in there. + fs.readFile(saveTarget, function (er, data) { + // ignore errors here, just don't save it. + try { + data = JSON.parse(data.toString("utf8")) + } catch (ex) { + er = ex + } - var deps = npm.config.get("save-optional") ? "optionalDependencies" - : npm.config.get("save-dev") ? "devDependencies" - : "dependencies" + if (er) { + return cb(null, installed, tree, pretty) + } - if (saveBundle) { - var bundle = data.bundleDependencies || data.bundledDependencies - delete data.bundledDependencies - if (!Array.isArray(bundle)) bundle = [] - data.bundleDependencies = bundle.sort() - } + var deps = npm.config.get("save-optional") ? "optionalDependencies" + : npm.config.get("save-dev") ? "devDependencies" + : "dependencies" - log.verbose('saving', things) - data[deps] = data[deps] || {} - Object.keys(things).forEach(function (t) { - data[deps][t] = things[t] if (saveBundle) { - var i = bundle.indexOf(t) - if (i === -1) bundle.push(t) + var bundle = data.bundleDependencies || data.bundledDependencies + delete data.bundledDependencies + if (!Array.isArray(bundle)) bundle = [] data.bundleDependencies = bundle.sort() } - }) - data[deps] = sortedObject(data[deps]) + log.verbose("saving", things) + data[deps] = data[deps] || {} + Object.keys(things).forEach(function (t) { + data[deps][t] = things[t] + if (saveBundle) { + var i = bundle.indexOf(t) + if (i === -1) bundle.push(t) + data.bundleDependencies = bundle.sort() + } + }) - data = JSON.stringify(data, null, 2) + "\n" - fs.writeFile(saveTarget, data, function (er) { - cb(er, installed, tree, pretty) + data[deps] = sortedObject(data[deps]) + + data = JSON.stringify(data, null, 2) + "\n" + fs.writeFile(saveTarget, data, function (er) { + cb(er, installed, tree, pretty) + }) }) }) } @@ -412,22 +433,22 @@ function save (where, installed, tree, pretty, hasArguments, cb) { // that the submodules are not immediately require()able. // TODO: Show the complete tree, ls-style, but only if --long is provided function prettify (tree, installed) { - if (npm.config.get("json")) { - function red (set, kv) { - set[kv[0]] = kv[1] - return set - } + function red (set, kv) { + set[kv[0]] = kv[1] + return set + } + if (npm.config.get("json")) { tree = Object.keys(tree).map(function (p) { if (!tree[p]) return null - var what = tree[p].what.split("@") - , name = what.shift() - , version = what.join("@") + var what = npa(tree[p].what) + , name = what.name + , version = what.spec , o = { name: name, version: version, from: tree[p].from } o.dependencies = tree[p].children.map(function P (dep) { - var what = dep.what.split("@") - , name = what.shift() - , version = what.join("@") + var what = npa(dep.what) + , name = what.name + , version = what.spec , o = { version: version, from: dep.from } o.dependencies = dep.children.map(P).reduce(red, {}) return [name, o] @@ -615,60 +636,70 @@ function installMany (what, where, context, cb) { } function targetResolver (where, context, deps) { - var alreadyInstalledManually = context.explicit ? [] : null + var alreadyInstalledManually = [] + , resolveLeft = 0 , nm = path.resolve(where, "node_modules") , parent = context.parent , wrap = context.wrap - if (!context.explicit) fs.readdir(nm, function (er, inst) { - if (er) return alreadyInstalledManually = [] + if (!context.explicit) readdir(nm) - // don't even mess with non-package looking things - inst = inst.filter(function (p) { - return !p.match(/^[\._-]/) - }) + function readdir(name) { + resolveLeft++ + fs.readdir(name, function (er, inst) { + if (er) return resolveLeft-- - asyncMap(inst, function (pkg, cb) { - readJson(path.resolve(nm, pkg, "package.json"), log.warn, function (er, d) { - if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) - // error means it's not a package, most likely. - if (er) return cb(null, []) - - // if it's a bundled dep, then assume that anything there is valid. - // otherwise, make sure that it's a semver match with what we want. - var bd = parent.bundleDependencies - if (bd && bd.indexOf(d.name) !== -1 || - semver.satisfies(d.version, deps[d.name] || "*", true) || - deps[d.name] === d._resolved) { - return cb(null, d.name) - } + // don't even mess with non-package looking things + inst = inst.filter(function (p) { + if (!p.match(/^[@\._-]/)) return true + // scope pacakges + var scopepath = path.join(name, p) + readdir(scopepath) + }) - // see if the package had been previously linked - fs.lstat(path.resolve(nm, pkg), function(err, s) { - if (err) return cb(null, []) - if (s.isSymbolicLink()) { + asyncMap(inst, function (pkg, cb) { + readJson(path.resolve(name, pkg, "package.json"), log.warn, function (er, d) { + if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) + // error means it's not a package, most likely. + if (er) return cb(null, []) + + // if it's a bundled dep, then assume that anything there is valid. + // otherwise, make sure that it's a semver match with what we want. + var bd = parent.bundleDependencies + if (bd && bd.indexOf(d.name) !== -1 || + semver.satisfies(d.version, deps[d.name] || "*", true) || + deps[d.name] === d._resolved) { return cb(null, d.name) } - // something is there, but it's not satisfactory. Clobber it. - return cb(null, []) + // see if the package had been previously linked + fs.lstat(path.resolve(nm, pkg), function(err, s) { + if (err) return cb(null, []) + if (s.isSymbolicLink()) { + return cb(null, d.name) + } + + // something is there, but it's not satisfactory. Clobber it. + return cb(null, []) + }) }) + }, function (er, inst) { + // this is the list of things that are valid and should be ignored. + alreadyInstalledManually = alreadyInstalledManually.concat(inst) + resolveLeft-- }) - }, function (er, inst) { - // this is the list of things that are valid and should be ignored. - alreadyInstalledManually = inst }) - }) + } var to = 0 return function resolver (what, cb) { - if (!alreadyInstalledManually) return setTimeout(function () { + if (resolveLeft) return setTimeout(function () { resolver(what, cb) }, to++) // now we know what's been installed here manually, // or tampered with in some way that npm doesn't want to overwrite. - if (alreadyInstalledManually.indexOf(what.split("@").shift()) !== -1) { + if (alreadyInstalledManually.indexOf(npa(what).name) !== -1) { log.verbose("already installed", "skipping %s %s", what, where) return cb(null, []) } @@ -692,7 +723,7 @@ function targetResolver (where, context, deps) { } if (wrap) { - var name = what.split(/@/).shift() + var name = npa(what).name if (wrap[name]) { var wrapTarget = readWrap(wrap[name]) what = name + "@" + wrapTarget @@ -709,19 +740,16 @@ function targetResolver (where, context, deps) { // already has a matching copy. // If it's not a git repo, and the parent already has that pkg, then // we can skip installing it again. - cache.add(what, null, false, function (er, data) { + var pkgroot = path.resolve(npm.prefix, (parent && parent._from) || "") + cache.add(what, null, pkgroot, false, function (er, data) { if (er && parent && parent.optionalDependencies && - parent.optionalDependencies.hasOwnProperty(what.split("@")[0])) { + parent.optionalDependencies.hasOwnProperty(npa(what).name)) { log.warn("optional dep failed, continuing", what) log.verbose("optional dep failed, continuing", [what, er]) return cb(null, []) } - var isGit = false - , maybeGit = what.split("@").slice(1).join() - - if (maybeGit) - isGit = isGitUrl(url.parse(maybeGit)) + var isGit = npa(what).type === "git" if (!er && data && @@ -733,6 +761,7 @@ function targetResolver (where, context, deps) { return cb(null, []) } + if (data && !data._from) data._from = what if (er && parent && parent.name) er.parent = parent.name return cb(er, data || []) @@ -771,6 +800,13 @@ function localLink (target, where, context, cb) { , parent = context.parent readJson(jsonFile, log.warn, function (er, data) { + function thenLink () { + npm.commands.link([target.name], function (er, d) { + log.silly("localLink", "back from link", [er, d]) + cb(er, [resultList(target, where, parent && parent._id)]) + }) + } + if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) if (er || data._id === target._id) { if (er) { @@ -781,14 +817,6 @@ function localLink (target, where, context, cb) { thenLink() }) } else thenLink() - - function thenLink () { - npm.commands.link([target.name], function (er, d) { - log.silly("localLink", "back from link", [er, d]) - cb(er, [resultList(target, where, parent && parent._id)]) - }) - } - } else { log.verbose("localLink", "install locally (no link)", target._id) installOne_(target, where, context, cb) diff --git a/deps/npm/lib/link.js b/deps/npm/lib/link.js index 8022fc78d..8c6a93029 100644 --- a/deps/npm/lib/link.js +++ b/deps/npm/lib/link.js @@ -10,6 +10,7 @@ var npm = require("./npm.js") , path = require("path") , rm = require("./utils/gently-rm.js") , build = require("./build.js") + , npa = require("npm-package-arg") module.exports = link @@ -49,25 +50,26 @@ function link (args, cb) { function linkInstall (pkgs, cb) { asyncMap(pkgs, function (pkg, cb) { + var t = path.resolve(npm.globalDir, "..") + , pp = path.resolve(npm.globalDir, pkg) + , rp = null + , target = path.resolve(npm.dir, pkg) + function n (er, data) { if (er) return cb(er, data) // install returns [ [folder, pkgId], ... ] // but we definitely installed just one thing. var d = data.filter(function (d) { return !d[3] }) + var what = npa(d[0][0]) pp = d[0][1] - pkg = path.basename(pp) + pkg = what.name target = path.resolve(npm.dir, pkg) next() } - var t = path.resolve(npm.globalDir, "..") - , pp = path.resolve(npm.globalDir, pkg) - , rp = null - , target = path.resolve(npm.dir, pkg) - - // if it's a folder or a random not-installed thing, then - // link or install it first - if (pkg.indexOf("/") !== -1 || pkg.indexOf("\\") !== -1) { + // if it's a folder, a random not-installed thing, or not a scoped package, + // then link or install it first + if (pkg[0] !== "@" && (pkg.indexOf("/") !== -1 || pkg.indexOf("\\") !== -1)) { return fs.lstat(path.resolve(pkg), function (er, st) { if (er || !st.isDirectory()) { npm.commands.install(t, pkg, n) diff --git a/deps/npm/lib/ls.js b/deps/npm/lib/ls.js index 781b6443b..ed329d19e 100644 --- a/deps/npm/lib/ls.js +++ b/deps/npm/lib/ls.js @@ -14,8 +14,8 @@ var npm = require("./npm.js") , archy = require("archy") , semver = require("semver") , url = require("url") - , isGitUrl = require("./utils/is-git-url.js") , color = require("ansicolors") + , npa = require("npm-package-arg") ls.usage = "npm ls" @@ -29,9 +29,9 @@ function ls (args, silent, cb) { // npm ls 'foo@~1.3' bar 'baz@<2' if (!args) args = [] else args = args.map(function (a) { - var nv = a.split("@") - , name = nv.shift() - , ver = semver.validRange(nv.join("@")) || "" + var p = npa(a) + , name = p.name + , ver = semver.validRange(p.rawSpec) || "" return [ name, ver ] }) @@ -39,6 +39,7 @@ function ls (args, silent, cb) { var depth = npm.config.get("depth") var opt = { depth: depth, log: log.warn, dev: true } readInstalled(dir, opt, function (er, data) { + pruneNestedExtraneous(data) var bfs = bfsify(data, args) , lite = getLite(bfs) @@ -75,6 +76,18 @@ function ls (args, silent, cb) { }) } +function pruneNestedExtraneous (data, visited) { + visited = visited || [] + visited.push(data) + for (var i in data.dependencies) { + if (data.dependencies[i].extraneous) { + data.dependencies[i].dependencies = {} + } else if (visited.indexOf(data.dependencies[i]) === -1) { + pruneNestedExtraneous(data.dependencies[i], visited) + } + } +} + function alphasort (a, b) { a = a.toLowerCase() b = b.toLowerCase() @@ -265,7 +278,7 @@ function makeArchy_ (data, long, dir, depth, parent, d) { // add giturl to name@version if (data._resolved) { - if (isGitUrl(url.parse(data._resolved))) + if (npa(data._resolved).type === "git") out.label += " (" + data._resolved + ")" } diff --git a/deps/npm/lib/npm.js b/deps/npm/lib/npm.js index 3139b1d14..c811be5a9 100644 --- a/deps/npm/lib/npm.js +++ b/deps/npm/lib/npm.js @@ -46,16 +46,6 @@ try { var j = JSON.parse(fs.readFileSync( path.join(__dirname, "../package.json"))+"") npm.version = j.version - npm.nodeVersionRequired = j.engines.node - if (!semver.satisfies(pv, j.engines.node)) { - log.warn("unsupported version", ["" - ,"npm requires node version: "+j.engines.node - ,"And you have: "+pv - ,"which is not satisfactory." - ,"" - ,"Bad things will likely happen. You have been warned." - ,""].join("\n")) - } } catch (ex) { try { log.info("error reading version", ex) @@ -153,6 +143,7 @@ var commandCache = {} ] , plumbing = [ "build" , "unbuild" + , "isntall" , "xmas" , "substack" , "visnup" diff --git a/deps/npm/lib/outdated.js b/deps/npm/lib/outdated.js index a71df7fe7..fdfd7624d 100644 --- a/deps/npm/lib/outdated.js +++ b/deps/npm/lib/outdated.js @@ -28,12 +28,13 @@ var path = require("path") , 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") , semver = require("semver") , os = require("os") + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") function outdated (args, silent, cb) { if (typeof cb !== "function") cb = silent, silent = false @@ -43,7 +44,7 @@ function outdated (args, silent, cb) { if (npm.config.get("json")) { console.log(makeJSON(list)) } else if (npm.config.get("parseable")) { - console.log(makeParseable(list)); + console.log(makeParseable(list)) } else { var outList = list.map(makePretty) var outTable = [[ "Package" @@ -99,7 +100,7 @@ function makePretty (p) { function ansiTrim (str) { var r = new RegExp("\x1b(?:\\[(?:\\d+[ABCDEFGJKSTm]|\\d+;\\d+[Hfm]|" + - "\\d+;\\d+;\\d+m|6n|s|u|\\?25[lh])|\\w)", "g"); + "\\d+;\\d+;\\d+m|6n|s|u|\\?25[lh])|\\w)", "g") return str.replace(r, "") } @@ -114,7 +115,7 @@ function makeParseable (list) { , dir = path.resolve(p[0], "node_modules", dep) , has = p[2] , want = p[3] - , latest = p[4]; + , latest = p[4] return [ dir , dep + "@" + want @@ -264,20 +265,25 @@ function shouldUpdate (args, dir, dep, has, req, depth, cb) { return skip() } - if (isGitUrl(url.parse(req))) + if (npa(req).type === "git") return doIt("git", "git") // search for the latest package - var uri = url.resolve(npm.config.get("registry"), dep) - npm.registry.get(uri, null, function (er, d) { + mapToRegistry(dep, npm.config, function (er, uri) { + if (er) return cb(er) + + npm.registry.get(uri, null, updateDeps) + }) + + function updateDeps (er, d) { if (er) return cb() - if (!d || !d['dist-tags'] || !d.versions) return cb() - var l = d.versions[d['dist-tags'].latest] + if (!d || !d["dist-tags"] || !d.versions) return cb() + var l = d.versions[d["dist-tags"].latest] if (!l) return cb() var r = req - if (d['dist-tags'][req]) - r = d['dist-tags'][req] + if (d["dist-tags"][req]) + r = d["dist-tags"][req] if (semver.validRange(r, true)) { // some kind of semver range. @@ -290,13 +296,13 @@ function shouldUpdate (args, dir, dep, has, req, depth, cb) { } // We didn't find the version in the doc. See if cache can find it. - cache.add(dep, req, false, onCacheAdd) + cache.add(dep, req, null, false, onCacheAdd) function onCacheAdd(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) { - if (er.code && er.code === 'ETARGET') { + if (er.code && er.code === "ETARGET") { // no viable version found return skip(er) } @@ -315,6 +321,5 @@ function shouldUpdate (args, dir, dep, has, req, depth, cb) { else skip() } - - }) + } } diff --git a/deps/npm/lib/owner.js b/deps/npm/lib/owner.js index 34dbbc247..2fdee7adb 100644 --- a/deps/npm/lib/owner.js +++ b/deps/npm/lib/owner.js @@ -5,6 +5,12 @@ owner.usage = "npm owner add <username> <pkg>" + "\nnpm owner rm <username> <pkg>" + "\nnpm owner ls <pkg>" +var npm = require("./npm.js") + , registry = npm.registry + , log = require("npmlog") + , readJson = require("read-package-json") + , mapToRegistry = require("./utils/map-to-registry.js") + owner.completion = function (opts, cb) { var argv = opts.conf.argv.remain if (argv.length > 4) return cb() @@ -14,65 +20,78 @@ owner.completion = function (opts, cb) { else subs.push("ls", "list") return cb(null, subs) } - var un = encodeURIComponent(npm.config.get("username")) - var theUser, uri - switch (argv[2]) { - case "ls": - if (argv.length > 3) return cb() - uri = url.resolve(npm.config.get("registry"), "-/short") - return registry.get(uri, null, cb) - - case "rm": - if (argv.length > 3) { - theUser = encodeURIComponent(argv[3]) - uri = url.resolve(npm.config.get("registry"), "-/by-user/"+theUser+"|"+un) - console.error(uri) - return registry.get(uri, null, function (er, d) { + + npm.commands.whoami([], true, function (er, username) { + if (er) return cb() + + var un = encodeURIComponent(username) + var byUser, theUser + switch (argv[2]) { + case "ls": + if (argv.length > 3) return cb() + return mapToRegistry("-/short", npm.config, function (er, uri) { if (er) return cb(er) - // return the intersection - return cb(null, d[theUser].filter(function (p) { - // kludge for server adminery. - return un === "isaacs" || d[un].indexOf(p) === -1 - })) + + registry.get(uri, null, cb) }) - } - // else fallthrough - case "add": - if (argv.length > 3) { - theUser = encodeURIComponent(argv[3]) - uri = url.resolve(npm.config.get("registry"), "-/by-user/"+theUser+"|"+un) - console.error(uri) - return registry.get(uri, null, function (er, d) { - console.error(uri, er || d) - // return mine that they're not already on. + + case "rm": + if (argv.length > 3) { + theUser = encodeURIComponent(argv[3]) + byUser = "-/by-user/" + theUser + "|" + un + return mapToRegistry(byUser, npm.config, function (er, uri) { + if (er) return cb(er) + + console.error(uri) + registry.get(uri, null, function (er, d) { + if (er) return cb(er) + // return the intersection + return cb(null, d[theUser].filter(function (p) { + // kludge for server adminery. + return un === "isaacs" || d[un].indexOf(p) === -1 + })) + }) + }) + } + // else fallthrough + case "add": + if (argv.length > 3) { + theUser = encodeURIComponent(argv[3]) + byUser = "-/by-user/" + theUser + "|" + un + return mapToRegistry(byUser, npm.config, function (er, uri) { + if (er) return cb(er) + + console.error(uri) + registry.get(uri, null, function (er, d) { + console.error(uri, er || d) + // return mine that they're not already on. + if (er) return cb(er) + var mine = d[un] || [] + , theirs = d[theUser] || [] + return cb(null, mine.filter(function (p) { + return theirs.indexOf(p) === -1 + })) + }) + }) + } + // just list all users who aren't me. + return mapToRegistry("-/users", npm.config, function (er, uri) { if (er) return cb(er) - var mine = d[un] || [] - , theirs = d[theUser] || [] - return cb(null, mine.filter(function (p) { - return theirs.indexOf(p) === -1 - })) + + registry.get(uri, null, function (er, list) { + if (er) return cb() + return cb(null, Object.keys(list).filter(function (n) { + return n !== un + })) + }) }) - } - // just list all users who aren't me. - uri = url.resolve(npm.config.get("registry"), "-/users") - return registry.get(uri, null, function (er, list) { - if (er) return cb() - return cb(null, Object.keys(list).filter(function (n) { - return n !== un - })) - }) - default: - return cb() - } + default: + return cb() + } + }) } -var npm = require("./npm.js") - , registry = npm.registry - , log = require("npmlog") - , readJson = require("read-package-json") - , url = require("url") - function owner (args, cb) { var action = args.shift() switch (action) { @@ -90,18 +109,23 @@ function ls (pkg, cb) { ls(pkg, cb) }) - var uri = url.resolve(npm.config.get("registry"), pkg) - registry.get(uri, null, function (er, data) { - var msg = "" - if (er) { - log.error("owner ls", "Couldn't get owner data", pkg) - return cb(er) - } - var owners = data.maintainers - if (!owners || !owners.length) msg = "admin party!" - else msg = owners.map(function (o) { return o.name +" <"+o.email+">" }).join("\n") - console.log(msg) - cb(er, owners) + mapToRegistry(pkg, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, data) { + var msg = "" + if (er) { + log.error("owner ls", "Couldn't get owner data", pkg) + return cb(er) + } + var owners = data.maintainers + if (!owners || !owners.length) msg = "admin party!" + else msg = owners.map(function (o) { + return o.name + " <" + o.email + ">" + }).join("\n") + console.log(msg) + cb(er, owners) + }) }) } @@ -120,7 +144,7 @@ function add (user, pkg, cb) { var o = owners[i] if (o.name === u.name) { log.info( "owner add" - , "Already a package owner: "+o.name+" <"+o.email+">") + , "Already a package owner: " + o.name + " <" + o.email + ">") return false } } @@ -145,7 +169,7 @@ function rm (user, pkg, cb) { return !match }) if (!found) { - log.info("owner rm", "Not a package owner: "+user) + log.info("owner rm", "Not a package owner: " + user) return false } if (!m.length) return new Error( @@ -156,15 +180,19 @@ function rm (user, pkg, cb) { function mutate (pkg, user, mutation, cb) { if (user) { - var uri = url.resolve(npm.config.get("registry"), "-/user/org.couchdb.user:"+user) - registry.get(uri, null, mutate_) + var byUser = "-/user/org.couchdb.user:" + user + mapToRegistry(byUser, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, mutate_) + }) } else { mutate_(null, null) } function mutate_ (er, u) { if (!er && user && (!u || u.error)) er = new Error( - "Couldn't get user data for "+user+": "+JSON.stringify(u)) + "Couldn't get user data for " + user + ": " + JSON.stringify(u)) if (er) { log.error("owner mutate", "Error getting user data for %s", user) @@ -172,27 +200,34 @@ function mutate (pkg, user, mutation, cb) { } if (u) u = { "name" : u.name, "email" : u.email } - var uri = url.resolve(npm.config.get("registry"), pkg) - registry.get(uri, null, function (er, data) { - if (er) { - log.error("owner mutate", "Error getting package data for %s", pkg) - return cb(er) - } - var m = mutation(u, data.maintainers) - if (!m) return cb() // handled - if (m instanceof Error) return cb(m) // error - data = { _id : data._id - , _rev : data._rev - , maintainers : m - } - var uri = url.resolve(npm.config.get("registry"), pkg+"/-rev/"+data._rev) - registry.request("PUT", uri, { body : data }, function (er, data) { - if (!er && data.error) er = new Error( - "Failed to update package metadata: "+JSON.stringify(data)) + mapToRegistry(pkg, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, data) { if (er) { - log.error("owner mutate", "Failed to update package metadata") + log.error("owner mutate", "Error getting package data for %s", pkg) + return cb(er) } - cb(er, data) + var m = mutation(u, data.maintainers) + if (!m) return cb() // handled + if (m instanceof Error) return cb(m) // error + data = { _id : data._id + , _rev : data._rev + , maintainers : m + } + var dataPath = pkg + "/-rev/" + data._rev + mapToRegistry(dataPath, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.request("PUT", uri, { body : data }, function (er, data) { + if (!er && data.error) er = new Error( + "Failed to update package metadata: " + JSON.stringify(data)) + if (er) { + log.error("owner mutate", "Failed to update package metadata") + } + cb(er, data) + }) + }) }) }) } @@ -207,5 +242,5 @@ function readLocalPkg (cb) { } function unknown (action, cb) { - cb("Usage: \n"+owner.usage) + cb("Usage: \n" + owner.usage) } diff --git a/deps/npm/lib/pack.js b/deps/npm/lib/pack.js index ea94dd154..f955cb71a 100644 --- a/deps/npm/lib/pack.js +++ b/deps/npm/lib/pack.js @@ -40,9 +40,14 @@ function printFiles (files, cb) { // add to cache, then cp to the cwd function pack_ (pkg, cb) { - cache.add(pkg, null, false, function (er, data) { + cache.add(pkg, null, null, false, function (er, data) { if (er) return cb(er) - var fname = path.resolve(data._id.replace(/@/g, "-") + ".tgz") + + var name = data.name + // scoped packages get special treatment + if (name[0] === "@") name = name.substr(1).replace(/\//g, "-") + + var fname = name + "-" + data.version + ".tgz" , cached = path.resolve( npm.cache , data.name , data.version diff --git a/deps/npm/lib/publish.js b/deps/npm/lib/publish.js index ccad3ea82..701dc2b92 100644 --- a/deps/npm/lib/publish.js +++ b/deps/npm/lib/publish.js @@ -1,7 +1,8 @@ module.exports = publish -var npm = require("./npm.js") +var url = require("url") + , npm = require("./npm.js") , log = require("npmlog") , path = require("path") , readJson = require("read-package-json") @@ -9,6 +10,7 @@ var npm = require("./npm.js") , chain = require("slide").chain , Conf = require("npmconf").Conf , RegClient = require("npm-registry-client") + , mapToRegistry = require("./utils/map-to-registry.js") publish.usage = "npm publish <tarball>" + "\nnpm publish <folder>" @@ -22,7 +24,10 @@ publish.completion = function (opts, cb) { } function publish (args, isRetry, cb) { - if (typeof cb !== "function") cb = isRetry, isRetry = false + if (typeof cb !== "function") { + cb = isRetry + isRetry = false + } if (args.length === 0) args = ["."] if (args.length !== 1) return cb(publish.usage) @@ -47,15 +52,15 @@ function publish (args, isRetry, cb) { // That means that we can run publish/postpublish in the dir, rather than // in the cache dir. function cacheAddPublish (dir, didPre, isRetry, cb) { - npm.commands.cache.add(dir, null, false, function (er, data) { + npm.commands.cache.add(dir, null, null, false, function (er, data) { if (er) return cb(er) log.silly("publish", data) var cachedir = path.resolve( npm.cache , data.name , data.version , "package" ) - chain - ( [ !didPre && [lifecycle, data, "prepublish", cachedir] + chain([ !didPre && + [lifecycle, data, "prepublish", cachedir] , [publish_, dir, data, isRetry, cachedir] , [lifecycle, data, "publish", didPre ? dir : cachedir] , [lifecycle, data, "postpublish", didPre ? dir : cachedir] ] @@ -66,48 +71,61 @@ function cacheAddPublish (dir, didPre, isRetry, cb) { function publish_ (arg, data, isRetry, cachedir, cb) { if (!data) return cb(new Error("no package.json file found")) - // check for publishConfig hash var registry = npm.registry - var registryURI = npm.config.get("registry") + var config = npm.config + + // check for publishConfig hash if (data.publishConfig) { - var pubConf = new Conf(npm.config) - pubConf.save = npm.config.save.bind(npm.config) + config = new Conf(npm.config) + config.save = npm.config.save.bind(npm.config) // don't modify the actual publishConfig object, in case we have // to set a login token or some other data. - pubConf.unshift(Object.keys(data.publishConfig).reduce(function (s, k) { + config.unshift(Object.keys(data.publishConfig).reduce(function (s, k) { s[k] = data.publishConfig[k] return s }, {})) - registry = new RegClient(pubConf) - registryURI = pubConf.get("registry") + registry = new RegClient(config) } data._npmVersion = npm.version - data._npmUser = { name: npm.config.get("username") - , email: npm.config.get("email") } delete data.modules - if (data.private) return cb(new Error - ("This package has been marked as private\n" - +"Remove the 'private' field from the package.json to publish it.")) - - var tarball = cachedir + ".tgz" - registry.publish(registryURI, data, tarball, function (er) { - if (er && er.code === "EPUBLISHCONFLICT" - && npm.config.get("force") && !isRetry) { - log.warn("publish", "Forced publish over "+data._id) - return npm.commands.unpublish([data._id], function (er) { - // ignore errors. Use the force. Reach out with your feelings. - // but if it fails again, then report the first error. - publish([arg], er || true, cb) - }) - } - // report the unpublish error if this was a retry and unpublish failed - if (er && isRetry && isRetry !== true) return cb(isRetry) + if (data.private) return cb( + new Error( + "This package has been marked as private\n" + + "Remove the 'private' field from the package.json to publish it." + ) + ) + + mapToRegistry(data.name, config, function (er, registryURI) { if (er) return cb(er) - console.log("+ " + data._id) - cb() + + var tarball = cachedir + ".tgz" + + // we just want the base registry URL in this case + var registryBase = url.resolve(registryURI, ".") + log.verbose("publish", "registryBase", registryBase) + + var c = config.getCredentialsByURI(registryBase) + data._npmUser = {name: c.username, email: c.email} + + registry.publish(registryBase, data, tarball, function (er) { + if (er && er.code === "EPUBLISHCONFLICT" + && npm.config.get("force") && !isRetry) { + log.warn("publish", "Forced publish over " + data._id) + return npm.commands.unpublish([data._id], function (er) { + // ignore errors. Use the force. Reach out with your feelings. + // but if it fails again, then report the first error. + publish([arg], er || true, cb) + }) + } + // report the unpublish error if this was a retry and unpublish failed + if (er && isRetry && isRetry !== true) return cb(isRetry) + if (er) return cb(er) + console.log("+ " + data._id) + cb() + }) }) } diff --git a/deps/npm/lib/rebuild.js b/deps/npm/lib/rebuild.js index e296451b7..ab372c6ec 100644 --- a/deps/npm/lib/rebuild.js +++ b/deps/npm/lib/rebuild.js @@ -5,6 +5,7 @@ var readInstalled = require("read-installed") , semver = require("semver") , log = require("npmlog") , npm = require("./npm.js") + , npa = require("npm-package-arg") rebuild.usage = "npm rebuild [<name>[@<version>] [name[@<version>] ...]]" @@ -46,9 +47,9 @@ function filter (data, args, set, seen) { else if (data.name && data._id) { for (var i = 0, l = args.length; i < l; i ++) { var arg = args[i] - , nv = arg.split("@") - , n = nv.shift() - , v = nv.join("@") + , nv = npa(arg) + , n = nv.name + , v = nv.rawSpec if (n !== data.name) continue if (!semver.satisfies(data.version, v, true)) continue pass = true diff --git a/deps/npm/lib/repo.js b/deps/npm/lib/repo.js index d209c3ca8..c6db8e37b 100644 --- a/deps/npm/lib/repo.js +++ b/deps/npm/lib/repo.js @@ -5,9 +5,12 @@ repo.usage = "npm repo <pkgname>" repo.completion = function (opts, cb) { if (opts.conf.argv.remain.length > 2) return cb() - var uri = url_.resolve(npm.config.get("registry"), "/-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("/-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } @@ -19,10 +22,12 @@ var npm = require("./npm.js") , path = require("path") , readJson = require("read-package-json") , fs = require("fs") - , url_ = require('url') + , url_ = require("url") + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") function repo (args, cb) { - var n = args.length && args[0].split("@").shift() || '.' + var n = args.length && npa(args[0]).name || "." fs.stat(n, function (er, s) { if (er && er.code === "ENOENT") return callRegistry(n, cb) else if (er) return cb(er) @@ -35,8 +40,8 @@ function repo (args, cb) { } function getUrlAndOpen (d, cb) { - var r = d.repository; - if (!r) return cb(new Error('no repository')); + var r = d.repository + if (!r) return cb(new Error('no repository')) // XXX remove this when npm@v1.3.10 from node 0.10 is deprecated // from https://github.com/npm/npm-www/issues/418 if (githubUserRepo(r.url)) @@ -52,10 +57,13 @@ function getUrlAndOpen (d, cb) { } function callRegistry (n, cb) { - var uri = url_.resolve(npm.config.get("registry"), n + "/latest") - registry.get(uri, { timeout : 3600 }, function (er, d) { + mapToRegistry(n, npm.config, function (er, uri) { if (er) return cb(er) - getUrlAndOpen(d, cb) + + registry.get(uri + "/latest", { timeout : 3600 }, function (er, d) { + if (er) return cb(er) + getUrlAndOpen(d, cb) + }) }) } diff --git a/deps/npm/lib/run-script.js b/deps/npm/lib/run-script.js index 25e98f01d..6cb7bf7fb 100644 --- a/deps/npm/lib/run-script.js +++ b/deps/npm/lib/run-script.js @@ -8,7 +8,7 @@ var lifecycle = require("./utils/lifecycle.js") , log = require("npmlog") , chain = require("slide").chain -runScript.usage = "npm run-script [<pkg>] <command>" +runScript.usage = "npm run-script <command> [-- <args>]" runScript.completion = function (opts, cb) { @@ -21,7 +21,7 @@ runScript.completion = function (opts, cb) { if (argv.length === 3) { // either specified a script locally, in which case, done, // or a package, in which case, complete against its scripts - var json = path.join(npm.prefix, "package.json") + var json = path.join(npm.localPrefix, "package.json") return readJson(json, function (er, d) { if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) if (er) d = {} @@ -30,7 +30,7 @@ runScript.completion = function (opts, cb) { if (scripts.indexOf(argv[2]) !== -1) return cb() // ok, try to find out which package it was, then var pref = npm.config.get("global") ? npm.config.get("prefix") - : npm.prefix + : npm.localPrefix var pkgDir = path.resolve( pref, "node_modules" , argv[2], "package.json" ) readJson(pkgDir, function (er, d) { @@ -54,7 +54,7 @@ runScript.completion = function (opts, cb) { }) if (npm.config.get("global")) scripts = [], next() - else readJson(path.join(npm.prefix, "package.json"), function (er, d) { + else readJson(path.join(npm.localPrefix, "package.json"), function (er, d) { if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) d = d || {} scripts = Object.keys(d.scripts || {}) @@ -69,18 +69,18 @@ runScript.completion = function (opts, cb) { function runScript (args, cb) { if (!args.length) return list(cb) - var pkgdir = args.length === 1 ? process.cwd() - : path.resolve(npm.dir, args[0]) - , cmd = args.pop() + + var pkgdir = npm.localPrefix + , cmd = args.shift() readJson(path.resolve(pkgdir, "package.json"), function (er, d) { if (er) return cb(er) - run(d, pkgdir, cmd, cb) + run(d, pkgdir, cmd, args, cb) }) } function list(cb) { - var json = path.join(npm.prefix, 'package.json') + var json = path.join(npm.localPrefix, 'package.json') return readJson(json, function(er, d) { if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er) if (er) d = {} @@ -109,7 +109,7 @@ function list(cb) { }) } -function run (pkg, wd, cmd, cb) { +function run (pkg, wd, cmd, args, cb) { var cmds = [] if (!pkg.scripts) pkg.scripts = {} if (cmd === "restart") { @@ -124,7 +124,21 @@ function run (pkg, wd, cmd, cb) { } log.verbose("run-script", cmds) chain(cmds.map(function (c) { + // pass cli arguments after -- to script. + if (pkg.scripts[c]) pkg.scripts[c] = pkg.scripts[c] + joinArgs(args) + // when running scripts explicitly, assume that they're trusted. return [lifecycle, pkg, c, wd, true] }), cb) } + +// join arguments after '--' and pass them to script, +// handle special characters such as ', ", ' '. +function joinArgs (args) { + var joinedArgs = '' + args.forEach(function(arg, i) { + if (arg.match(/[ '"]/)) arg = '"' + arg.replace(/"/g, '\\"') + '"' + joinedArgs += ' ' + arg + }) + return joinedArgs +} diff --git a/deps/npm/lib/search.js b/deps/npm/lib/search.js index e7892350c..3be8b0d27 100644 --- a/deps/npm/lib/search.js +++ b/deps/npm/lib/search.js @@ -1,10 +1,10 @@ module.exports = exports = search -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , columnify = require('columnify') + , mapToRegistry = require("./utils/map-to-registry.js") search.usage = "npm search [some search terms ...]" @@ -63,10 +63,13 @@ function getFilteredData (staleness, args, notArgs, cb) { follow : true, staleOk : true } - var uri = url.resolve(npm.config.get("registry"), "-/all") - registry.get(uri, opts, function (er, data) { + mapToRegistry("-/all", npm.config, function (er, uri) { if (er) return cb(er) - return cb(null, filter(data, args, notArgs)) + + registry.get(uri, opts, function (er, data) { + if (er) return cb(er) + return cb(null, filter(data, args, notArgs)) + }) }) } diff --git a/deps/npm/lib/star.js b/deps/npm/lib/star.js index 9c0b4ea9e..123c4ebbb 100644 --- a/deps/npm/lib/star.js +++ b/deps/npm/lib/star.js @@ -1,19 +1,22 @@ module.exports = star -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") , asyncMap = require("slide").asyncMap + , mapToRegistry = require("./utils/map-to-registry.js") star.usage = "npm star <package> [pkg, pkg, ...]\n" + "npm unstar <package> [pkg, pkg, ...]" star.completion = function (opts, cb) { - var uri = url.resolve(npm.config.get("registry"), "-/short") - registry.get(uri, { timeout : 60000 }, function (er, list) { - return cb(null, list || []) + mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, { timeout : 60000 }, function (er, list) { + return cb(null, list || []) + }) }) } @@ -24,13 +27,16 @@ function star (args, cb) { , using = !(npm.command.match(/^un/)) if (!using) s = u asyncMap(args, function (pkg, cb) { - var uri = url.resolve(npm.config.get("registry"), pkg) - registry.star(uri, using, function (er, data, raw, req) { - if (!er) { - console.log(s + " "+pkg) - log.verbose("star", data) - } - cb(er, data, raw, req) + mapToRegistry(pkg, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.star(uri, using, function (er, data, raw, req) { + if (!er) { + console.log(s + " "+pkg) + log.verbose("star", data) + } + cb(er, data, raw, req) + }) }) }, cb) } diff --git a/deps/npm/lib/stars.js b/deps/npm/lib/stars.js index f0d2ef73a..dee5c152a 100644 --- a/deps/npm/lib/stars.js +++ b/deps/npm/lib/stars.js @@ -2,23 +2,26 @@ module.exports = stars stars.usage = "npm stars [username]" -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") + , mapToRegistry = require("./utils/map-to-registry.js") function stars (args, cb) { - var name = args.length === 1 ? args[0] : npm.config.get("username") - , uri = url.resolve(npm.config.get("registry"), name) - registry.stars(uri, showstars) + npm.commands.whoami([], true, function (er, username) { + var name = args.length === 1 ? args[0] : username + mapToRegistry("", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.stars(uri, name, showstars) + }) + }) function showstars (er, data) { - if (er) { - return cb(er) - } + if (er) return cb(er) if (data.rows.length === 0) { - log.warn('stars', 'user has not starred any packages.') + log.warn("stars", "user has not starred any packages.") } else { data.rows.forEach(function(a) { console.log(a.value) diff --git a/deps/npm/lib/submodule.js b/deps/npm/lib/submodule.js index 2231ced9c..eab4d21b0 100644 --- a/deps/npm/lib/submodule.js +++ b/deps/npm/lib/submodule.js @@ -9,7 +9,6 @@ var npm = require("./npm.js") , git = require("./utils/git.js") , asyncMap = require("slide").asyncMap , chain = require("slide").chain - , which = require("which") submodule.usage = "npm submodule <pkg>" @@ -23,7 +22,7 @@ function submodule (args, cb) { if (args.length === 0) return cb(submodule.usage) asyncMap(args, function (arg, cb) { - cache.add(arg, null, false, cb) + cache.add(arg, null, null, false, cb) }, function (er, pkgs) { if (er) return cb(er) chain(pkgs.map(function (pkg) { return function (cb) { @@ -71,7 +70,7 @@ function addSubmodule (name, url, cb) { var getSubmodules = function (cb) { var args = [ "submodule", "status" ] - + git.whichAndExec(args, function _(er, stdout) { if (er) return cb(er) var res = stdout.trim().split(/\n/).map(function (line) { diff --git a/deps/npm/lib/tag.js b/deps/npm/lib/tag.js index 1d04ad1f7..47e9a8c0a 100644 --- a/deps/npm/lib/tag.js +++ b/deps/npm/lib/tag.js @@ -5,16 +5,30 @@ tag.usage = "npm tag <project>@<version> [<tag>]" tag.completion = require("./unpublish.js").completion -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") + , semver = require("semver") function tag (args, cb) { - var thing = (args.shift() || "").split("@") - , project = thing.shift() - , version = thing.join("@") + var thing = npa(args.shift() || "") + , project = thing.name + , version = thing.rawSpec , t = args.shift() || npm.config.get("tag") + + t = t.trim() + if (!project || !version || !t) return cb("Usage:\n"+tag.usage) - var uri = url.resolve(npm.config.get("registry"), project) - registry.tag(uri, version, t, cb) + + if (semver.validRange(t)) { + var er = new Error("Tag name must not be a valid SemVer range: " + t) + return cb(er) + } + + mapToRegistry(project, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.tag(uri, version, t, cb) + }) } diff --git a/deps/npm/lib/unbuild.js b/deps/npm/lib/unbuild.js index b594f28a9..797762436 100644 --- a/deps/npm/lib/unbuild.js +++ b/deps/npm/lib/unbuild.js @@ -2,7 +2,6 @@ module.exports = unbuild unbuild.usage = "npm unbuild <folder>\n(this is plumbing)" var readJson = require("read-package-json") - , rm = require("./utils/gently-rm.js") , gentlyRm = require("./utils/gently-rm.js") , npm = require("./npm.js") , path = require("path") @@ -15,7 +14,7 @@ var readJson = require("read-package-json") // args is a list of folders. // remove any bins/etc, and then delete the folder. function unbuild (args, silent, cb) { - if (typeof silent === 'function') cb = silent, silent = false + if (typeof silent === "function") cb = silent, silent = false asyncMap(args, unbuild_(silent), cb) } @@ -28,7 +27,7 @@ function unbuild_ (silent) { return function (folder, cb_) { log.verbose(folder.substr(npm.prefix.length + 1), "unbuild") readJson(path.resolve(folder, "package.json"), function (er, pkg) { // if no json, then just trash it, but no scripts or whatever. - if (er) return rm(folder, cb) + if (er) return gentlyRm(folder, false, cb) readJson.cache.del(folder) chain ( [ [lifecycle, pkg, "preuninstall", folder, false, true] @@ -39,7 +38,7 @@ function unbuild_ (silent) { return function (folder, cb_) { } , [rmStuff, pkg, folder] , [lifecycle, pkg, "postuninstall", folder, false, true] - , [rm, folder] ] + , [gentlyRm, folder, undefined] ] , cb ) }) }} @@ -66,8 +65,8 @@ function rmBins (pkg, folder, parent, top, cb) { log.verbose([binRoot, pkg.bin], "binRoot") asyncMap(Object.keys(pkg.bin), function (b, cb) { if (process.platform === "win32") { - chain([ [rm, path.resolve(binRoot, b) + ".cmd"] - , [rm, path.resolve(binRoot, b) ] ], cb) + chain([ [gentlyRm, path.resolve(binRoot, b) + ".cmd", undefined] + , [gentlyRm, path.resolve(binRoot, b), undefined] ], cb) } else { gentlyRm( path.resolve(binRoot, b) , !npm.config.get("force") && folder diff --git a/deps/npm/lib/unpublish.js b/deps/npm/lib/unpublish.js index 225c1c3c4..2566cd5ae 100644 --- a/deps/npm/lib/unpublish.js +++ b/deps/npm/lib/unpublish.js @@ -1,40 +1,51 @@ module.exports = unpublish -var url = require("url") - , log = require("npmlog") +var log = require("npmlog") , npm = require("./npm.js") , registry = npm.registry , readJson = require("read-package-json") , path = require("path") + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") unpublish.usage = "npm unpublish <project>[@<version>]" unpublish.completion = function (opts, cb) { if (opts.conf.argv.remain.length >= 3) return cb() - var un = encodeURIComponent(npm.config.get("username")) - if (!un) return cb() - var uri = url.resolve(npm.config.get("registry"), "-/by-user/"+un) - registry.get(uri, null, function (er, pkgs) { - // do a bit of filtering at this point, so that we don't need - // to fetch versions for more than one thing, but also don't - // accidentally a whole project. - pkgs = pkgs[un] - if (!pkgs || !pkgs.length) return cb() - var partial = opts.partialWord.split("@") - , pp = partial.shift() - pkgs = pkgs.filter(function (p) { - return p.indexOf(pp) === 0 - }) - if (pkgs.length > 1) return cb(null, pkgs) - var uri = url.resolve(npm.config.get("registry"), pkgs[0]) - registry.get(uri, null, function (er, d) { + npm.commands.whoami([], true, function (er, username) { + if (er) return cb() + + var un = encodeURIComponent(username) + if (!un) return cb() + var byUser = "-/by-user/" + un + mapToRegistry(byUser, npm.config, function (er, uri) { if (er) return cb(er) - var vers = Object.keys(d.versions) - if (!vers.length) return cb(null, pkgs) - return cb(null, vers.map(function (v) { - return pkgs[0]+"@"+v - })) + + registry.get(uri, null, function (er, pkgs) { + // do a bit of filtering at this point, so that we don't need + // to fetch versions for more than one thing, but also don't + // accidentally a whole project. + pkgs = pkgs[un] + if (!pkgs || !pkgs.length) return cb() + var pp = npa(opts.partialWord).name + pkgs = pkgs.filter(function (p) { + return p.indexOf(pp) === 0 + }) + if (pkgs.length > 1) return cb(null, pkgs) + mapToRegistry(pkgs[0], npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, function (er, d) { + if (er) return cb(er) + var vers = Object.keys(d.versions) + if (!vers.length) return cb(null, pkgs) + return cb(null, vers.map(function (v) { + return pkgs[0] + "@" + v + })) + }) + }) + }) }) }) } @@ -42,23 +53,25 @@ unpublish.completion = function (opts, cb) { function unpublish (args, cb) { if (args.length > 1) return cb(unpublish.usage) - var thing = args.length ? args.shift().split("@") : [] - , project = thing.shift() - , version = thing.join("@") + var thing = args.length ? npa(args[0]) : {} + , project = thing.name + , version = thing.rawSpec + log.silly("unpublish", "args[0]", args[0]) + log.silly("unpublish", "thing", thing) if (!version && !npm.config.get("force")) { return cb("Refusing to delete entire project.\n" - +"Run with --force to do this.\n" - +unpublish.usage) + + "Run with --force to do this.\n" + + unpublish.usage) } - if (!project || path.resolve(project) === npm.prefix) { + if (!project || path.resolve(project) === npm.localPrefix) { // if there's a package.json in the current folder, then // read the package name and version out of that. - var cwdJson = path.join(process.cwd(), "package.json") + var cwdJson = path.join(npm.localPrefix, "package.json") return readJson(cwdJson, function (er, data) { if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er) - if (er) return cb("Usage:\n"+unpublish.usage) + if (er) return cb("Usage:\n" + unpublish.usage) gotProject(data.name, data.version, cb) }) } @@ -79,7 +92,10 @@ function gotProject (project, version, cb_) { return cb(er) } - var uri = url.resolve(npm.config.get("registry"), project) - registry.unpublish(uri, version, cb) + mapToRegistry(project, npm.config, function (er, uri) { + if (er) return cb(er) + + registry.unpublish(uri, version, cb) + }) }) } diff --git a/deps/npm/lib/utils/error-handler.js b/deps/npm/lib/utils/error-handler.js index 5c4f4c99e..788d3f8cc 100644 --- a/deps/npm/lib/utils/error-handler.js +++ b/deps/npm/lib/utils/error-handler.js @@ -24,13 +24,18 @@ process.on("exit", function (code) { } if (wroteLogFile) { - log.error("", ["" - ,"Additional logging details can be found in:" + // just a line break + if (log.levels[log.level] <= log.levels.error) console.error("") + + log.error("", + ["Please include the following file with any support request:" ," " + path.resolve("npm-debug.log") ].join("\n")) wroteLogFile = false } - log.error("not ok", "code", code) + if (code) { + log.error("code", code) + } } var doExit = npm.config.get("_exit") @@ -87,7 +92,6 @@ function exit (code, noLog) { function errorHandler (er) { - var printStack = false // console.error("errorHandler", er) if (!npm.config || !npm.config.loaded) { // logging won't work unless we pretend that it's ready @@ -112,13 +116,55 @@ function errorHandler (er) { var m = er.code || er.message.match(/^(?:Error: )?(E[A-Z]+)/) if (m && !er.code) er.code = m + ; [ "type" + , "fstream_path" + , "fstream_unc_path" + , "fstream_type" + , "fstream_class" + , "fstream_finish_call" + , "fstream_linkpath" + , "stack" + , "fstream_stack" + , "statusCode" + , "pkgid" + ].forEach(function (k) { + var v = er[k] + if (!v) return + if (k === "fstream_stack") v = v.join("\n") + log.verbose(k, v) + }) + + log.verbose("cwd", process.cwd()) + + var os = require("os") + // log.error("System", os.type() + " " + os.release()) + // log.error("command", process.argv.map(JSON.stringify).join(" ")) + // log.error("node -v", process.version) + // log.error("npm -v", npm.version) + log.error("", os.type() + " " + os.release()) + log.error("argv", process.argv.map(JSON.stringify).join(" ")) + log.error("node", process.version) + log.error("npm ", "v" + npm.version) + + ; [ "file" + , "path" + , "code" + , "errno" + , "syscall" + ].forEach(function (k) { + var v = er[k] + if (v) log.error(k, v) + }) + + // just a line break + if (log.levels[log.level] <= log.levels.error) console.error("") + switch (er.code) { case "ECONNREFUSED": log.error("", er) log.error("", ["\nIf you are behind a proxy, please make sure that the" ,"'proxy' config is set properly. See: 'npm help config'" ].join("\n")) - printStack = true break case "EACCES": @@ -126,7 +172,6 @@ function errorHandler (er) { log.error("", er) log.error("", ["\nPlease try running this command again as root/Administrator." ].join("\n")) - printStack = true break case "ELIFECYCLE": @@ -160,24 +205,22 @@ function errorHandler (er) { ].join("\n"), "JSON.parse") break + // TODO(isaacs) + // Add a special case here for E401 and E403 explaining auth issues? + case "E404": var msg = [er.message] if (er.pkgid && er.pkgid !== "-") { msg.push("", "'"+er.pkgid+"' is not in the npm registry." - ,"You should bug the author to publish it") + ,"You should bug the author to publish it (or use the name yourself!)") if (er.parent) { msg.push("It was specified as a dependency of '"+er.parent+"'") } - if (er.pkgid.match(/^node[\.\-]|[\.\-]js$/)) { - var s = er.pkgid.replace(/^node[\.\-]|[\.\-]js$/g, "") - if (s !== er.pkgid) { - s = s.replace(/[^a-z0-9]/g, ' ') - msg.push("\nMaybe try 'npm search " + s + "'") - } - } msg.push("\nNote that you can also install from a" - ,"tarball, folder, or http url, or git url.") + ,"tarball, folder, http url, or git url.") } + // There's no need to have 404 in the message as well. + msg[0] = msg[0].replace(/^404\s+/, "") log.error("404", msg.join("\n")) break @@ -185,9 +228,6 @@ function errorHandler (er) { log.error("publish fail", ["Cannot publish over existing version." ,"Update the 'version' field in package.json and try again." ,"" - ,"If the previous version was published in error, see:" - ," npm help unpublish" - ,"" ,"To automatically increment version numbers, see:" ," npm help version" ].join("\n")) @@ -295,50 +335,13 @@ function errorHandler (er) { break default: - log.error("", er.stack || er.message || er) - log.error("", ["If you need help, you may report this *entire* log," - ,"including the npm and node versions, at:" + log.error("", er.message || er) + log.error("", ["", "If you need help, you may report this error at:" ," <http://github.com/npm/npm/issues>" ].join("\n")) - printStack = false break } - var os = require("os") - // just a line break - if (log.levels[log.level] <= log.levels.error) console.error("") - log.error("System", os.type() + " " + os.release()) - log.error("command", process.argv - .map(JSON.stringify).join(" ")) - log.error("cwd", process.cwd()) - log.error("node -v", process.version) - log.error("npm -v", npm.version) - - ; [ "file" - , "path" - , "type" - , "syscall" - , "fstream_path" - , "fstream_unc_path" - , "fstream_type" - , "fstream_class" - , "fstream_finish_call" - , "fstream_linkpath" - , "code" - , "errno" - , "stack" - , "fstream_stack" - ].forEach(function (k) { - var v = er[k] - if (k === "stack") { - if (!printStack) return - if (!v) v = er.message - } - if (!v) return - if (k === "fstream_stack") v = v.join("\n") - log.error(k, v) - }) - exit(typeof er.errno === "number" ? er.errno : 1) } @@ -350,17 +353,16 @@ function writeLogFile (cb) { var fs = require("graceful-fs") , fstr = fs.createWriteStream("npm-debug.log") - , util = require("util") , os = require("os") , out = "" log.record.forEach(function (m) { var pref = [m.id, m.level] if (m.prefix) pref.push(m.prefix) - pref = pref.join(' ') + pref = pref.join(" ") m.message.trim().split(/\r?\n/).map(function (line) { - return (pref + ' ' + line).trim() + return (pref + " " + line).trim() }).forEach(function (line) { out += line + os.EOL }) diff --git a/deps/npm/lib/utils/fetch.js b/deps/npm/lib/utils/fetch.js deleted file mode 100644 index f6e5166ff..000000000 --- a/deps/npm/lib/utils/fetch.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Fetch an HTTP url to a local file. - **/ - -var request = require("request") - , fs = require("graceful-fs") - , npm = require("../npm.js") - , url = require("url") - , log = require("npmlog") - , path = require("path") - , mkdir = require("mkdirp") - , chownr = require("chownr") - , regHost - , once = require("once") - , crypto = require("crypto") - -module.exports = fetch - -function fetch (remote, local, headers, cb) { - if (typeof cb !== "function") cb = headers, headers = {} - cb = once(cb) - log.verbose("fetch", "to=", local) - mkdir(path.dirname(local), function (er, made) { - if (er) return cb(er) - fetch_(remote, local, headers, cb) - }) -} - -function fetch_ (remote, local, headers, cb) { - var fstr = fs.createWriteStream(local, { mode : npm.modes.file }) - var response = null - - fstr.on("error", function (er) { - cb(er) - fstr.destroy() - }) - - var req = makeRequest(remote, fstr, headers) - req.on("response", function (res) { - log.http(res.statusCode, remote) - response = res - response.resume() - // Work around bug in node v0.10.0 where the CryptoStream - // gets stuck and never starts reading again. - if (process.version === "v0.10.0") { - response.resume = function (orig) { return function() { - var ret = orig.apply(response, arguments) - if (response.socket.encrypted) - response.socket.encrypted.read(0) - return ret - }}(response.resume) - } - }) - - fstr.on("close", function () { - var er - if (response && response.statusCode && response.statusCode >= 400) { - er = new Error(response.statusCode + " " - + require("http").STATUS_CODES[response.statusCode]) - } - cb(er, response) - }) -} - -function makeRequest (remote, fstr, headers) { - remote = url.parse(remote) - log.http("GET", remote.href) - regHost = regHost || url.parse(npm.config.get("registry")).host - - if (remote.host === regHost && npm.config.get("always-auth")) { - remote.auth = new Buffer( npm.config.get("_auth") - , "base64" ).toString("utf8") - if (!remote.auth) return fstr.emit("error", new Error( - "Auth required and none provided. Please run 'npm adduser'")) - } - - var proxy - if (remote.protocol !== "https:" || !(proxy = npm.config.get("https-proxy"))) { - proxy = npm.config.get("proxy") - } - - var sessionToken = npm.registry.sessionToken - if (!sessionToken) { - sessionToken = crypto.randomBytes(8).toString("hex") - npm.registry.sessionToken = sessionToken - } - - var ca = remote.host === regHost ? npm.config.get("ca") : undefined - var opts = { url: remote - , proxy: proxy - , strictSSL: npm.config.get("strict-ssl") - , rejectUnauthorized: npm.config.get("strict-ssl") - , ca: ca - , headers: - { "user-agent": npm.config.get("user-agent") - , "npm-session": sessionToken - , referer: npm.registry.refer - } - } - var req = request(opts) - req.on("error", function (er) { - fstr.emit("error", er) - }) - req.pipe(fstr) - return req -} diff --git a/deps/npm/lib/utils/gently-rm.js b/deps/npm/lib/utils/gently-rm.js index 241740fed..d43d0725e 100644 --- a/deps/npm/lib/utils/gently-rm.js +++ b/deps/npm/lib/utils/gently-rm.js @@ -3,54 +3,159 @@ module.exports = gentlyRm -var rimraf = require("rimraf") - , fs = require("graceful-fs") - , npm = require("../npm.js") - , path = require("path") +var npm = require("../npm.js") + , log = require("npmlog") + , resolve = require("path").resolve + , dirname = require("path").dirname + , lstat = require("graceful-fs").lstat + , readlink = require("graceful-fs").readlink + , isInside = require("path-is-inside") + , vacuum = require("fs-vacuum") + , rimraf = require("rimraf") + , some = require("async-some") -function gentlyRm (p, gently, cb) { - if (!cb) cb = gently, gently = null +function gentlyRm (path, gently, cb) { + if (!cb) { + cb = gently + gently = null + } // never rm the root, prefix, or bin dirs. // just a safety precaution. - p = path.resolve(p) - if (p === npm.dir || - p === npm.root || - p === npm.bin || - p === npm.prefix || - p === npm.globalDir || - p === npm.globalRoot || - p === npm.globalBin || - p === npm.globalPrefix) { - return cb(new Error("May not delete: " + p)) + var prefixes = [ + npm.dir, npm.root, npm.bin, npm.prefix, + npm.globalDir, npm.globalRoot, npm.globalBin, npm.globalPrefix + ] + + var resolved = resolve(path) + if (prefixes.indexOf(resolved) !== -1) { + log.verbose("gentlyRm", resolved, "is part of npm and can't be removed") + return cb(new Error("May not delete: "+resolved)) } - if (npm.config.get("force") || !gently) { - return rimraf(p, cb) + var options = {log : log.silly.bind(log, "gentlyRm")} + if (npm.config.get("force") || !gently) options.purge = true + + if (!gently) { + log.verbose("gentlyRm", "vacuuming", resolved) + return vacuum(resolved, options, cb) } - gently = path.resolve(gently) + var parent = resolve(gently) + log.verbose("gentlyRm", "verifying that", parent, "is managed by npm") + some(prefixes, isManaged(parent), function (er, matched) { + if (er) return cb(er) + + if (!matched) { + log.verbose("gentlyRm", parent, "is not managed by npm") + return clobberFail(resolved, parent, cb) + } + + log.silly("gentlyRm", parent, "is managed by npm") + + if (isInside(resolved, parent)) { + log.silly("gentlyRm", resolved, "is under", parent) + log.verbose("gentlyRm", "vacuuming", resolved, "up to", parent) + options.base = parent + return vacuum(resolved, options, cb) + } + + log.silly("gentlyRm", resolved, "is not under", parent) + log.silly("gentlyRm", "checking to see if", resolved, "is a link") + lstat(resolved, function (er, stat) { + if (er) { + if (er.code === "ENOENT") return cb(null) + return cb(er) + } + + if (!stat.isSymbolicLink()) { + log.verbose("gentlyRm", resolved, "is outside", parent, "and not a link") + return clobberFail(resolved, parent, cb) + } + + log.silly("gentlyRm", resolved, "is a link") + readlink(resolved, function (er, link) { + if (er) { + if (er.code === "ENOENT") return cb(null) + return cb(er) + } - // lstat it, see if it's a symlink. - fs.lstat(p, function (er, s) { - if (er) return rimraf(p, cb) - if (!s.isSymbolicLink()) next(null, path.resolve(p)) - realish(p, next) + var source = resolve(dirname(resolved), link) + if (isInside(source, parent)) { + log.silly("gentlyRm", source, "inside", parent) + log.verbose("gentlyRm", "vacuuming", resolved) + return vacuum(resolved, options, cb) + } + + log.silly("gentlyRm", "checking to see if", source, "is managed by npm") + some(prefixes, isManaged(source), function (er, matched) { + if (er) return cb(er) + + if (matched) { + log.silly("gentlyRm", source, "is under", matched) + log.verbose("gentlyRm", "removing", resolved) + rimraf(resolved, cb) + } + + log.verbose("gentlyRm", source, "is not managed by npm") + return clobberFail(path, parent, cb) + }) + }) + }) }) +} - function next (er, rp) { - if (rp && rp.indexOf(gently) !== 0) { - return clobberFail(p, gently, cb) +var resolvedPaths = {} +function isManaged (target) { + return predicate + + function predicate (path, cb) { + if (!path) { + log.verbose("isManaged", "no path") + return cb(null, false) + } + + path = resolve(path) + + // if the path has already been memoized, return immediately + var resolved = resolvedPaths[path] + if (resolved) { + var inside = isInside(target, resolved) + log.silly("isManaged", target, inside ? "is" : "is not", "inside", resolved) + + return cb(null, inside && path) } - rimraf(p, cb) + + // otherwise, check the path + lstat(path, function (er, stat) { + if (er) { + if (er.code === "ENOENT") return cb(null, false) + + return cb(er) + } + + // if it's not a link, cache & test the path itself + if (!stat.isSymbolicLink()) return cacheAndTest(path, path, target, cb) + + // otherwise, cache & test the link's source + readlink(path, function (er, source) { + if (er) { + if (er.code === "ENOENT") return cb(null, false) + + return cb(er) + } + + cacheAndTest(resolve(path, source), path, target, cb) + }) + }) } -} -function realish (p, cb) { - fs.readlink(p, function (er, r) { - if (er) return cb(er) - return cb(null, path.resolve(path.dirname(p), r)) - }) + function cacheAndTest (resolved, source, target, cb) { + resolvedPaths[source] = resolved + var inside = isInside(target, resolved) + log.silly("cacheAndTest", target, inside ? "is" : "is not", "inside", resolved) + cb(null, inside && source) + } } function clobberFail (p, g, cb) { diff --git a/deps/npm/lib/utils/is-git-url.js b/deps/npm/lib/utils/is-git-url.js deleted file mode 100644 index 7ded4b602..000000000 --- a/deps/npm/lib/utils/is-git-url.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = isGitUrl - -function isGitUrl (url) { - switch (url.protocol) { - case "git:": - case "git+http:": - case "git+https:": - case "git+rsync:": - case "git+ftp:": - case "git+ssh:": - return true - } -} diff --git a/deps/npm/lib/utils/lifecycle.js b/deps/npm/lib/utils/lifecycle.js index 8bcb99689..c0eb83dfb 100644 --- a/deps/npm/lib/utils/lifecycle.js +++ b/deps/npm/lib/utils/lifecycle.js @@ -71,11 +71,6 @@ function lifecycle_ (pkg, stage, wd, env, unsafe, failOk, cb) { , p = wd.split("node_modules") , acc = path.resolve(p.shift()) - // first add the directory containing the `node` executable currently - // running, so that any lifecycle script that invoke "node" will execute - // this same one. - pathArr.unshift(path.dirname(process.execPath)) - p.forEach(function (pp) { pathArr.unshift(path.join(acc, "node_modules", ".bin")) acc = path.join(acc, "node_modules", pp) @@ -353,13 +348,9 @@ function makeEnv (data, prefix, env) { function cmd (stage) { function CMD (args, cb) { - if (args.length) { - chain(args.map(function (p) { - return [npm.commands, "run-script", [p, stage]] - }), cb) - } else npm.commands["run-script"]([stage], cb) + npm.commands["run-script"]([stage].concat(args), cb) } - CMD.usage = "npm "+stage+" <name>" + CMD.usage = "npm "+stage+" [-- <args>]" var installedShallow = require("./completion/installed-shallow.js") CMD.completion = function (opts, cb) { installedShallow(opts, function (d) { diff --git a/deps/npm/lib/utils/map-to-registry.js b/deps/npm/lib/utils/map-to-registry.js new file mode 100644 index 000000000..cf665e4f6 --- /dev/null +++ b/deps/npm/lib/utils/map-to-registry.js @@ -0,0 +1,54 @@ +var url = require("url") + +var log = require("npmlog") + , npa = require("npm-package-arg") + +module.exports = mapToRegistry + +function mapToRegistry(name, config, cb) { + var uri + var scopedRegistry + + // the name itself takes precedence + var data = npa(name) + if (data.scope) { + // the name is definitely scoped, so escape now + name = name.replace("/", "%2f") + + log.silly("mapToRegistry", "scope", data.scope) + + scopedRegistry = config.get(data.scope + ":registry") + if (scopedRegistry) { + log.silly("mapToRegistry", "scopedRegistry (scoped package)", scopedRegistry) + uri = url.resolve(scopedRegistry, name) + } + else { + log.verbose("mapToRegistry", "no registry URL found for scope", data.scope) + } + } + + // ...then --scope=@scope or --scope=scope + var scope = config.get("scope") + if (!uri && scope) { + // I'm an enabler, sorry + if (scope.charAt(0) !== "@") scope = "@" + scope + + scopedRegistry = config.get(scope + ":registry") + if (scopedRegistry) { + log.silly("mapToRegistry", "scopedRegistry (scope in config)", scopedRegistry) + uri = url.resolve(scopedRegistry, name) + } + else { + log.verbose("mapToRegistry", "no registry URL found for scope", scope) + } + } + + // ...and finally use the default registry + if (!uri) { + uri = url.resolve(config.get("registry"), name) + } + + log.verbose("mapToRegistry", "name", name) + log.verbose("mapToRegistry", "uri", uri) + cb(null, uri) +} diff --git a/deps/npm/lib/version.js b/deps/npm/lib/version.js index 95d5ff2ee..5091ab9e2 100644 --- a/deps/npm/lib/version.js +++ b/deps/npm/lib/version.js @@ -23,7 +23,7 @@ version.usage = "npm version [<newversion> | major | minor | patch | prerelease function version (args, silent, cb_) { if (typeof cb_ !== "function") cb_ = silent, silent = false if (args.length > 1) return cb_(version.usage) - fs.readFile(path.join(process.cwd(), "package.json"), function (er, data) { + fs.readFile(path.join(npm.localPrefix, "package.json"), function (er, data) { if (!args.length) { var v = {} Object.keys(process.versions).forEach(function (k) { @@ -63,7 +63,7 @@ function version (args, silent, cb_) { if (data.version === newVer) return cb_(new Error("Version not changed")) data.version = newVer - fs.stat(path.join(process.cwd(), ".git"), function (er, s) { + fs.stat(path.join(npm.localPrefix, ".git"), function (er, s) { function cb (er) { if (!er && !silent) console.log("v" + newVer) cb_(er) @@ -111,7 +111,7 @@ function checkGit (data, cb) { } function write (data, cb) { - fs.writeFile( path.join(process.cwd(), "package.json") + fs.writeFile( path.join(npm.localPrefix, "package.json") , new Buffer(JSON.stringify(data, null, 2) + "\n") , cb ) } diff --git a/deps/npm/lib/view.js b/deps/npm/lib/view.js index 33bf550dd..43d09cbbc 100644 --- a/deps/npm/lib/view.js +++ b/deps/npm/lib/view.js @@ -4,21 +4,26 @@ module.exports = view view.usage = "npm view pkg[@version] [<field>[.subfield]...]" view.completion = function (opts, cb) { - var uri if (opts.conf.argv.remain.length <= 2) { - uri = url.resolve(npm.config.get("registry"), "-/short") - return registry.get(uri, null, cb) + return mapToRegistry("-/short", npm.config, function (er, uri) { + if (er) return cb(er) + + registry.get(uri, null, cb) + }) } // have the package, get the fields. var tag = npm.config.get("tag") - uri = url.resolve(npm.config.get("registry"), opts.conf.argv.remain[2]) - registry.get(uri, null, function (er, d) { + mapToRegistry(opts.conf.argv.remain[2], npm.config, function (er, uri) { if (er) return cb(er) - var dv = d.versions[d["dist-tags"][tag]] - , fields = [] - d.versions = Object.keys(d.versions).sort(semver.compareLoose) - fields = getFields(d).concat(getFields(dv)) - cb(null, fields) + + registry.get(uri, null, function (er, d) { + if (er) return cb(er) + var dv = d.versions[d["dist-tags"][tag]] + , fields = [] + d.versions = Object.keys(d.versions).sort(semver.compareLoose) + fields = getFields(d).concat(getFields(dv)) + cb(null, fields) + }) }) function getFields (d, f, pref) { @@ -42,71 +47,75 @@ view.completion = function (opts, cb) { } } -var url = require("url") - , npm = require("./npm.js") +var npm = require("./npm.js") , registry = npm.registry , log = require("npmlog") , util = require("util") , semver = require("semver") + , mapToRegistry = require("./utils/map-to-registry.js") + , npa = require("npm-package-arg") function view (args, silent, cb) { if (typeof cb !== "function") cb = silent, silent = false if (!args.length) return cb("Usage: "+view.usage) var pkg = args.shift() - , nv = pkg.split("@") - , name = nv.shift() - , version = nv.join("@") || npm.config.get("tag") + , nv = npa(pkg) + , name = nv.name + , version = nv.rawSpec || npm.config.get("tag") if (name === ".") return cb(view.usage) // get the data about this package - var uri = url.resolve(npm.config.get("registry"), name) - registry.get(uri, null, function (er, data) { + mapToRegistry(name, npm.config, function (er, uri) { if (er) return cb(er) - if (data["dist-tags"] && data["dist-tags"].hasOwnProperty(version)) { - version = data["dist-tags"][version] - } - if (data.time && data.time.unpublished) { - var u = data.time.unpublished - er = new Error("Unpublished by " + u.name + " on " + u.time) - er.statusCode = 404 - er.code = "E404" - er.pkgid = data._id - return cb(er, data) - } + registry.get(uri, null, function (er, data) { + if (er) return cb(er) + if (data["dist-tags"] && data["dist-tags"].hasOwnProperty(version)) { + version = data["dist-tags"][version] + } + if (data.time && data.time.unpublished) { + var u = data.time.unpublished + er = new Error("Unpublished by " + u.name + " on " + u.time) + er.statusCode = 404 + er.code = "E404" + er.pkgid = data._id + return cb(er, data) + } - var results = [] - , error = null - , versions = data.versions || {} - data.versions = Object.keys(versions).sort(semver.compareLoose) - if (!args.length) args = [""] - // remove readme unless we asked for it - if (-1 === args.indexOf("readme")) { - delete data.readme - } + var results = [] + , error = null + , versions = data.versions || {} + data.versions = Object.keys(versions).sort(semver.compareLoose) + if (!args.length) args = [""] + + // remove readme unless we asked for it + if (-1 === args.indexOf("readme")) { + delete data.readme + } - Object.keys(versions).forEach(function (v) { - if (semver.satisfies(v, version, true)) args.forEach(function (args) { - // remove readme unless we asked for it - if (-1 === args.indexOf("readme")) { - delete versions[v].readme - } - results.push(showFields(data, versions[v], args)) + Object.keys(versions).forEach(function (v) { + if (semver.satisfies(v, version, true)) args.forEach(function (args) { + // remove readme unless we asked for it + if (-1 === args.indexOf("readme")) { + delete versions[v].readme + } + results.push(showFields(data, versions[v], args)) + }) }) - }) - results = results.reduce(reducer, {}) - var retval = results + results = results.reduce(reducer, {}) + var retval = results - if (args.length === 1 && args[0] === "") { - retval = cleanBlanks(retval) - log.silly("cleanup", retval) - } + if (args.length === 1 && args[0] === "") { + retval = cleanBlanks(retval) + log.silly("cleanup", retval) + } - if (error || silent) cb(error, retval) - else printData(results, data._id, cb.bind(null, error, retval)) + if (error || silent) cb(error, retval) + else printData(results, data._id, cb.bind(null, error, retval)) + }) }) } diff --git a/deps/npm/lib/whoami.js b/deps/npm/lib/whoami.js index f1c67e2b0..b33f93743 100644 --- a/deps/npm/lib/whoami.js +++ b/deps/npm/lib/whoami.js @@ -1,13 +1,39 @@ -module.exports = whoami - var npm = require("./npm.js") -whoami.usage = "npm whoami\n(just prints the 'username' config)" +module.exports = whoami + +whoami.usage = "npm whoami\n(just prints username according to given registry)" function whoami (args, silent, cb) { - if (typeof cb !== "function") cb = silent, silent = false - var me = npm.config.get("username") - var msg = me ? me : "Not authed. Run 'npm adduser'" + // FIXME: need tighter checking on this, but is a breaking change + if (typeof cb !== "function") { + cb = silent + silent = false + } + + var registry = npm.config.get("registry") + if (!registry) return cb(new Error("no default registry set")) + + var credentials = npm.config.getCredentialsByURI(registry) + if (credentials) { + if (credentials.username) { + if (!silent) console.log(credentials.username) + return process.nextTick(cb.bind(this, null, credentials.username)) + } + else if (credentials.token) { + return npm.registry.whoami(registry, function (er, username) { + if (er) return cb(er) + + if (!silent) console.log(username) + cb(null, username) + }) + } + } + + // At this point, if they have a credentials object, it doesn't + // have a token or auth in it. Probably just the default + // registry. + var msg = "Not authed. Run 'npm adduser'" if (!silent) console.log(msg) - process.nextTick(cb.bind(this, null, me)) + process.nextTick(cb.bind(this, null, msg)) } |