var fs = require("fs") var path = require("path") var log = require("npmlog") var semver = require("semver") exports.checkEngine = checkEngine function checkEngine (target, npmVer, nodeVer, force, strict, cb) { var nodev = force ? null : nodeVer , strict = strict || target.engineStrict , eng = target.engines if (!eng) return cb() if (nodev && eng.node && !semver.satisfies(nodev, eng.node) || eng.npm && !semver.satisfies(npmVer, eng.npm)) { if (strict) { var er = new Error("Unsupported") er.code = "ENOTSUP" er.required = eng er.pkgid = target._id return cb(er) } else { log.warn( "engine", "%s: wanted: %j (current: %j)" , target._id, eng, {node: nodev, npm: npmVer} ) } } return cb() } exports.checkPlatform = checkPlatform function checkPlatform (target, force, cb) { var platform = process.platform , arch = process.arch , osOk = true , cpuOk = true if (force) { return cb() } if (target.os) { osOk = checkList(platform, target.os) } if (target.cpu) { cpuOk = checkList(arch, target.cpu) } if (!osOk || !cpuOk) { var er = new Error("Unsupported") er.code = "EBADPLATFORM" er.os = target.os || ['any'] er.cpu = target.cpu || ['any'] er.pkgid = target._id return cb(er) } return cb() } function checkList (value, list) { var tmp , match = false , blc = 0 if (typeof list === "string") { list = [list] } if (list.length === 1 && list[0] === "any") { return true } for (var i = 0; i < list.length; ++i) { tmp = list[i] if (tmp[0] === '!') { tmp = tmp.slice(1) if (tmp === value) { return false } ++blc } else { match = match || tmp === value } } return match || blc === list.length } exports.checkCycle = checkCycle function checkCycle (target, ancestors, cb) { // there are some very rare and pathological edge-cases where // a cycle can cause npm to try to install a never-ending tree // of stuff. // Simplest: // // A -> B -> A' -> B' -> A -> B -> A' -> B' -> A -> ... // // Solution: Simply flat-out refuse to install any name@version // that is already in the prototype tree of the ancestors object. // A more correct, but more complex, solution would be to symlink // the deeper thing into the new location. // Will do that if anyone whines about this irl. // // Note: `npm install foo` inside of the `foo` package will abort // earlier if `--force` is not set. However, if it IS set, then // we need to still fail here, but just skip the first level. Of // course, it'll still fail eventually if it's a true cycle, and // leave things in an undefined state, but that's what is to be // expected when `--force` is used. That is why getPrototypeOf // is used *twice* here: to skip the first level of repetition. var p = Object.getPrototypeOf(Object.getPrototypeOf(ancestors)) , name = target.name , version = target.version while (p && p !== Object.prototype && p[name] !== version) { p = Object.getPrototypeOf(p) } if (p[name] !== version) return cb() var er = new Error("Unresolvable cycle detected") var tree = [target._id, JSON.parse(JSON.stringify(ancestors))] , t = Object.getPrototypeOf(ancestors) while (t && t !== Object.prototype) { if (t === p) t.THIS_IS_P = true tree.push(JSON.parse(JSON.stringify(t))) t = Object.getPrototypeOf(t) } log.verbose("unresolvable dependency tree", tree) er.pkgid = target._id er.code = "ECYCLE" return cb(er) } exports.checkGit = checkGit function checkGit (folder, cb) { // if it's a git repo then don't touch it! fs.lstat(folder, function (er, s) { if (er || !s.isDirectory()) return cb() else checkGit_(folder, cb) }) } function checkGit_ (folder, cb) { fs.stat(path.resolve(folder, ".git"), function (er, s) { if (!er && s.isDirectory()) { var e = new Error("Appears to be a git repo or submodule.") e.path = folder e.code = "EISGIT" return cb(e) } cb() }) }