diff --git a/lib/actions/actions.js b/lib/actions/actions.js index 2d91f322..b0ef7470 100644 --- a/lib/actions/actions.js +++ b/lib/actions/actions.js @@ -386,183 +386,3 @@ actions.bulkDirectory = function directory(source, destination, process) { }); return this; }; - -/** - * Remotely fetch a package from github (or an archive), store this into a _cache - * folder, and provide a "remote" object as a facade API to ourself (part of - * genrator API, copy, template, directory). It's possible to remove local cache, - * and force a new remote fetch of the package. - * - * ### Examples: - * - * this.remote('user', 'repo', function(err, remote) { - * remote.copy('.', 'vendors/user-repo'); - * }); - * - * this.remote('user', 'repo', 'branch', function(err, remote) { - * remote.copy('.', 'vendors/user-repo'); - * }); - * - * this.remote('http://foo.com/bar.zip', function(err, remote) { - * remote.copy('.', 'vendors/user-repo'); - * }); - * - * When fetching from Github - * @param {String} username - * @param {String} repo - * @param {String} branch - * @param {Function} cb - * @param {Boolean} refresh - * - * @also - * When fetching an archive - * @param {String} url - * @param {Function} cb - * @param {Boolean} refresh - */ - -actions.remote = function () { - var username; - var repo; - var branch; - var cb; - var refresh; - var url; - var cache; - - if (arguments.length <= 3 && typeof arguments[2] !== 'function') { - url = arguments[0]; - cb = arguments[1]; - refresh = arguments[2]; - cache = path.join(this.cacheRoot(), _.slugify(url)); - } else { - username = arguments[0]; - repo = arguments[1]; - branch = arguments[2]; - cb = arguments[3]; - refresh = arguments[4]; - - if (!cb) { - cb = branch; - branch = 'master'; - } - - cache = path.join(this.cacheRoot(), username, repo, branch); - url = 'http://github.com/' + [username, repo, 'archive', branch].join('/') + '.tar.gz'; - } - - var self = this; - - var done = function (err) { - if (err) { - return cb(err); - } - - self.remoteDir(cache, cb); - }; - - fs.stat(cache, function (err) { - // already cached - if (!err) { - // no refresh, so we can use this cache - if (!refresh) { - return done(); - } - - // otherwise, we need to remove it, to fetch it again - rimraf(cache, function (err) { - if (err) { - return cb(err); - } - self.tarball(url, cache, done); - }); - - } else { - self.tarball(url, cache, done); - } - }); - - return this; -}; - -/** - * Retrieve a stored directory and use as a remote reference. This is handy if - * you have files you want to move that may have been downloaded differently to - * using `this.remove()` (eg such as `node_modules` installed via `package.json`) - * - * ### Examples: - * - * this.remote('foo/bar', function(err, remote) { - * remote.copy('.', 'vendors/user-repo'); - * }); - * - * @param {String} cache - */ -actions.remoteDir = function (cache, cb) { - var self = this; - var files = this.expandFiles('**', { cwd: cache, dot: true }); - - var remote = {}; - remote.cachePath = cache; - - // simple proxy to `.copy(source, destination)` - remote.copy = function copy(source, destination) { - source = path.join(cache, source); - self.copy(source, destination); - return this; - }; - - // simple proxy to `.bulkCopy(source, destination)` - remote.bulkCopy = function copy(source, destination) { - source = path.join(cache, source); - self.bulkCopy(source, destination); - return this; - }; - - // same as `.template(source, destination, data)` - remote.template = function template(source, destination, data) { - data = data || self; - destination = destination || source; - source = path.join(cache, source); - - var body = self.engine(self.read(source), data); - self.write(destination, body); - }; - - // same as `.template(source, destination)` - remote.directory = function directory(source, destination) { - var root = self.sourceRoot(); - self.sourceRoot(cache); - self.directory(source, destination); - self.sourceRoot(root); - }; - - // simple proxy to `.bulkDirectory(source, destination)` - remote.bulkDirectory = function directory(source, destination) { - var root = self.sourceRoot(); - self.sourceRoot(cache); - self.bulkDirectory(source, destination); - self.sourceRoot(root); - }; - - var noop = function () {}; - var fileLogger = { write: noop, warn: noop }; - - // Set the file-utils environments - // Set logger as a noop as logging is handled by the yeoman conflicter - remote.src = file.createEnv({ - base: cache, - dest: self.destinationRoot(), - logger: fileLogger - }); - remote.dest = file.createEnv({ - base: self.destinationRoot(), - dest: cache, - logger: fileLogger - }); - - remote.dest.registerValidationFilter('collision', self.getCollisionFilter()); - remote.src.registerValidationFilter('collision', self.getCollisionFilter()); - - cb(null, remote, files); -}; diff --git a/lib/actions/fetch.js b/lib/actions/fetch.js index 1c3eb476..e9154440 100644 --- a/lib/actions/fetch.js +++ b/lib/actions/fetch.js @@ -63,6 +63,12 @@ fetch.extract = function _extract(archive, destination, cb) { }); }; +/** @alias fetch.extract */ fetch.tarball = fetch.extract; +/** + * Export of the `request` node module. + * @deprecated Require your own version of the request module. + * Yeoman will eventually remove it. + */ fetch.request = require('request'); diff --git a/lib/actions/remote.js b/lib/actions/remote.js new file mode 100644 index 00000000..a7ff8fb8 --- /dev/null +++ b/lib/actions/remote.js @@ -0,0 +1,193 @@ +'use strict'; +var fs = require('fs'); +var path = require('path'); +var _ = require('lodash'); +var fileUtils = require('file-utils'); +var rimraf = require('rimraf'); + +var noop = _.noop; + +/** + * @mixin + * @alias actions/remote + */ +var remote = module.exports; + +/** + * Remotely fetch a package from github (or an archive), store this into a _cache + * folder, and provide a "remote" object as a facade API to ourself (part of + * genrator API, copy, template, directory). It's possible to remove local cache, + * and force a new remote fetch of the package. + * + * ### Examples: + * + * this.remote('user', 'repo', function(err, remote) { + * remote.copy('.', 'vendors/user-repo'); + * }); + * + * this.remote('user', 'repo', 'branch', function(err, remote) { + * remote.copy('.', 'vendors/user-repo'); + * }); + * + * this.remote('http://foo.com/bar.zip', function(err, remote) { + * remote.copy('.', 'vendors/user-repo'); + * }); + * + * When fetching from Github + * @param {String} username + * @param {String} repo + * @param {String} branch + * @param {Function} cb + * @param {Boolean} refresh + * + * @also + * When fetching an archive + * @param {String} url + * @param {Function} cb + * @param {Boolean} refresh + */ + +remote.remote = function () { + var username; + var repo; + var branch; + var cb; + var refresh; + var url; + var cache; + + if (arguments.length <= 3 && typeof arguments[2] !== 'function') { + url = arguments[0]; + cb = arguments[1]; + refresh = arguments[2]; + cache = path.join(this.cacheRoot(), _.slugify(url)); + } else { + username = arguments[0]; + repo = arguments[1]; + branch = arguments[2]; + cb = arguments[3]; + refresh = arguments[4]; + + if (!cb) { + cb = branch; + branch = 'master'; + } + + cache = path.join(this.cacheRoot(), username, repo, branch); + url = 'http://github.com/' + [username, repo, 'archive', branch].join('/') + '.tar.gz'; + } + + var self = this; + + var done = function (err) { + if (err) { + return cb(err); + } + + self.remoteDir(cache, cb); + }; + + fs.stat(cache, function (err) { + // already cached + if (!err) { + // no refresh, so we can use this cache + if (!refresh) { + return done(); + } + + // otherwise, we need to remove it, to fetch it again + rimraf(cache, function (err) { + if (err) { + return cb(err); + } + self.extract(url, cache, done); + }); + + } else { + self.extract(url, cache, done); + } + }); + + return this; +}; + +/** + * Retrieve a stored directory and use as a remote reference. This is handy if + * you have files you want to move that may have been downloaded differently to + * using `this.remove()` (eg such as `node_modules` installed via `package.json`) + * + * ### Examples: + * + * this.remote('foo/bar', function(err, remote) { + * remote.copy('.', 'vendors/user-repo'); + * }); + * + * @param {String} cache + */ +remote.remoteDir = function (cache, cb) { + var self = this; + var files = this.expandFiles('**', { cwd: cache, dot: true }); + + var remote = {}; + remote.cachePath = cache; + + // simple proxy to `.copy(source, destination)` + remote.copy = function copy(source, destination) { + source = path.join(cache, source); + self.copy(source, destination); + return this; + }; + + // simple proxy to `.bulkCopy(source, destination)` + remote.bulkCopy = function copy(source, destination) { + source = path.join(cache, source); + self.bulkCopy(source, destination); + return this; + }; + + // same as `.template(source, destination, data)` + remote.template = function template(source, destination, data) { + data = data || self; + destination = destination || source; + source = path.join(cache, source); + + var body = self.engine(self.read(source), data); + self.write(destination, body); + }; + + // same as `.template(source, destination)` + remote.directory = function directory(source, destination) { + var root = self.sourceRoot(); + self.sourceRoot(cache); + self.directory(source, destination); + self.sourceRoot(root); + }; + + // simple proxy to `.bulkDirectory(source, destination)` + remote.bulkDirectory = function directory(source, destination) { + var root = self.sourceRoot(); + self.sourceRoot(cache); + self.bulkDirectory(source, destination); + self.sourceRoot(root); + }; + + var fileLogger = { write: noop, warn: noop }; + + // Set the file-utils environments + // Set logger as a noop as logging is handled by the yeoman conflicter + remote.src = fileUtils.createEnv({ + base: cache, + dest: self.destinationRoot(), + logger: fileLogger + }); + remote.dest = fileUtils.createEnv({ + base: self.destinationRoot(), + dest: cache, + logger: fileLogger + }); + + remote.dest.registerValidationFilter('collision', self.getCollisionFilter()); + remote.src.registerValidationFilter('collision', self.getCollisionFilter()); + + cb(null, remote, files); +}; diff --git a/lib/base.js b/lib/base.js index 1d12b23d..6a1ef927 100644 --- a/lib/base.js +++ b/lib/base.js @@ -36,6 +36,7 @@ var fileLogger = { write: noop, warn: noop }; * @mixes actions/invoke * @mixes actions/spawn_command * @mixes actions/string + * @mixes actions/remote * @mixes actions/user * @mixes actions/wiring * @mixes actions/help @@ -176,6 +177,7 @@ _.extend(Base.prototype, require('./actions/fetch')); _.extend(Base.prototype, require('./actions/file')); _.extend(Base.prototype, require('./actions/install')); _.extend(Base.prototype, require('./actions/string')); +_.extend(Base.prototype, require('./actions/remote')); _.extend(Base.prototype, require('./actions/wiring')); _.extend(Base.prototype, require('./actions/help')); _.extend(Base.prototype, require('./util/common')); diff --git a/test/fetch.js b/test/fetch.js index 342f6f80..b442f772 100644 --- a/test/fetch.js +++ b/test/fetch.js @@ -5,48 +5,29 @@ var path = require('path'); var util = require('util'); var events = require('events'); var tmpdir = require('os').tmpdir(); -var assert = require('assert'); +var assert = require('../lib/test/assert'); var nock = require('nock'); -var generators = require('..'); +var yeoman = require('..'); var fetch = require('../lib/actions/fetch'); -var noop = function () { return this; }; - -describe('generators.Base fetch utilities', function () { - // increase timeout to 15s for this suite (slow connections like mine needs that) - this.timeout(50000); - - before(generators.test.setUpTestDirectory(path.join(__dirname, 'temp'))); +var tmp = path.join(tmpdir, 'yeoman-test'); +describe('yeoman.generators.Base fetch utilities', function () { beforeEach(function () { - function Dummy() { - generators.Base.apply(this, arguments); - } - util.inherits(Dummy, generators.Base); - - Dummy.prototype.test = function () { - this.shouldRun = true; - }; - - this.env = generators(); - this.dummy = new Dummy({ - env: this.env, + this.dummy = new yeoman.generators.Base({ + env: yeoman(), resolved: 'test:fetch' }); - this.Dummy = Dummy; }); describe('#tarball()', function () { it('download and untar via the NPM download package', function (done) { - var tmp = path.join(tmpdir, 'yeoman-test'); var scope = nock('http://example.com') .get('/f.tar.gz') .replyWithFile(200, path.join(__dirname, 'fixtures/testFile.tar.gz')); this.dummy.tarball('http://example.com/f.tar.gz', tmp, function (err) { - if (err) { - return done(err); - } + if (err) return done(err); assert(scope.isDone()); done(); }); @@ -59,43 +40,16 @@ describe('generators.Base fetch utilities', function () { describe('#fetch()', function () { it('allow the fetching of a single file', function (done) { - var tmp = path.join(tmpdir, 'yeoman-test'); var scope = nock('http://example.com') .get('/f.txt') .replyWithFile(200, path.join(__dirname, 'fixtures/help.txt')); this.dummy.fetch('http://example.com/f.txt', tmp, function (err) { - if (err) { - return done(err); - } + if (err) return done(err); assert(scope.isDone()); fs.stat(path.join(tmp, 'f.txt'), done); }); }); }); - describe('#remote()', function () { - it('remotely fetch a package on github', function (done) { - this.dummy.remote('yeoman', 'generator', done); - }); - - it('cache the result internally into a `_cache` folder', function (done) { - fs.stat(path.join(this.dummy.cacheRoot(), 'yeoman/generator/master'), done); - }); - - it('invoke `cb` with a remote object to interract with the downloaded package', function (done) { - this.dummy.remote('yeoman', 'generator', function (err, remote) { - if (err) { - return done(err); - } - - ['copy', 'template', 'directory'].forEach(function (method) { - assert.equal(typeof remote[method], 'function'); - }); - - done(); - }); - }); - }); - }); diff --git a/test/remote.js b/test/remote.js index eb6d4d39..d2567d0c 100644 --- a/test/remote.js +++ b/test/remote.js @@ -14,6 +14,25 @@ describe('yeoman.base#remote', function () { this.dummy = env.create('dummy'); }); + it('remotely fetch a package on github', function (done) { + this.dummy.remote('yeoman', 'generator', done); + }); + + it('cache the result internally into a `_cache` folder', function (done) { + this.dummy.remote('yeoman', 'generator', function () { + fs.stat(path.join(this.dummy.cacheRoot(), 'yeoman/generator/master'), done); + }.bind(this)); + }); + + it('invoke `cb` with a remote object to interract with the downloaded package', function (done) { + this.dummy.remote('yeoman', 'generator', function (err, remote) { + if (err) return done(err); + + assert.implement(remote, ['copy', 'template', 'directory']); + done(); + }); + }); + describe('github', function () { describe('callback argument remote#copy', function () {