lib/package.js
'use strict';
var path = require('path'),
cwd = process.cwd(),
fs = require('fs'),
deferred = require('deferred'),
rimraf = deferred.promisify(require('rimraf')),
mkdirp = deferred.promisify(require('mkdirp')),
log = require('npmlog'),
Download = require('download'),
cache = require('npm-cache-filename'),
slash = require('slash'),
tmp = slash(path.join(require('os').tmpdir(), 'any-packages-cache')),
tarpack = require('tar-pack')
;
function setPkg(pkg){
pkg.resolvedKey = '_anyPackages';
pkg.opts = {};
pkg.installMethod = undefined;
Object.defineProperty(pkg, 'installTo', {
get: function(){
return slash(path.join(cwd, 'node_modules', this.name));
}
});
Object.defineProperty(pkg, 'cacheTo', {
get: function(){
return cache(tmp, this.url);
}
});
Object.defineProperty(pkg, 'installed', {
get: function() {
var pkgJson = slash(path.join(this.installTo, 'package.json')),
existing = fs.existsSync(pkgJson)
;
if (existing) {
delete require.cache[require.resolve(pkgJson)];
}
return (existing && (!require(pkgJson)[this.resolvedKey] || require(pkgJson)[this.resolvedKey] === this.url));
}
});
Object.defineProperty(pkg, 'cached', {
get: function() {
return !!this.opts.cache && fs.existsSync(this.cacheTo);
}
});
}
function cacheInstall(pkg){
pkg.installMethod = 'cache';
log.info('cache', pkg.url, ' into ', pkg.name);
if (pkg.opts.test) {
log.info('by-passed', 'for', pkg.name);
return deferred.resolve(pkg);
} else {
var unpacking = deferred();
fs
.createReadStream(pkg.cacheTo)
.pipe(tarpack.unpack(pkg.installTo, function(err){
if (err){
unpacking.reject(err);
} else {
unpacking.resolve(pkg);
}
}));
return unpacking.promise;
}
}
function downloadInstall(pkg){
pkg.installMethod = 'download';
log.info('downloading', pkg.url, ' into ', pkg.name);
if (pkg.opts.test) {
log.info('by-passed', 'for', pkg.name);
return deferred.resolve(pkg);
} else {
var downloading = deferred();
new Download({extract: true, mode: '755'})
.get(pkg.url)
.dest(pkg.installTo)
.run(function(err){
if (err){
downloading.reject(err);
} else {
downloading.resolve(pkg);
}
});
return downloading.promise;
}
}
function cacheSave(pkg) {
if (!pkg.installed || !pkg.opts.cache || pkg.opts.test) {
return deferred.resolve(pkg);
}
return mkdirp(path.dirname(pkg.cacheTo)).then(function(){
var dest = fs.createWriteStream(pkg.cacheTo),
caching = deferred()
;
tarpack.pack(pkg.installTo)
.pipe(dest)
.on('close', function(){
caching.resolve(pkg);
})
.on('error', function(err){
caching.reject(err);
});
return caching.promise;
});
}
function writePackageJson(pkg) {
var filepath = slash(path.join(pkg.installTo, 'package.json')),
writeFile = deferred.promisify(fs.writeFile),
pkgJson = null
;
if (pkg.opts.test) {
return deferred.resolve(pkg);
}
if (!fs.existsSync(filepath)) {
pkgJson = {
name: pkg.name,
version: pkg.version || '0.0.0',
description: pkg.name + ', added with love by any-packages'
};
} else {
pkgJson = require(filepath);
}
pkgJson[pkg.resolvedKey] = pkg.url;
return writeFile(filepath, JSON.stringify(pkgJson, null, 2)).then(function(){
return pkg;
});
}
function install(pkg, opts){
setPkg(pkg);
pkg.opts = opts || {};
var def = deferred(),
action
;
rimraf(pkg.installTo).then(function(){
if (pkg.cached && pkg.opts.cache && !pkg.opts.force) {
action = cacheInstall(pkg);
} else {
action = downloadInstall(pkg);
}
return action.then(
writePackageJson
).then(
cacheSave
).then(function(pkg){
log.info('done', pkg.name);
def.resolve(pkg);
}, function(err){
log.error('failed', pkg.name, ':', err.message);
def.resolve(pkg);
});
}, function(err) {
log.error('cannot remove ', pkg.installTo);
return def.resolve(pkg);
});
return def.promise;
}
function clearCache(){
require('rimraf').sync(tmp);
}
module.exports = {
setPkg: setPkg,
install: install,
clearCache: clearCache
};