blob: 10f214fa931e9755610d90b3a40dc3615c009e63 [file] [log] [blame]
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()
})
}