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() }) }