You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.1 KiB

var fs = require('fs')
var path = require('path')
var util = require('util')
var semver = require('semver')
exports.checkEngine = checkEngine
function checkEngine (target, npmVer, nodeVer, force, strict, cb) {
var nodev = force ? null : nodeVer
var eng = target.engines
if (!eng) return cb()
if (nodev && eng.node && !semver.satisfies(nodev, eng.node) ||
eng.npm && !semver.satisfies(npmVer, eng.npm)) {
var er = new Error(util.format('Unsupported engine for %s: wanted: %j (current: %j)',
target._id, eng, {node: nodev, npm: npmVer}))
er.code = 'ENOTSUP'
er.required = eng
er.pkgid = target._id
if (strict) {
return cb(er)
} else {
return cb(null, er)
}
}
return cb()
}
exports.checkPlatform = checkPlatform
function checkPlatform (target, force, cb) {
var platform = process.platform
var arch = process.arch
var osOk = true
var 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(util.format('Unsupported platform for %s: wanted %j (current: %j)',
target._id, target, {os: platform, cpu: arch}))
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
var match = false
var 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))
var name = target.name
var 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(target._id + ': Unresolvable cycle detected')
var tree = [target._id, JSON.parse(JSON.stringify(ancestors))]
var 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)
}
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(folder + ': Appears to be a git repo or submodule.')
e.path = folder
e.code = 'EISGIT'
return cb(e)
}
cb()
})
}