From f04a502e2ff8aba3dd1c012a3e3031d2d91c37b7 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Fri, 18 Mar 2016 17:22:16 -0400 Subject: [PATCH 01/14] a variety of smaller fixes, scanning through the project slowly --- lib/api/add.js | 4 +--- lib/api/remove.js | 4 +--- lib/index.js | 28 ++++++++++++--------------- lib/template.js | 49 +++++++++++++++++++++++------------------------ 4 files changed, 38 insertions(+), 47 deletions(-) diff --git a/lib/api/add.js b/lib/api/add.js index f46db3d..3da13c2 100644 --- a/lib/api/add.js +++ b/lib/api/add.js @@ -9,7 +9,5 @@ import Template from '../template' */ export default function (sprout, name, src) { const template = new Template(sprout, name, src) - return template.save().then((template) => { - sprout.templates[name] = template - }) + return template.save().then(template => sprout.templates[name] = template) } diff --git a/lib/api/remove.js b/lib/api/remove.js index 726f71b..7b19d84 100644 --- a/lib/api/remove.js +++ b/lib/api/remove.js @@ -9,9 +9,7 @@ import W from 'when' export default function (sprout, name) { const template = sprout.templates[name] if (template) { - return template.remove().then(() => { - delete sprout.templates[name] - }) + return template.remove().then(_ => delete sprout.templates[name]) } return W.reject(new Error(`template ${name} does not exist`)) } diff --git a/lib/index.js b/lib/index.js index d98475b..bd72616 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,32 +9,28 @@ import api from './api' * @param {String} basePath - Path to directory containing Sprout templates. * @return {Function} - Sprout instance. */ -class Sprout { +class Sprout extends EventEmitter { constructor (basePath) { + super() this.templates = {} this.path = basePath - this.emitter = new EventEmitter() - var lstat, dirs, name, dir - if (!fs.existsSync(this.path)) { - throw new Error(this.path + ' does not exist') - } else { - lstat = fs.lstatSync(this.path) - if (!lstat.isDirectory()) { - throw new Error(this.path + ' is not a directory') - } + try { fs.accessSync(this.path) } catch (err) { + throw new Error(`${this.path} does not exist`) + } + + if (!fs.lstatSync(this.path).isDirectory()) { + throw new Error(`${this.path} is not a directory`) } - dirs = fs.readdirSync(this.path) - for (var i = 0; i < dirs.length; i++) { - name = dirs[i] - dir = path.join(this.path, name) - lstat = fs.lstatSync(dir) + fs.readdirSync(this.path).map((name) => { + const lstat = fs.lstatSync(path.join(this.path, name)) if (lstat.isDirectory() || lstat.isSymbolicLink()) { + console.log(name) this.templates[name] = new Template(this, name) } - } + }) } /* diff --git a/lib/template.js b/lib/template.js index 5b9e714..833a29a 100644 --- a/lib/template.js +++ b/lib/template.js @@ -43,7 +43,6 @@ class Template { constructor (sprout, name, src) { this.sprout = sprout - this.emitter = sprout.emitter this.name = name this.path = path.join(sprout.path, name) this.root = path.join(this.path, 'root') @@ -100,7 +99,7 @@ class Template { // If source is remote, clone into path. If source is local, symlink // to sprout's path. if (this.isRemote) { - this.emitter.emit('cmd', `git clone ${this.src} ${this.path}`) + this.sprout.emit('cmd', `git clone ${this.src} ${this.path}`) return exec(`git clone --recursive ${this.src} ${this.path}`) } @@ -154,19 +153,19 @@ class Template { }).then(() => { // If tag or version option passed, store current branch. if (options.tag || options.branch) { - this.emitter.emit('cmd', 'git rev-parse --abbrev-ref HEAD', this.path) + this.sprout.emit('cmd', 'git rev-parse --abbrev-ref HEAD', this.path) return exec('git rev-parse --abbrev-ref HEAD', { cwd: this.path }).spread((stdout) => { branch = stdout }) } }).then(() => { // If branch option passed, attempt to checkout to specified branch if (options.branch && branch) { - this.emitter.emit('cmd', 'git branch --list', this.path) + this.sprout.emit('cmd', 'git branch --list', this.path) return exec('git branch --list', { cwd: this.path }).spread((stdout) => { var branches if (stdout) { branches = _.compact(stdout.replace(/[\*]?\ +/g, '').split('\n')) if (_.includes(branches, options.branch)) { - this.emitter.emit('cmd', `git checkout ${options.branch}`, this.path) + this.sprout.emit('cmd', `git checkout ${options.branch}`, this.path) return exec(`git checkout ${options.branch}`, { cwd: this.path }) } } @@ -188,14 +187,14 @@ class Template { } }).then(() => { // Create the target directory. - this.emitter.emit('msg', `creating target directory: ${target}`) + this.sprout.emit('msg', `creating target directory: ${target}`) return fs.mkdir(target) }).then(() => { // Install npm dependencies, if present. Eventually, this should work with // package managers other than npm. var pkg = path.join(this.path, 'package.json') if (fs.existsSync(pkg)) { - this.emitter.emit('msg', 'installing npm dependencies') + this.sprout.emit('msg', 'installing npm dependencies') return exec('npm install', { cwd: this.path }) } }).then(() => { @@ -217,7 +216,7 @@ class Template { } try { - this.emitter.emit('msg', 'requiring ' + initPath) + this.sprout.emit('msg', 'requiring ' + initPath) init = require(initPath) } catch (error) { return reject(error) @@ -227,7 +226,7 @@ class Template { }).then(() => { // Run before hook if present. if (init.before) { - this.emitter.emit('msg', 'running before hook') + this.sprout.emit('msg', 'running before hook') return W(init.before(utils, config)) } }).then(() => { @@ -236,7 +235,7 @@ class Template { // If a config path is set, pull out its values and merge into config. if (options.config) { - this.emitter.emit('msg', `merging config: ${options.config}`) + this.sprout.emit('msg', `merging config: ${options.config}`) try { _.extend(config, /\.json$/.test(options.config) ? require(options.config) : yaml.safeLoad(fs.readFileSync(options.config, 'utf8'))) @@ -249,23 +248,23 @@ class Template { if (_.isFunction(options.questionnaire) && _.isArray(init.configure)) { // Run questionnaire, omitting keys already set in config return // answers merged with config values. - this.emitter.emit('msg', 'running questionnaire function') + this.sprout.emit('msg', 'running questionnaire function') return options.questionnaire(init.configure, _.keys(config)) .then((answers) => { return _.extend(config, answers) }) } }).then(() => { // Copy all files in root to target. - this.emitter.emit('msg', 'copying files in root to target') + this.sprout.emit('msg', 'copying files in root to target') return ncp(this.root, target) }).then(() => { // Run beforeRender hook if present. if (init.beforeRender) { - this.emitter.emit('msg', 'running beforeRender hook') + this.sprout.emit('msg', 'running beforeRender hook') return W(init.beforeRender(utils, config)) } }).then(() => { // Read target directory. - this.emitter.emit('msg', 'reading target directory') + this.sprout.emit('msg', 'reading target directory') return readdirp({ root: target, directoryFilter: ['!.git', '!node_modules'] @@ -287,13 +286,13 @@ class Template { // Write all files with our Utils class. return W.map(files, (file) => { - this.emitter.emit('msg', `reading ${file.fullPath}`) + this.sprout.emit('msg', `reading ${file.fullPath}`) // If the file is not a binary, render it. if (!isBinaryFile(file.fullPath)) { - this.emitter.emit('msg', 'reading ' + file.fullPath) + this.sprout.emit('msg', 'reading ' + file.fullPath) return utils.target.read(file.path).then((output) => { - this.emitter.emit('msg', 'writing ' + file.fullPath) + this.sprout.emit('msg', 'writing ' + file.fullPath) return utils.target.write(file.path, output, config) }) } @@ -301,13 +300,13 @@ class Template { }).then(() => { // Run after hook if present. if (init.after) { - this.emitter.emit('msg', 'running after hook') + this.sprout.emit('msg', 'running after hook') return W(init.after(utils, config)) } }).then(() => { // If original branch is stored, checkout to said branch. if (branch) { - this.emitter.emit('cmd', `git checkout ${branch}`, this.path) + this.sprout.emit('cmd', `git checkout ${branch}`, this.path) return exec(`git checkout ${branch}`, { cwd: this.path }) } }).catch((error) => { @@ -333,14 +332,14 @@ class Template { }).then(() => { // If remote origin exists, run `git remote update` to update the // repository. - this.emitter.emit('msg', 'updating template') - this.emitter.emit('cmd', 'git remote', this.path) + this.sprout.emit('msg', 'updating template') + this.sprout.emit('cmd', 'git remote', this.path) return exec('git remote', { cwd: this.path }).then((stdout) => { var origin if (stdout[0]) { origin = _.trim(stdout[0]) - this.emitter.emit('cmd', 'git pull ' + origin + ' HEAD', this.path) + this.sprout.emit('cmd', 'git pull ' + origin + ' HEAD', this.path) return exec('git pull ' + origin + ' HEAD', { cwd: this.path }) } }) @@ -387,7 +386,7 @@ class Template { } try { - this.emitter.emit('msg', `requiring '${generator}' generator`) + this.sprout.emit('msg', `requiring '${generator}' generator`) return resolve(require(generatorPath)) } catch (error) { return reject(error) @@ -402,7 +401,7 @@ class Template { (args = _.isArray(args) ? args : []).unshift(utils) // Call the generator pass the Utils instance and the arguments. - this.emitter.emit('msg', `running ${generator} generator`) + this.sprout.emit('msg', `running ${generator} generator`) return generator.apply(null, args) }).yield(this) } @@ -416,7 +415,7 @@ class Template { if (!fs.existsSync(this.path)) { return W.resolve() } // rm -rf the path. - this.emitter.emit('msg', 'removing template') + this.sprout.emit('msg', 'removing template') return fs.lstatAsync(this.path).then((stat) => { if (stat.isSymbolicLink()) { return fs.unlink(this.path) } From 4212076e252f18cdb499b4a741dc25e17d31fbbd Mon Sep 17 00:00:00 2001 From: kylemac Date: Fri, 18 Mar 2016 19:32:00 -0400 Subject: [PATCH 02/14] remove superflous console.log --- lib/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index bd72616..94ece72 100644 --- a/lib/index.js +++ b/lib/index.js @@ -27,7 +27,6 @@ class Sprout extends EventEmitter { fs.readdirSync(this.path).map((name) => { const lstat = fs.lstatSync(path.join(this.path, name)) if (lstat.isDirectory() || lstat.isSymbolicLink()) { - console.log(name) this.templates[name] = new Template(this, name) } }) From 78799d31db9fe0e24bb9884b81eaee182f396ee6 Mon Sep 17 00:00:00 2001 From: kylemac Date: Fri, 18 Mar 2016 19:32:12 -0400 Subject: [PATCH 03/14] pass linting --- lib/api/add.js | 4 +++- lib/api/remove.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/api/add.js b/lib/api/add.js index 3da13c2..a39c56c 100644 --- a/lib/api/add.js +++ b/lib/api/add.js @@ -9,5 +9,7 @@ import Template from '../template' */ export default function (sprout, name, src) { const template = new Template(sprout, name, src) - return template.save().then(template => sprout.templates[name] = template) + return template.save().then(function (template) { + sprout.templates[name] = template + }) } diff --git a/lib/api/remove.js b/lib/api/remove.js index 7b19d84..3be9e9d 100644 --- a/lib/api/remove.js +++ b/lib/api/remove.js @@ -9,7 +9,7 @@ import W from 'when' export default function (sprout, name) { const template = sprout.templates[name] if (template) { - return template.remove().then(_ => delete sprout.templates[name]) + return template.remove().then((_) => delete sprout.templates[name]) } return W.reject(new Error(`template ${name} does not exist`)) } From 3962b48339c439e9217cd9543a4300f7b1ee135e Mon Sep 17 00:00:00 2001 From: kylemac Date: Fri, 18 Mar 2016 20:25:53 -0400 Subject: [PATCH 04/14] es6ify + feross/standard'ify the readme --- readme.md | 95 ++++++++++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/readme.md b/readme.md index 26bdab0..12556ee 100644 --- a/readme.md +++ b/readme.md @@ -28,9 +28,8 @@ $ npm install sprout --save To construct a `Sprout` instance: ```javascript - -var Sprout = require('sprout') - , sprout = new Sprout('/path/where/templates/live'); +import Sprout from 'sprout' +const sprout = new Sprout('/path/where/templates/live') ``` A `Sprout` instance has the following public values: @@ -48,13 +47,11 @@ Each method returns an A+ promise. The promise, on success, returns the same sp Create a new template called `name` from `src`. ```javascript -var name = 'sprout-sprout'; -var src = 'git@github.com:carrot/sprout-sprout'; -sprout.add(name, src).then( - function (sprout) { - console.log('template added!'); - } -); +const name = 'sprout-sprout' +const src = 'git@github.com:carrot/sprout-sprout' +sprout.add(name, src).then((sprout) => + console.log('template added!') +) ``` #### sprout.remove(name) @@ -62,12 +59,10 @@ sprout.add(name, src).then( Remove an existing template called `name`. ```javascript -var name = 'sprout-sprout'; -sprout.remove(name).then( - function (sprout) { - console.log('template removed!'); - } -); +const name = 'sprout-sprout' +sprout.remove(name).then((sprout) => + console.log('template removed!') +) ``` #### sprout.init(name, target, options) @@ -75,8 +70,8 @@ sprout.remove(name).then( Use a template called `name` and save instance to `target`. ```javascript -var name = 'sprout-sprout'; -var target = '~/Projects/sprout-sprout-instance'; +const name = 'sprout-sprout' +const target = '~/Projects/sprout-sprout-instance' /* * Options: @@ -86,17 +81,15 @@ var target = '~/Projects/sprout-sprout-instance'; * config {String} - Path to a JSON or yaml file with pre-defined values. */ -var options = { +const options = { locals: { foo: 'bar' } -}; +} -sprout.init(name, target, options).then( - function (sprout) { - console.log('template generated!'); - } -); +sprout.init(name, target, options).then((sprout) => + console.log('template generated!') +) ``` #### sprout.run(name, target, generator, args) @@ -104,14 +97,12 @@ sprout.init(name, target, options).then( Run a generator in a template called `name` and on template instance instance at `target`, optionally with an array of `args`. ```javascript -var name = 'mvc' -var target = '~/Projects/mvc-instance'; -var generator = 'model'; -sprout.run(name, target, generator, ['User']).then( - function (sprout) { - console.log('a model named `User` was created!'); - } -); +const name = 'mvc' +const target = '~/Projects/mvc-instance' +const generator = 'model' +sprout.run(name, target, generator, ['User']).then((sprout) => + console.log('a model named `User` was created!') +) ``` ## Writing Your Own Templates @@ -139,7 +130,6 @@ First thing you'll want to do is set up your project structure, which will proba `init.js` (or `init.coffee`) sets the hooks and configuration for your sprout template. ```javascript - /* * This function is executed before any of the configuration happens. * It's a good place to put any introductory messages you want to display. @@ -147,7 +137,7 @@ First thing you'll want to do is set up your project structure, which will proba */ exports.before = function (utils) { - console.log('Getting started...'); + console.log('Getting started...') } /* @@ -173,9 +163,9 @@ exports.configure = [ message: 'What is your github handle?' }, { - type: "confirm", - name: "travis", - message: "Do you want to utilize Travis CI?", + type: 'confirm', + name: 'travis', + message: 'Do you want to utilize Travis CI?', default: false } ] @@ -187,11 +177,10 @@ exports.configure = [ */ exports.beforeRender = function (utils, config) { - config.foo = 'bar'; - return utils.target.write('foo.jade', 'h1 Hello World!', config); + config.foo = 'bar' + return utils.target.write('foo.jade', 'h1 Hello World!', config) } - /* * This function is executed after the templates are rendered. It's a good place * to do any other custom config you need, like building extra files etc. You @@ -199,21 +188,21 @@ exports.beforeRender = function (utils, config) { */ exports.after = function (utils, config) { - return utils.target.rename('foo.jade', 'bar.jade'); + return utils.target.rename('foo.jade', 'bar.jade') } /* * Optionally specify globs to ignore. */ - exports.ignore = ['foo.*']; +exports.ignore = ['fizz.*'] - /* - * Optionally specify a defaults object to your templates. - * Helpful for adding locals to use within your templates - */ +/* +* Optionally specify a defaults object to your templates. +* Helpful for adding locals to use within your templates +*/ - exports.defaults = { moment: require('moment') } +exports.defaults = { moment: require('moment') } ``` @@ -254,18 +243,16 @@ Sprout templates may also include "generators": small scripts to be executed on ```javascript module.exports = function (utils, name) { - return utils.src.read('templates/model').then( - function (output) { - return utils.target.write('lib/models/' + name + '.js', output, {name: name}); - } - ) + return utils.src.read('templates/model').then(function (output) { + return utils.target.write('lib/models/' + name + '.js', output, {name: name}) + }) } ``` These generators are stored in your template's `generators` folder and can be used with sprout's `run` method: ```javascript -sprout.run('mvc', 'model', ['User']); +sprout.run('mvc', 'model', ['User']) ``` ### Versioning Templates From 93da954f8273ab55d0e726e0fbc9ea1d33f7edc0 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Fri, 18 Mar 2016 21:33:30 -0400 Subject: [PATCH 05/14] various syntax and logic fixes --- lib/template.js | 298 ++++++++++++++++++++++++------------------------ lib/utils.js | 7 +- package.json | 3 +- test/test.js | 1 + 4 files changed, 156 insertions(+), 153 deletions(-) diff --git a/lib/template.js b/lib/template.js index 833a29a..f49b66c 100644 --- a/lib/template.js +++ b/lib/template.js @@ -6,7 +6,7 @@ import which from 'which' import path from 'path' import yaml from 'js-yaml' import minimatch from 'minimatch' -import _ from 'lodash' +import lodash from 'lodash' import _rimraf from 'rimraf' import _readdirp from 'readdirp' import _fs from 'fs' @@ -25,18 +25,18 @@ const fs = node.liftAll(_fs, (pfs, lifted, name) => { const dns = node.liftAll(_dns) /* - * Register CoffeeScript `init` file in template - * can be either CoffeeScript or JavaScript. + * Register CoffeeScript `init` file in template can be either CoffeeScript or + * JavaScript. */ require('coffee-script/register') /* - * Given a Sprout instance and a name, returns a - * Template instance. + * Given a Sprout instance and a name, returns a Template instance. * @param {Function} sprout - Sprout instance. * @param {String} name - Name of template - * @param {String} src (optional) - Path or URL to source. Only required for `template.save`. + * @param {String} src (optional) - Path or URL to source. Only required for + * `template.save`. * @return {Function} - Template instance. */ class Template { @@ -49,9 +49,8 @@ class Template { this.generators = path.join(this.path, 'generators') /* - * If `src` is set, use `isGitURL` helper to - * determine whether `src` is a local path or - * a remote URL. + * If `src` is set, use `isGitURL` helper to determine whether `src` is a + * local path or a remote URL. */ if (src) { this.src = src @@ -64,38 +63,42 @@ class Template { * @return {Promise} - promise for Template instance. */ save () { - return W.promise((resolve, reject) => { - // Check if `src` (path or URL) is provided. - if (!this.src) { return reject(new Error('no source provided')) } - + return W.resolve().then(_ => { + // TODO name: ensureGitInstall // Check if git is installed if (!which.sync('git')) { - return reject(new Error('you need to have git installed')) + throw new Error('you need to have git installed') } - return resolve() - }).then(() => { - // If source is remote, try to resolve google.com to make sure internet - // connection exists. - if (this.isRemote) { - return dns.resolve('google.com').catch(function () { - throw new Error('make sure that you are connected to the internet!') - }) - } + // TODO name: validateSource + // Check if `src` (path or URL) is provided. + if (!this.src) { throw new Error('no source provided') } // If source is local, make sure path exists. - if (!fs.existsSync(this.src)) { + if (!this.isRemote && !fs.existsSync(this.src)) { throw new Error(`there is no sprout template located at ${this.src}`) } // If source is local, make sure it's a git repo. - if (!fs.existsSync(path.join(this.src, '.git'))) { - throw new Error(this.src + ' is not a git repository') + if (!this.isRemote && !fs.existsSync(path.join(this.src, '.git'))) { + throw new Error(`${this.src} is not a git repository`) + } + + // TODO name: ensureInternetConnectionIfRemote + // TODO is this needed? could replace with timeout + // If source is remote, try to resolve google.com to make sure internet + // connection exists. + if (this.isRemote) { + return dns.resolve('google.com').catch(function () { + throw new Error('make sure that you are connected to the internet!') + }) } - }).then(() => { + }).then(_ => { + // TODO: name: removeTemplate // Remove existing template with the same name. return this.remove() - }).then(() => { + }).then(_ => { + // TODO: name: moveSourceToSproutPath // If source is remote, clone into path. If source is local, symlink // to sprout's path. if (this.isRemote) { @@ -104,176 +107,182 @@ class Template { } return fs.symlinkAsync(this.src, this.path) - }).then(() => { + }).then(_ => { + // TODO: name: validateTemplate // Anything occuring beyond this point which throws an error should // trigger the removal of the template directory! - return W.promise((resolve, reject) => { + return W.resolve().then(_ => { // Check for init.js or init.coffee. if (!fs.existsSync(path.join(this.path, 'init.js')) && !fs.existsSync(path.join(this.path, 'init.coffee'))) { - return reject(new Error('neither init.coffee nor init.js exist in this template')) + throw new Error('neither init.coffee nor init.js exist in this template') } // Check for root path. if (!fs.existsSync(this.root)) { - return reject(new Error('root path doesn\'t exist in template')) + throw new Error('root path doesn\'t exist in template') } - - return resolve() }).catch((error) => { - // Remove tenokate directory if Sprout created it and an error is + // Remove template directory if Sprout created it and an error is // thrown. - return rimraf(this.path).then(() => { throw error }) + return rimraf(this.path).then(_ => { throw error }) }) }).yield(this) } /* * Initialize template and save to `target` - * param {String} target - The path to save the template to. + * @param {String} target - The path to save the template to. * @return {Promise} - Promise for Template instance. */ - - init (target, opts) { - var options = (opts || {}) + init (target, opts = {}) { + // TODO: validate options with joi var utils = new Utils(this.path, target) var config = {} var branch var init - return this.update().then(() => { + + return this.update().then(_ => { + // TODO name: validateTarget // If root directory doesn't exist in template, pass an error. + // TODO: remove this, checked already in `save`. if (!fs.existsSync(this.root)) { throw new Error(`root path doesn't exist in ${this.name}`) } // If target not passed, throw an error. + // TODO: remove this, validate with joi if (!target) { throw new Error('target path required') } // If target directory exists, throw an error. if (fs.existsSync(target)) { throw new Error(`${target} already exists`) } - }).then(() => { + }).then(_ => { + // TODO name: handleBranchOrTagOption // If tag or version option passed, store current branch. - if (options.tag || options.branch) { + if (opts.tag || opts.branch) { this.sprout.emit('cmd', 'git rev-parse --abbrev-ref HEAD', this.path) return exec('git rev-parse --abbrev-ref HEAD', { cwd: this.path }).spread((stdout) => { branch = stdout }) } - }).then(() => { + }).then(_ => { + // TODO name: handleBranchOrTagOption // If branch option passed, attempt to checkout to specified branch - if (options.branch && branch) { + if (opts.branch && branch) { this.sprout.emit('cmd', 'git branch --list', this.path) - return exec('git branch --list', { cwd: this.path }).spread((stdout) => { - var branches - if (stdout) { - branches = _.compact(stdout.replace(/[\*]?\ +/g, '').split('\n')) - if (_.includes(branches, options.branch)) { - this.sprout.emit('cmd', `git checkout ${options.branch}`, this.path) - return exec(`git checkout ${options.branch}`, { cwd: this.path }) + return exec('git branch --list', { cwd: this.path }) + .spread((stdout) => { + if (!stdout) { return true } + const branches = lodash.compact(stdout.replace(/[\*]?\ +/g, '').split('\n')) + if (lodash.includes(branches, opts.branch)) { + this.sprout.emit('cmd', `git checkout ${opts.branch}`, this.path) + return exec(`git checkout ${opts.branch}`, { cwd: this.path }) } - } - }) + }) } - }).then(() => { + }).then(_ => { + // TODO name: handleBranchOrTagOption // If tag option passed, attempt to checkout to specified tag - if (options.tag && branch) { + if (opts.tag && branch) { return exec('git tag --list', { cwd: this.path }).spread((stdout) => { - var tags if (stdout) { - tags = _.compact(stdout.split('\n')) - if (_.includes(tags, options.tag)) { - return exec(`git checkout tags/${options.tag}`, { cwd: this.path }) + const tags = lodash.compact(stdout.split('\n')) + if (lodash.includes(tags, opts.tag)) { + return exec(`git checkout tags/${opts.tag}`, { cwd: this.path }) } } - throw new Error(`tag '${options.tag}' does not exist`) + throw new Error(`tag '${opts.tag}' does not exist`) }) } - }).then(() => { + }).then(_ => { + // TODO name: createTargetDirectory // Create the target directory. this.sprout.emit('msg', `creating target directory: ${target}`) return fs.mkdir(target) - }).then(() => { + }).then(_ => { + // TODO name: installDependenciesIfPresent // Install npm dependencies, if present. Eventually, this should work with // package managers other than npm. - var pkg = path.join(this.path, 'package.json') - if (fs.existsSync(pkg)) { + if (fs.existsSync(path.join(this.path, 'package.json'))) { this.sprout.emit('msg', 'installing npm dependencies') return exec('npm install', { cwd: this.path }) } - }).then(() => { + }).then(_ => { // Anything occuring beyond this point which throws an error should // trigger the removal of target directory! - return W.promise((resolve, reject) => { + return W.resolve().then(_ => { + // TODO name: loadConfigFile // Check for init.js. If init.js doesn't exist, confirm that // init.coffee exists. Require init once this is determined. - var initCoffee = path.join(this.path, 'init.coffee') - var initJS = path.join(this.path, 'init.js') - var initPath + const initCoffee = path.join(this.path, 'init.coffee') + const initJS = path.join(this.path, 'init.js') + let initPath if (fs.existsSync(initJS)) { initPath = initJS } else if (fs.existsSync(initCoffee)) { initPath = initCoffee } else { - return reject(new Error('neither init.coffee nor init.js exist')) - } - - try { - this.sprout.emit('msg', 'requiring ' + initPath) - init = require(initPath) - } catch (error) { - return reject(error) + throw new Error('neither init.coffee nor init.js exist') } - return resolve() - }).then(() => { + this.sprout.emit('msg', 'requiring ' + initPath) + init = require(initPath) + }).then(_ => { + // TODO name: runBeforeHook // Run before hook if present. if (init.before) { this.sprout.emit('msg', 'running before hook') return W(init.before(utils, config)) } - }).then(() => { + }).then(_ => { + // TODO name: mergeConfig // Merge in all known values for config. - _.extend(config, init.defaults, options.locals) + lodash.assign(config, init.defaults, opts.locals) // If a config path is set, pull out its values and merge into config. - if (options.config) { - this.sprout.emit('msg', `merging config: ${options.config}`) + if (opts.config) { + this.sprout.emit('msg', `merging config: ${opts.config}`) try { - _.extend(config, /\.json$/.test(options.config) ? require(options.config) - : yaml.safeLoad(fs.readFileSync(options.config, 'utf8'))) + lodash.assign(config, /\.json$/.test(opts.config) ? require(opts.config) + : yaml.safeLoad(fs.readFileSync(opts.config, 'utf8'))) } catch (error) { - throw new Error(`could not open configuration file ${options.config}`) + throw new Error(`could not open configuration file ${opts.config}`) } } + // TODO name: runPrompts // If questionnaire function exists, run it to get answers. - if (_.isFunction(options.questionnaire) && _.isArray(init.configure)) { + if (lodash.isFunction(opts.questionnaire) && lodash.isArray(init.configure)) { // Run questionnaire, omitting keys already set in config return // answers merged with config values. this.sprout.emit('msg', 'running questionnaire function') - return options.questionnaire(init.configure, _.keys(config)) - .then((answers) => { return _.extend(config, answers) }) + return opts.questionnaire(init.configure, lodash.keys(config)) + .then((answers) => { return lodash.assign(config, answers) }) } - }).then(() => { + }).then(_ => { + // TODO name: copyTemplateToTarget // Copy all files in root to target. this.sprout.emit('msg', 'copying files in root to target') return ncp(this.root, target) - }).then(() => { + }).then(_ => { + // TODO name: runBeforeRenderHook // Run beforeRender hook if present. if (init.beforeRender) { this.sprout.emit('msg', 'running beforeRender hook') return W(init.beforeRender(utils, config)) } - }).then(() => { + }).then(_ => { + // TODO name: readTemplateFiles // Read target directory. this.sprout.emit('msg', 'reading target directory') return readdirp({ root: target, directoryFilter: ['!.git', '!node_modules'] }) - }).then((result) => { + }).then(result => { + // TODO name: removeIgnoredFiles // Remove ignored files. - var files = _.filter(result.files, (file) => { + const files = lodash.filter(result.files, (file) => { if (init.ignore) { - init.ignore = _.isArray(init.ignore) ? init.ignore + init.ignore = lodash.isArray(init.ignore) ? init.ignore : [init.ignore] for (var i = 0; i < init.ignore.length; i++) { if (minimatch(file.path, init.ignore[i])) { @@ -284,35 +293,39 @@ class Template { return true }) + // TODO name: writeTemplateFiles // Write all files with our Utils class. - return W.map(files, (file) => { + return W.map(files, file => { this.sprout.emit('msg', `reading ${file.fullPath}`) // If the file is not a binary, render it. if (!isBinaryFile(file.fullPath)) { - this.sprout.emit('msg', 'reading ' + file.fullPath) - return utils.target.read(file.path).then((output) => { - this.sprout.emit('msg', 'writing ' + file.fullPath) + this.sprout.emit('msg', `reading ${file.fullPath}`) + return utils.target.read(file.path).then(output => { + this.sprout.emit('msg', `writing ${file.fullPath}`) return utils.target.write(file.path, output, config) }) } }) - }).then(() => { + }).then(_ => { + // TODO name: runAfterHook // Run after hook if present. if (init.after) { this.sprout.emit('msg', 'running after hook') return W(init.after(utils, config)) } - }).then(() => { + }).then(_ => { + // TODO name: checkoutBranchIfNecessary // If original branch is stored, checkout to said branch. if (branch) { this.sprout.emit('cmd', `git checkout ${branch}`, this.path) return exec(`git checkout ${branch}`, { cwd: this.path }) } - }).catch((error) => { + }).catch(error => { + // TODO name: removeTargetDirectory // Remove target directory if Sprout created it and an error is // thrown. - return rimraf(target).then(() => { throw error }) + return rimraf(target).then(_ => { throw error }) }) }).yield(this) } @@ -322,27 +335,21 @@ class Template { * @return {Promise} - Promise for Template instance. */ update () { - return W.promise((resolve, reject) => { - // Confirm template is a git repository. If not, throw an error. - if (!fs.existsSync(path.join(this.path, '.git'))) { - return reject(new Error(`${this.name} is not a git repository`)) - } + // Confirm template is a git repository. If not, throw an error. + if (!fs.existsSync(path.join(this.path, '.git'))) { + return W.reject(new Error(`${this.name} is not a git repository`)) + } - return resolve() - }).then(() => { - // If remote origin exists, run `git remote update` to update the - // repository. - this.sprout.emit('msg', 'updating template') - this.sprout.emit('cmd', 'git remote', this.path) - - return exec('git remote', { cwd: this.path }).then((stdout) => { - var origin - if (stdout[0]) { - origin = _.trim(stdout[0]) - this.sprout.emit('cmd', 'git pull ' + origin + ' HEAD', this.path) - return exec('git pull ' + origin + ' HEAD', { cwd: this.path }) - } - }) + // If remote origin exists, run `git remote update` to update the + // repository. + this.sprout.emit('msg', 'updating template') + this.sprout.emit('cmd', 'git remote', this.path) + + return exec('git remote', { cwd: this.path }).then(stdout => { + if (!stdout[0]) { return true } + const origin = lodash.trim(stdout[0]) + this.sprout.emit('cmd', `git pull ${origin} HEAD`, this.path) + return exec(`git pull ${origin} HEAD`, { cwd: this.path }) }).yield(this) } @@ -350,57 +357,52 @@ class Template { * Run a template generator in the specified target. * @param {String} target - the target path. * @param {String} name - the name of the generator to use. - * @param + * @param {Object} args - arguments to be passed to the generator * @return {Promise} - Promise for Template instance. */ run (target, generator, args) { - return W.promise((resolve, reject) => { + return W.resolve().then(_ => { + // TODO: replace with joi validation, name validateArgs // If target not passed, throw an error. - if (!target) { - return reject(new Error('target path required')) - } + if (!target) { throw new Error('target path required') } // If target directory doesn't exist, throw an error. if (!fs.existsSync(target)) { - return reject(new Error(target + ' does not exist')) + throw new Error(`${target} does not exist`) } // If generator name isn't passed, throw an error. - if (!generator) { - return reject(new Error('generator name required')) - } + if (!generator) { throw new Error('generator name required') } + // TODO name: validateGenerator // Check for {generator}.js. If {generator}.js doesn't exist, confirm // that {name}.coffee exists. Require {generator}.js or {generator}.coffee // once this is determined. - var generatorCoffee = path.join(this.generators, generator + '.coffee') - var generatorJs = path.join(this.generators, generator + '.js') - var generatorPath + const generatorCoffee = path.join(this.generators, `${generator}.coffee`) + const generatorJs = path.join(this.generators, `${generator}.js`) + let generatorPath if (fs.existsSync(generatorJs)) { generatorPath = generatorJs } else if (fs.existsSync(generatorCoffee)) { generatorPath = generatorCoffee } else { - return reject(new Error(`'${generator}' is not a generator in this template`)) + throw new Error(`'${generator}' is not a generator in this template`) } - try { - this.sprout.emit('msg', `requiring '${generator}' generator`) - return resolve(require(generatorPath)) - } catch (error) { - return reject(error) - } - }).then((generator) => { + this.sprout.emit('msg', `requiring '${generator}' generator`) + return require(generatorPath) + }).then(generator => { + // TODO name: executeGenerator // Create a Utils instance where the `src` and `target` are both the // target directory. var utils = new Utils(this.path, target) // Add `utils` as the first object in our `args` array. var args - (args = _.isArray(args) ? args : []).unshift(utils) + (args = lodash.isArray(args) ? args : []).unshift(utils) - // Call the generator pass the Utils instance and the arguments. + // Call the generator, pass the Utils instance and the arguments. this.sprout.emit('msg', `running ${generator} generator`) return generator.apply(null, args) }).yield(this) @@ -412,12 +414,12 @@ class Template { */ remove () { // Resolve if path does not exist. - if (!fs.existsSync(this.path)) { return W.resolve() } + if (!fs.existsSync(this.path)) { return W.resolve(this) } // rm -rf the path. this.sprout.emit('msg', 'removing template') - return fs.lstatAsync(this.path).then((stat) => { + return fs.lstatAsync(this.path).then(stat => { if (stat.isSymbolicLink()) { return fs.unlink(this.path) } return rimraf(this.path) }).yield(this) diff --git a/lib/utils.js b/lib/utils.js index 2aa8817..0e6c08c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -23,7 +23,6 @@ const mkdirp = node.lift(_mkdirp) * @param {String} target - The template destination path. * @return {Function} - Template instance. */ - const Utils = function (src, target) { /* * Copy a file from one path relative to the src, to another relative to the @@ -32,7 +31,6 @@ const Utils = function (src, target) { * @param {String} to - the path to copy to. * @return {Promise} - promise to copy */ - this.copy = function (from, to) { return copy(path.resolve(src, from), path.resolve(target, to)) } @@ -152,7 +150,7 @@ function read (from) { * @return {String} - the contents of the file. */ function write (to, what, locals) { - var content = ejs.render(what, _.extend({}, (locals || {}), { S: underscoreString })) + const content = ejs.render(what, _.extend({}, (locals || {}), { S: underscoreString })) return mkdirp(path.dirname(to)).then(() => { return fs.writeFileAsync(to, content, 'utf8') }) @@ -175,7 +173,8 @@ function rename (from, to) { * @return {Promise} - a promise to remove the file */ function remove (from, what) { - if (!_.isArray(what)) what = [what] + // TODO array coerce trick + if (!_.isArray(what)) { what = [what] } return W.map(what, (p) => { return fs.unlinkSync(path.resolve(from, p)) } diff --git a/package.json b/package.json index 3a52a91..0634cac 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "standard": { "ignore": [ "/test/*" - ] + ], + "parser": "babel-eslint" } } diff --git a/test/test.js b/test/test.js index d974454..6647df0 100644 --- a/test/test.js +++ b/test/test.js @@ -589,6 +589,7 @@ describe('template', } ).catch( function (error) { + console.log('here we are') fs.existsSync(template.path).should.be.false error.toString().should.eq('Error: neither init.coffee nor init.js exist in this template') done() From d2f2cb5b730281411d44074c6e9241a86b78dec9 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Mon, 21 Mar 2016 15:48:22 -0400 Subject: [PATCH 06/14] fix lint errors --- lib/template.js | 60 ++++++++++++++++++++++++------------------------- package.json | 3 +-- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/lib/template.js b/lib/template.js index f49b66c..957b0b7 100644 --- a/lib/template.js +++ b/lib/template.js @@ -63,7 +63,7 @@ class Template { * @return {Promise} - promise for Template instance. */ save () { - return W.resolve().then(_ => { + return W.resolve().then(() => { // TODO name: ensureGitInstall // Check if git is installed if (!which.sync('git')) { @@ -93,11 +93,11 @@ class Template { throw new Error('make sure that you are connected to the internet!') }) } - }).then(_ => { + }).then(() => { // TODO: name: removeTemplate // Remove existing template with the same name. return this.remove() - }).then(_ => { + }).then(() => { // TODO: name: moveSourceToSproutPath // If source is remote, clone into path. If source is local, symlink // to sprout's path. @@ -107,11 +107,11 @@ class Template { } return fs.symlinkAsync(this.src, this.path) - }).then(_ => { + }).then(() => { // TODO: name: validateTemplate // Anything occuring beyond this point which throws an error should // trigger the removal of the template directory! - return W.resolve().then(_ => { + return W.resolve().then(() => { // Check for init.js or init.coffee. if (!fs.existsSync(path.join(this.path, 'init.js')) && !fs.existsSync(path.join(this.path, 'init.coffee'))) { throw new Error('neither init.coffee nor init.js exist in this template') @@ -124,7 +124,7 @@ class Template { }).catch((error) => { // Remove template directory if Sprout created it and an error is // thrown. - return rimraf(this.path).then(_ => { throw error }) + return rimraf(this.path).then(() => { throw error }) }) }).yield(this) } @@ -141,7 +141,7 @@ class Template { var branch var init - return this.update().then(_ => { + return this.update().then(() => { // TODO name: validateTarget // If root directory doesn't exist in template, pass an error. // TODO: remove this, checked already in `save`. @@ -155,14 +155,14 @@ class Template { // If target directory exists, throw an error. if (fs.existsSync(target)) { throw new Error(`${target} already exists`) } - }).then(_ => { + }).then(() => { // TODO name: handleBranchOrTagOption // If tag or version option passed, store current branch. if (opts.tag || opts.branch) { this.sprout.emit('cmd', 'git rev-parse --abbrev-ref HEAD', this.path) return exec('git rev-parse --abbrev-ref HEAD', { cwd: this.path }).spread((stdout) => { branch = stdout }) } - }).then(_ => { + }).then(() => { // TODO name: handleBranchOrTagOption // If branch option passed, attempt to checkout to specified branch if (opts.branch && branch) { @@ -177,7 +177,7 @@ class Template { } }) } - }).then(_ => { + }).then(() => { // TODO name: handleBranchOrTagOption // If tag option passed, attempt to checkout to specified tag if (opts.tag && branch) { @@ -191,12 +191,12 @@ class Template { throw new Error(`tag '${opts.tag}' does not exist`) }) } - }).then(_ => { + }).then(() => { // TODO name: createTargetDirectory // Create the target directory. this.sprout.emit('msg', `creating target directory: ${target}`) return fs.mkdir(target) - }).then(_ => { + }).then(() => { // TODO name: installDependenciesIfPresent // Install npm dependencies, if present. Eventually, this should work with // package managers other than npm. @@ -204,10 +204,10 @@ class Template { this.sprout.emit('msg', 'installing npm dependencies') return exec('npm install', { cwd: this.path }) } - }).then(_ => { + }).then(() => { // Anything occuring beyond this point which throws an error should // trigger the removal of target directory! - return W.resolve().then(_ => { + return W.resolve().then(() => { // TODO name: loadConfigFile // Check for init.js. If init.js doesn't exist, confirm that // init.coffee exists. Require init once this is determined. @@ -225,14 +225,14 @@ class Template { this.sprout.emit('msg', 'requiring ' + initPath) init = require(initPath) - }).then(_ => { + }).then(() => { // TODO name: runBeforeHook // Run before hook if present. if (init.before) { this.sprout.emit('msg', 'running before hook') return W(init.before(utils, config)) } - }).then(_ => { + }).then(() => { // TODO name: mergeConfig // Merge in all known values for config. lodash.assign(config, init.defaults, opts.locals) @@ -257,19 +257,19 @@ class Template { return opts.questionnaire(init.configure, lodash.keys(config)) .then((answers) => { return lodash.assign(config, answers) }) } - }).then(_ => { + }).then(() => { // TODO name: copyTemplateToTarget // Copy all files in root to target. this.sprout.emit('msg', 'copying files in root to target') return ncp(this.root, target) - }).then(_ => { + }).then(() => { // TODO name: runBeforeRenderHook // Run beforeRender hook if present. if (init.beforeRender) { this.sprout.emit('msg', 'running beforeRender hook') return W(init.beforeRender(utils, config)) } - }).then(_ => { + }).then(() => { // TODO name: readTemplateFiles // Read target directory. this.sprout.emit('msg', 'reading target directory') @@ -277,7 +277,7 @@ class Template { root: target, directoryFilter: ['!.git', '!node_modules'] }) - }).then(result => { + }).then((result) => { // TODO name: removeIgnoredFiles // Remove ignored files. const files = lodash.filter(result.files, (file) => { @@ -295,37 +295,37 @@ class Template { // TODO name: writeTemplateFiles // Write all files with our Utils class. - return W.map(files, file => { + return W.map(files, (file) => { this.sprout.emit('msg', `reading ${file.fullPath}`) // If the file is not a binary, render it. if (!isBinaryFile(file.fullPath)) { this.sprout.emit('msg', `reading ${file.fullPath}`) - return utils.target.read(file.path).then(output => { + return utils.target.read(file.path).then((output) => { this.sprout.emit('msg', `writing ${file.fullPath}`) return utils.target.write(file.path, output, config) }) } }) - }).then(_ => { + }).then(() => { // TODO name: runAfterHook // Run after hook if present. if (init.after) { this.sprout.emit('msg', 'running after hook') return W(init.after(utils, config)) } - }).then(_ => { + }).then(() => { // TODO name: checkoutBranchIfNecessary // If original branch is stored, checkout to said branch. if (branch) { this.sprout.emit('cmd', `git checkout ${branch}`, this.path) return exec(`git checkout ${branch}`, { cwd: this.path }) } - }).catch(error => { + }).catch((error) => { // TODO name: removeTargetDirectory // Remove target directory if Sprout created it and an error is // thrown. - return rimraf(target).then(_ => { throw error }) + return rimraf(target).then(() => { throw error }) }) }).yield(this) } @@ -345,7 +345,7 @@ class Template { this.sprout.emit('msg', 'updating template') this.sprout.emit('cmd', 'git remote', this.path) - return exec('git remote', { cwd: this.path }).then(stdout => { + return exec('git remote', { cwd: this.path }).then((stdout) => { if (!stdout[0]) { return true } const origin = lodash.trim(stdout[0]) this.sprout.emit('cmd', `git pull ${origin} HEAD`, this.path) @@ -361,7 +361,7 @@ class Template { * @return {Promise} - Promise for Template instance. */ run (target, generator, args) { - return W.resolve().then(_ => { + return W.resolve().then(() => { // TODO: replace with joi validation, name validateArgs // If target not passed, throw an error. if (!target) { throw new Error('target path required') } @@ -392,7 +392,7 @@ class Template { this.sprout.emit('msg', `requiring '${generator}' generator`) return require(generatorPath) - }).then(generator => { + }).then((generator) => { // TODO name: executeGenerator // Create a Utils instance where the `src` and `target` are both the // target directory. @@ -419,7 +419,7 @@ class Template { // rm -rf the path. this.sprout.emit('msg', 'removing template') - return fs.lstatAsync(this.path).then(stat => { + return fs.lstatAsync(this.path).then((stat) => { if (stat.isSymbolicLink()) { return fs.unlink(this.path) } return rimraf(this.path) }).yield(this) diff --git a/package.json b/package.json index 0634cac..3a52a91 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "standard": { "ignore": [ "/test/*" - ], - "parser": "babel-eslint" + ] } } From 13a1aea664fb01e7a2057b477619d79c8d9d1707 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Mon, 21 Mar 2016 18:38:32 -0400 Subject: [PATCH 07/14] that named function life tho --- lib/template.js | 497 +++++++++++++++++++++++------------------------- test/test.js | 5 +- 2 files changed, 244 insertions(+), 258 deletions(-) diff --git a/lib/template.js b/lib/template.js index 957b0b7..0338df9 100644 --- a/lib/template.js +++ b/lib/template.js @@ -63,70 +63,14 @@ class Template { * @return {Promise} - promise for Template instance. */ save () { - return W.resolve().then(() => { - // TODO name: ensureGitInstall - // Check if git is installed - if (!which.sync('git')) { - throw new Error('you need to have git installed') - } - - // TODO name: validateSource - // Check if `src` (path or URL) is provided. - if (!this.src) { throw new Error('no source provided') } - - // If source is local, make sure path exists. - if (!this.isRemote && !fs.existsSync(this.src)) { - throw new Error(`there is no sprout template located at ${this.src}`) - } - - // If source is local, make sure it's a git repo. - if (!this.isRemote && !fs.existsSync(path.join(this.src, '.git'))) { - throw new Error(`${this.src} is not a git repository`) - } - - // TODO name: ensureInternetConnectionIfRemote - // TODO is this needed? could replace with timeout - // If source is remote, try to resolve google.com to make sure internet - // connection exists. - if (this.isRemote) { - return dns.resolve('google.com').catch(function () { - throw new Error('make sure that you are connected to the internet!') - }) - } - }).then(() => { - // TODO: name: removeTemplate - // Remove existing template with the same name. - return this.remove() - }).then(() => { - // TODO: name: moveSourceToSproutPath - // If source is remote, clone into path. If source is local, symlink - // to sprout's path. - if (this.isRemote) { - this.sprout.emit('cmd', `git clone ${this.src} ${this.path}`) - return exec(`git clone --recursive ${this.src} ${this.path}`) - } - - return fs.symlinkAsync(this.src, this.path) - }).then(() => { - // TODO: name: validateTemplate - // Anything occuring beyond this point which throws an error should - // trigger the removal of the template directory! - return W.resolve().then(() => { - // Check for init.js or init.coffee. - if (!fs.existsSync(path.join(this.path, 'init.js')) && !fs.existsSync(path.join(this.path, 'init.coffee'))) { - throw new Error('neither init.coffee nor init.js exist in this template') - } - - // Check for root path. - if (!fs.existsSync(this.root)) { - throw new Error('root path doesn\'t exist in template') - } - }).catch((error) => { - // Remove template directory if Sprout created it and an error is - // thrown. - return rimraf(this.path).then(() => { throw error }) - }) - }).yield(this) + return W.resolve().with(this) + .then(ensureGitInstall) + .then(validateSource) + .then(ensureInternetConnection) + .then(removeTemplate) + .then(moveSourceToSproutPath) + .then(validateTemplate) + .yield(this) } /* @@ -137,197 +81,27 @@ class Template { init (target, opts = {}) { // TODO: validate options with joi var utils = new Utils(this.path, target) - var config = {} - var branch - var init - - return this.update().then(() => { - // TODO name: validateTarget - // If root directory doesn't exist in template, pass an error. - // TODO: remove this, checked already in `save`. - if (!fs.existsSync(this.root)) { - throw new Error(`root path doesn't exist in ${this.name}`) - } - - // If target not passed, throw an error. - // TODO: remove this, validate with joi - if (!target) { throw new Error('target path required') } - - // If target directory exists, throw an error. - if (fs.existsSync(target)) { throw new Error(`${target} already exists`) } - }).then(() => { - // TODO name: handleBranchOrTagOption - // If tag or version option passed, store current branch. - if (opts.tag || opts.branch) { - this.sprout.emit('cmd', 'git rev-parse --abbrev-ref HEAD', this.path) - return exec('git rev-parse --abbrev-ref HEAD', { cwd: this.path }).spread((stdout) => { branch = stdout }) - } - }).then(() => { - // TODO name: handleBranchOrTagOption - // If branch option passed, attempt to checkout to specified branch - if (opts.branch && branch) { - this.sprout.emit('cmd', 'git branch --list', this.path) - return exec('git branch --list', { cwd: this.path }) - .spread((stdout) => { - if (!stdout) { return true } - const branches = lodash.compact(stdout.replace(/[\*]?\ +/g, '').split('\n')) - if (lodash.includes(branches, opts.branch)) { - this.sprout.emit('cmd', `git checkout ${opts.branch}`, this.path) - return exec(`git checkout ${opts.branch}`, { cwd: this.path }) - } - }) - } - }).then(() => { - // TODO name: handleBranchOrTagOption - // If tag option passed, attempt to checkout to specified tag - if (opts.tag && branch) { - return exec('git tag --list', { cwd: this.path }).spread((stdout) => { - if (stdout) { - const tags = lodash.compact(stdout.split('\n')) - if (lodash.includes(tags, opts.tag)) { - return exec(`git checkout tags/${opts.tag}`, { cwd: this.path }) - } - } - throw new Error(`tag '${opts.tag}' does not exist`) - }) - } - }).then(() => { - // TODO name: createTargetDirectory - // Create the target directory. - this.sprout.emit('msg', `creating target directory: ${target}`) - return fs.mkdir(target) - }).then(() => { - // TODO name: installDependenciesIfPresent - // Install npm dependencies, if present. Eventually, this should work with - // package managers other than npm. - if (fs.existsSync(path.join(this.path, 'package.json'))) { - this.sprout.emit('msg', 'installing npm dependencies') - return exec('npm install', { cwd: this.path }) - } - }).then(() => { - // Anything occuring beyond this point which throws an error should - // trigger the removal of target directory! - return W.resolve().then(() => { - // TODO name: loadConfigFile - // Check for init.js. If init.js doesn't exist, confirm that - // init.coffee exists. Require init once this is determined. - const initCoffee = path.join(this.path, 'init.coffee') - const initJS = path.join(this.path, 'init.js') - let initPath - - if (fs.existsSync(initJS)) { - initPath = initJS - } else if (fs.existsSync(initCoffee)) { - initPath = initCoffee - } else { - throw new Error('neither init.coffee nor init.js exist') - } - - this.sprout.emit('msg', 'requiring ' + initPath) - init = require(initPath) - }).then(() => { - // TODO name: runBeforeHook - // Run before hook if present. - if (init.before) { - this.sprout.emit('msg', 'running before hook') - return W(init.before(utils, config)) - } - }).then(() => { - // TODO name: mergeConfig - // Merge in all known values for config. - lodash.assign(config, init.defaults, opts.locals) - - // If a config path is set, pull out its values and merge into config. - if (opts.config) { - this.sprout.emit('msg', `merging config: ${opts.config}`) - try { - lodash.assign(config, /\.json$/.test(opts.config) ? require(opts.config) - : yaml.safeLoad(fs.readFileSync(opts.config, 'utf8'))) - } catch (error) { - throw new Error(`could not open configuration file ${opts.config}`) - } - } - // TODO name: runPrompts - // If questionnaire function exists, run it to get answers. - if (lodash.isFunction(opts.questionnaire) && lodash.isArray(init.configure)) { - // Run questionnaire, omitting keys already set in config return - // answers merged with config values. - this.sprout.emit('msg', 'running questionnaire function') - return opts.questionnaire(init.configure, lodash.keys(config)) - .then((answers) => { return lodash.assign(config, answers) }) - } - }).then(() => { - // TODO name: copyTemplateToTarget - // Copy all files in root to target. - this.sprout.emit('msg', 'copying files in root to target') - return ncp(this.root, target) - }).then(() => { - // TODO name: runBeforeRenderHook - // Run beforeRender hook if present. - if (init.beforeRender) { - this.sprout.emit('msg', 'running beforeRender hook') - return W(init.beforeRender(utils, config)) - } - }).then(() => { - // TODO name: readTemplateFiles - // Read target directory. - this.sprout.emit('msg', 'reading target directory') - return readdirp({ - root: target, - directoryFilter: ['!.git', '!node_modules'] - }) - }).then((result) => { - // TODO name: removeIgnoredFiles - // Remove ignored files. - const files = lodash.filter(result.files, (file) => { - if (init.ignore) { - init.ignore = lodash.isArray(init.ignore) ? init.ignore - : [init.ignore] - for (var i = 0; i < init.ignore.length; i++) { - if (minimatch(file.path, init.ignore[i])) { - return false - } - } - } - return true - }) - - // TODO name: writeTemplateFiles - // Write all files with our Utils class. - return W.map(files, (file) => { - this.sprout.emit('msg', `reading ${file.fullPath}`) - - // If the file is not a binary, render it. - if (!isBinaryFile(file.fullPath)) { - this.sprout.emit('msg', `reading ${file.fullPath}`) - return utils.target.read(file.path).then((output) => { - this.sprout.emit('msg', `writing ${file.fullPath}`) - return utils.target.write(file.path, output, config) - }) - } - }) - }).then(() => { - // TODO name: runAfterHook - // Run after hook if present. - if (init.after) { - this.sprout.emit('msg', 'running after hook') - return W(init.after(utils, config)) - } - }).then(() => { - // TODO name: checkoutBranchIfNecessary - // If original branch is stored, checkout to said branch. - if (branch) { - this.sprout.emit('cmd', `git checkout ${branch}`, this.path) - return exec(`git checkout ${branch}`, { cwd: this.path }) - } - }).catch((error) => { - // TODO name: removeTargetDirectory - // Remove target directory if Sprout created it and an error is - // thrown. - return rimraf(target).then(() => { throw error }) - }) - }).yield(this) + return this.update().with(this) + .then(validateTarget.bind(this, target)) + .then(handleBranchOrTagOption.bind(this, opts)) + .then(createTargetDirectory.bind(this, target)) + .then(installDependenciesIfPresent) + .then(() => { + return W.resolve().with(this) + .then(loadConfigFile) + .then(runBeforeHook.bind(this, utils)) + .then(mergeConfig.bind(this, opts)) + .then(runPrompts.bind(this, opts)) + .then(copyTemplateToTarget.bind(this, target)) + .then(runBeforeRenderHook.bind(this, utils)) + .then(readTemplateFiles.bind(this, target)) + .then(removeIgnoredFiles) + .then(writeTemplateFiles.bind(this, utils)) + .then(runAfterHook.bind(this, utils)) + .then(checkoutBranchIfPresent.bind(this, this.currentBranch)) + .catch((err) => { return rimraf(target).then(() => { throw err }) }) + }).yield(this) } /* @@ -346,7 +120,7 @@ class Template { this.sprout.emit('cmd', 'git remote', this.path) return exec('git remote', { cwd: this.path }).then((stdout) => { - if (!stdout[0]) { return true } + if (!stdout[0]) { return } const origin = lodash.trim(stdout[0]) this.sprout.emit('cmd', `git pull ${origin} HEAD`, this.path) return exec(`git pull ${origin} HEAD`, { cwd: this.path }) @@ -426,4 +200,217 @@ class Template { } } +function ensureGitInstall () { + // Check if git is installed + if (!which.sync('git')) { + throw new Error('you need to have git installed') + } +} + +function validateSource () { + if (!this.src) { throw new Error('no source provided') } + + if (!this.isRemote && !fs.existsSync(this.src)) { + throw new Error(`there is no sprout template located at ${this.src}`) + } + + if (!this.isRemote && !fs.existsSync(path.join(this.src, '.git'))) { + throw new Error(`${this.src} is not a git repository`) + } +} + +// TODO is this needed? could replace with timeout +function ensureInternetConnection () { + if (!this.isRemote) { return } + return dns.resolve('google.com').catch(() => { + throw new Error('make sure that you are connected to the internet!') + }) +} + +function removeTemplate () { + return this.remove() +} + +function moveSourceToSproutPath () { + if (this.isRemote) { + this.sprout.emit('cmd', `git clone ${this.src} ${this.path}`) + return exec(`git clone --recursive ${this.src} ${this.path}`) + } + + return fs.symlinkAsync(this.src, this.path) +} + +function validateTemplate () { + return W.resolve().then(() => { + if (!fs.existsSync(path.join(this.path, 'init.js')) && !fs.existsSync(path.join(this.path, 'init.coffee'))) { + throw new Error('neither init.coffee nor init.js exist in this template') + } + + // Check for root path. + if (!fs.existsSync(this.root)) { + throw new Error('root path doesn\'t exist in template') + } + }).catch((error) => { + return rimraf(this.path).then(() => { throw error }) + }) +} + +function validateTarget (target) { + // TODO: remove this, checked already in `save`. + if (!fs.existsSync(this.root)) { + throw new Error(`root path doesn't exist in ${this.name}`) + } + + // If target not passed, throw an error. + // TODO: remove this, validate with joi + if (!target) { throw new Error('target path required') } + + // If target directory exists, throw an error. + if (fs.existsSync(target)) { throw new Error(`${target} already exists`) } +} + +function handleBranchOrTagOption (opts) { + if (!(opts.tag || opts.branch)) { return } + + this.sprout.emit('cmd', 'git rev-parse --abbrev-ref HEAD', this.path) + + return exec('git rev-parse --abbrev-ref HEAD', { cwd: this.path }) + .spread((stdout) => { this.currentBranch = stdout }) + .then(checkoutBranchIfPresent.bind(this, opts.branch)) + .then(checkoutTagIfPresent.bind(this, opts.tag)) +} + +function checkoutBranchIfPresent (branch) { + if (!branch) { return } + this.sprout.emit('cmd', `git checkout ${branch}`, this.path) + return exec(`git checkout ${branch}`, { cwd: this.path }) +} + +function checkoutTagIfPresent (tag) { + if (!tag) { return } + this.sprout.emit('cmd', `git checkout tags/${tag}`, this.path) + return exec(`git checkout tags/${tag}`, { cwd: this.path }) +} + +function createTargetDirectory (target) { + this.sprout.emit('msg', `creating target directory: ${target}`) + return fs.mkdir(target) +} + +function installDependenciesIfPresent () { + if (!fs.existsSync(path.join(this.path, 'package.json'))) { return } + this.sprout.emit('msg', 'installing npm dependencies') + return exec('npm install', { cwd: this.path }) +} + +function loadConfigFile () { + const initCoffee = path.join(this.path, 'init.coffee') + const initJS = path.join(this.path, 'init.js') + let initPath + + if (fs.existsSync(initJS)) { + initPath = initJS + } else if (fs.existsSync(initCoffee)) { + initPath = initCoffee + } else { + throw new Error('neither init.coffee nor init.js exist') + } + + this.sprout.emit('msg', `requiring ${initPath}`) + this.init = require(initPath) +} + +function runBeforeHook (utils) { + if (this.init.before) { + this.sprout.emit('msg', 'running before hook') + return W(this.init.before(utils, this.config)) + } +} + +function mergeConfig (opts) { + this.config = {} + + // Merge in all known values for config. + lodash.assign(this.config, this.init.defaults, opts.locals) + + // If a config path is set, pull out its values and merge into config. + if (opts.config) { + this.sprout.emit('msg', `merging config: ${opts.config}`) + let externalConfig + + if (/\.json$/.test(opts.config)) { + externalConfig = require(opts.config) + } else { + externalConfig = yaml.safeLoad(fs.readFileSync(opts.config, 'utf8')) + } + + lodash.assign(this.config, externalConfig) + } +} + +// If questionnaire function exists, run it to get answers. +// Omitting keys already set in config return answers merged with config values. +function runPrompts (opts) { + if (lodash.isFunction(opts.questionnaire) && lodash.isArray(this.init.configure)) { + this.sprout.emit('msg', 'running questionnaire function') + return opts.questionnaire(this.init.configure, lodash.keys(this.config)) + .then((answers) => { return lodash.assign(this.config, answers) }) + } +} + +function copyTemplateToTarget (target) { + this.sprout.emit('msg', 'copying files in root to target') + return ncp(this.root, target) +} + +function runBeforeRenderHook (utils) { + if (this.init.beforeRender) { + this.sprout.emit('msg', 'running beforeRender hook') + return W(this.init.beforeRender(utils, this.config)) + } +} + +function readTemplateFiles (target) { + this.sprout.emit('msg', 'reading target directory') + return readdirp({ + root: target, + directoryFilter: ['!.git', '!node_modules'] + }) +} + +function removeIgnoredFiles (result) { + return lodash.filter(result.files, (file) => { + if (this.init.ignore) { + this.init.ignore = Array.prototype.concat(this.init.ignore) + for (var i = 0; i < this.init.ignore.length; i++) { + if (minimatch(file.path, this.init.ignore[i])) { + return false + } + } + } + return true + }) +} + +function writeTemplateFiles (utils, files) { + return W.map(files, (file) => { + this.sprout.emit('msg', `reading ${file.fullPath}`) + + if (!isBinaryFile(file.fullPath)) { + this.sprout.emit('msg', `reading ${file.fullPath}`) + return utils.target.read(file.path).then((output) => { + this.sprout.emit('msg', `writing ${file.fullPath}`) + return utils.target.write(file.path, output, this.config) + }) + } + }) +} + +function runAfterHook (utils) { + if (this.init.after) { + this.sprout.emit('msg', 'running after hook') + return W(this.init.after(utils, this.config)) + } +} + module.exports = Template diff --git a/test/test.js b/test/test.js index 6647df0..6ee3777 100644 --- a/test/test.js +++ b/test/test.js @@ -589,7 +589,6 @@ describe('template', } ).catch( function (error) { - console.log('here we are') fs.existsSync(template.path).should.be.false error.toString().should.eq('Error: neither init.coffee nor init.js exist in this template') done() @@ -1046,7 +1045,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.eq('Error: tag \'foooooooo\' does not exist') + error.toString().should.match(/Error: Command failed: git checkout tags\/foooooooo/) return template.remove().then( function () { return rimraf(target, done) @@ -1224,7 +1223,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.eq('Error: could not open configuration file ' + configPath) + error.toString().should.match(/Error: ENOENT: no such file or directory/) return template.remove().then( function () { done() From 621c588c65467140609c133e24be0a37c00f1130 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Mon, 21 Mar 2016 18:43:38 -0400 Subject: [PATCH 08/14] test string correction --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 6ee3777..843cd10 100644 --- a/test/test.js +++ b/test/test.js @@ -1045,7 +1045,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.match(/Error: Command failed: git checkout tags\/foooooooo/) + error.toString().should.match(/Error: Command failed:.*git checkout tags\/foooooooo/) return template.remove().then( function () { return rimraf(target, done) From 53ca7d1a21e56f0a1b1de8501388b75c7cec0f2f Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Tue, 22 Mar 2016 17:34:44 -0400 Subject: [PATCH 09/14] refactor run function --- lib/template.js | 97 ++++++++++++++++++++++++++----------------------- package.json | 1 + test/test.js | 4 +- 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/lib/template.js b/lib/template.js index 0338df9..0824aac 100644 --- a/lib/template.js +++ b/lib/template.js @@ -7,6 +7,7 @@ import path from 'path' import yaml from 'js-yaml' import minimatch from 'minimatch' import lodash from 'lodash' +import Joi from 'joi' import _rimraf from 'rimraf' import _readdirp from 'readdirp' import _fs from 'fs' @@ -134,52 +135,24 @@ class Template { * @param {Object} args - arguments to be passed to the generator * @return {Promise} - Promise for Template instance. */ - run (target, generator, args) { - return W.resolve().then(() => { - // TODO: replace with joi validation, name validateArgs - // If target not passed, throw an error. - if (!target) { throw new Error('target path required') } - - // If target directory doesn't exist, throw an error. - if (!fs.existsSync(target)) { - throw new Error(`${target} does not exist`) - } - - // If generator name isn't passed, throw an error. - if (!generator) { throw new Error('generator name required') } - - // TODO name: validateGenerator - // Check for {generator}.js. If {generator}.js doesn't exist, confirm - // that {name}.coffee exists. Require {generator}.js or {generator}.coffee - // once this is determined. - const generatorCoffee = path.join(this.generators, `${generator}.coffee`) - const generatorJs = path.join(this.generators, `${generator}.js`) - let generatorPath - - if (fs.existsSync(generatorJs)) { - generatorPath = generatorJs - } else if (fs.existsSync(generatorCoffee)) { - generatorPath = generatorCoffee - } else { - throw new Error(`'${generator}' is not a generator in this template`) - } - - this.sprout.emit('msg', `requiring '${generator}' generator`) - return require(generatorPath) - }).then((generator) => { - // TODO name: executeGenerator - // Create a Utils instance where the `src` and `target` are both the - // target directory. - var utils = new Utils(this.path, target) - - // Add `utils` as the first object in our `args` array. - var args - (args = lodash.isArray(args) ? args : []).unshift(utils) - - // Call the generator, pass the Utils instance and the arguments. - this.sprout.emit('msg', `running ${generator} generator`) - return generator.apply(null, args) - }).yield(this) + run (target, generator, _args) { + let args = { target: target, generator: generator, generatorArgs: _args } + const schema = Joi.object().keys({ + target: Joi.string().required(), + generator: Joi.string().required(), + generatorArgs: Joi.array().default([]) + }) + + return node.call(Joi.validate, args, schema) + .then((res) => { args = res }) + .then(() => { + if (!fs.existsSync(target)) { + throw new Error(`${target} does not exist`) + } + }) + .then(validateGenerator.bind(this, args.generator)) + .then(executeGenerator.bind(this, args.target, args.generatorArgs)) + .yield(this) } /* @@ -200,6 +173,10 @@ class Template { } } +// +// Utility Functions +// + function ensureGitInstall () { // Check if git is installed if (!which.sync('git')) { @@ -413,4 +390,32 @@ function runAfterHook (utils) { } } +function validateGenerator (generator) { + const generatorCoffee = path.join(this.generators, `${generator}.coffee`) + const generatorJs = path.join(this.generators, `${generator}.js`) + let generatorPath + + if (fs.existsSync(generatorJs)) { + generatorPath = generatorJs + } else if (fs.existsSync(generatorCoffee)) { + generatorPath = generatorCoffee + } else { + throw new Error(`'${generator}' is not a generator in this template`) + } + + this.sprout.emit('msg', `requiring '${generator}' generator`) + return require(generatorPath) +} + +function executeGenerator (target, args, generator) { + const utils = new Utils(this.path, target) + + args = lodash.isArray(args) ? args : [] + args.unshift(utils) + + // Call the generator, pass the Utils instance and the arguments. + this.sprout.emit('msg', `running ${generator} generator`) + return generator.apply(null, args) +} + module.exports = Template diff --git a/package.json b/package.json index 3a52a91..4825ff2 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "coffee-script": "^1.9.3", "ejs": "^2.3.1", "isbinaryfile": "^3.0.0", + "joi": "^8.0.5", "js-yaml": "^3.4.5", "lodash": "^4.6.1", "minimatch": "^3.0.0", diff --git a/test/test.js b/test/test.js index 843cd10..eb1b8e5 100644 --- a/test/test.js +++ b/test/test.js @@ -1661,7 +1661,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.eq('Error: target path required') + error.toString().should.eq('ValidationError: child "target" fails because ["target" must be a string]') return template.remove(name).then( function () { return rimraf(target, done) @@ -1726,7 +1726,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.eq('Error: generator name required') + error.toString().should.eq('ValidationError: child "generator" fails because ["generator" must be a string]') return template.remove(name).then( function () { return rimraf(target, done) From c0916a10a27f700d22ec1a4c8ca544e6c63c1142 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Tue, 22 Mar 2016 17:43:16 -0400 Subject: [PATCH 10/14] Joi options for init function --- lib/template.js | 27 ++++++++++++++++++--------- readme.md | 3 ++- test/test.js | 6 +++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/template.js b/lib/template.js index 0824aac..3f5c5a9 100644 --- a/lib/template.js +++ b/lib/template.js @@ -80,10 +80,18 @@ class Template { * @return {Promise} - Promise for Template instance. */ init (target, opts = {}) { - // TODO: validate options with joi - var utils = new Utils(this.path, target) + const utils = new Utils(this.path, target) + const schema = Joi.object().keys({ + locals: Joi.object().default({}), + tag: Joi.string(), + branch: Joi.string(), + configPath: Joi.string(), + questionnaire: Joi.func() + }) - return this.update().with(this) + return node.call(Joi.validate, opts, schema).with(this) + .then((res) => { opts = res }) + .then(this.update.bind(this)) .then(validateTarget.bind(this, target)) .then(handleBranchOrTagOption.bind(this, opts)) .then(createTargetDirectory.bind(this, target)) @@ -311,14 +319,14 @@ function mergeConfig (opts) { lodash.assign(this.config, this.init.defaults, opts.locals) // If a config path is set, pull out its values and merge into config. - if (opts.config) { - this.sprout.emit('msg', `merging config: ${opts.config}`) + if (opts.configPath) { + this.sprout.emit('msg', `merging config: ${opts.configPath}`) let externalConfig - if (/\.json$/.test(opts.config)) { - externalConfig = require(opts.config) + if (/\.json$/.test(opts.configPath)) { + externalConfig = require(opts.configPath) } else { - externalConfig = yaml.safeLoad(fs.readFileSync(opts.config, 'utf8')) + externalConfig = yaml.safeLoad(fs.readFileSync(opts.configPath, 'utf8')) } lodash.assign(this.config, externalConfig) @@ -328,7 +336,8 @@ function mergeConfig (opts) { // If questionnaire function exists, run it to get answers. // Omitting keys already set in config return answers merged with config values. function runPrompts (opts) { - if (lodash.isFunction(opts.questionnaire) && lodash.isArray(this.init.configure)) { + // TODO remove type check here after further joi additions + if (opts.questionnaire && lodash.isArray(this.init.configure)) { this.sprout.emit('msg', 'running questionnaire function') return opts.questionnaire(this.init.configure, lodash.keys(this.config)) .then((answers) => { return lodash.assign(this.config, answers) }) diff --git a/readme.md b/readme.md index 12556ee..13afd2a 100644 --- a/readme.md +++ b/readme.md @@ -78,7 +78,8 @@ const target = '~/Projects/sprout-sprout-instance' * locals {Object} - EJS locals to template. * tag {String} - A git tag to generate the template from. * branch {String} - A git branch to generate the template from. - * config {String} - Path to a JSON or yaml file with pre-defined values. + * configPath {String} - Path to a JSON or yaml file with pre-defined values. + * questionnaire {Function} - Function that returns a promise for an object */ const options = { diff --git a/test/test.js b/test/test.js index eb1b8e5..f511af1 100644 --- a/test/test.js +++ b/test/test.js @@ -1070,7 +1070,7 @@ describe('template', ).then( function (template) { fs.existsSync(template.path).should.be.true - return template.init(target, {config: path.join(fixture, 'config.json')}) + return template.init(target, {configPath: path.join(fixture, 'config.json')}) } ).then( function (template) { @@ -1099,7 +1099,7 @@ describe('template', ).then( function (template) { fs.existsSync(template.path).should.be.true - return template.init(target, {config: path.join(fixture, 'config.yaml')}) + return template.init(target, {configPath: path.join(fixture, 'config.yaml')}) } ).then( function (template) { @@ -1219,7 +1219,7 @@ describe('template', ).then( function (template) { fs.existsSync(template.path).should.be.true - return template.init(target, {config: configPath}) + return template.init(target, {configPath: configPath}) } ).catch( function (error) { From 384bfcad3cba82e83c4dec924a821262406b1143 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Wed, 23 Mar 2016 12:40:33 -0400 Subject: [PATCH 11/14] joi for template constructor --- lib/api/add.js | 2 +- lib/index.js | 2 +- lib/template.js | 45 ++++++++++-------- test/test.js | 123 ++++++++++++++++++++++++------------------------ 4 files changed, 88 insertions(+), 84 deletions(-) diff --git a/lib/api/add.js b/lib/api/add.js index a39c56c..f092fec 100644 --- a/lib/api/add.js +++ b/lib/api/add.js @@ -8,7 +8,7 @@ import Template from '../template' * @return {Promise} - Promise for Template instance. */ export default function (sprout, name, src) { - const template = new Template(sprout, name, src) + const template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then(function (template) { sprout.templates[name] = template }) diff --git a/lib/index.js b/lib/index.js index 94ece72..0e6cb88 100644 --- a/lib/index.js +++ b/lib/index.js @@ -27,7 +27,7 @@ class Sprout extends EventEmitter { fs.readdirSync(this.path).map((name) => { const lstat = fs.lstatSync(path.join(this.path, name)) if (lstat.isDirectory() || lstat.isSymbolicLink()) { - this.templates[name] = new Template(this, name) + this.templates[name] = new Template({ sprout: this, name: name }) } }) } diff --git a/lib/template.js b/lib/template.js index 3f5c5a9..552abef 100644 --- a/lib/template.js +++ b/lib/template.js @@ -42,21 +42,26 @@ require('coffee-script/register') */ class Template { - constructor (sprout, name, src) { - this.sprout = sprout - this.name = name - this.path = path.join(sprout.path, name) - this.root = path.join(this.path, 'root') - this.generators = path.join(this.path, 'generators') - - /* - * If `src` is set, use `isGitURL` helper to determine whether `src` is a - * local path or a remote URL. - */ - if (src) { - this.src = src - this.isRemote = isGitUrl(src) - } + constructor (_options) { + const schema = Joi.object().keys({ + sprout: Joi.any().required(), + name: Joi.string().required(), + src: Joi.string(), + rootFolderName: Joi.string().default('root'), + generatorsFolderName: Joi.string().default('generators') + }) + + let options = Joi.validate(_options, schema) + if (options.error) { throw options.error } + options = options.value + + this.sprout = options.sprout + this.name = options.name + this.path = path.join(this.sprout.path, this.name) + this.rootPath = path.join(this.path, options.rootFolderName) + this.generatorsPath = path.join(this.path, options.generatorsFolderName) + this.src = options.src + if (this.src) { this.isRemote = isGitUrl(this.src) } } /* @@ -232,7 +237,7 @@ function validateTemplate () { } // Check for root path. - if (!fs.existsSync(this.root)) { + if (!fs.existsSync(this.rootPath)) { throw new Error('root path doesn\'t exist in template') } }).catch((error) => { @@ -242,7 +247,7 @@ function validateTemplate () { function validateTarget (target) { // TODO: remove this, checked already in `save`. - if (!fs.existsSync(this.root)) { + if (!fs.existsSync(this.rootPath)) { throw new Error(`root path doesn't exist in ${this.name}`) } @@ -346,7 +351,7 @@ function runPrompts (opts) { function copyTemplateToTarget (target) { this.sprout.emit('msg', 'copying files in root to target') - return ncp(this.root, target) + return ncp(this.rootPath, target) } function runBeforeRenderHook (utils) { @@ -400,8 +405,8 @@ function runAfterHook (utils) { } function validateGenerator (generator) { - const generatorCoffee = path.join(this.generators, `${generator}.coffee`) - const generatorJs = path.join(this.generators, `${generator}.js`) + const generatorCoffee = path.join(this.generatorsPath, `${generator}.coffee`) + const generatorJs = path.join(this.generatorsPath, `${generator}.js`) let generatorPath if (fs.existsSync(generatorJs)) { diff --git a/test/test.js b/test/test.js index f511af1..776d9ff 100644 --- a/test/test.js +++ b/test/test.js @@ -401,7 +401,7 @@ describe('template', function (done) { var name = 'validNamePath' var src = path.join(templateFixturesPath, name) - ;(function () { return new Template(sprout, name, src) }).should.be.ok + ;(function () { return new Template({ sprout: sprout, name: name, src: src }) }).should.be.ok done() } ) @@ -410,7 +410,7 @@ describe('template', function (done) { var name = null var src = path.join(templateFixturesPath, 'foo') - ;(function () { return new Template(sprout, name, src) }).should.throw + ;(function () { return new Template({ sprout: sprout, name: name, src: src }) }).should.throw done() } ) @@ -419,7 +419,7 @@ describe('template', function (done) { var name = 'foo' var src = 'https://github.com/carrot/sprout-sprout' - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) template.isRemote.should.be.true done() } @@ -429,7 +429,7 @@ describe('template', function (done) { var name = 'foo' var src = path.join(templateFixturesPath, 'isLocal') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) template.isRemote.should.be.false done() } @@ -449,7 +449,7 @@ describe('template', function (done) { var name = 'remote' var src = 'https://github.com/carrot/sprout-sprout' - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -467,7 +467,7 @@ describe('template', function (done) { var name = 'local' var src = path.join(saveTemplateFixturesPath, name) - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) gitInit(src).then( function () { return template.save() @@ -489,7 +489,7 @@ describe('template', function (done) { var name = 'replace' var src = path.join(saveTemplateFixturesPath, name) - var template = new Template(sprout, name, 'https://github.com/carrot/sprout-sprout') + var template = new Template({ sprout: sprout, name: name, src: 'https://github.com/carrot/sprout-sprout' }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -497,7 +497,7 @@ describe('template', } ).then( function () { - return (new Template(sprout, name, src)).save() + return (new Template({ sprout: sprout, name: name, src: src })).save() } ).then( function () { @@ -517,8 +517,7 @@ describe('template', it('should throw if template has no src', function (done) { var name = 'noSrc' - var src = null - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name }) return template.save().catch( function (error) { error.toString().should.eq('Error: no source provided') @@ -538,7 +537,7 @@ describe('template', }) var name = 'noInternet' var src = 'https://github.com/carrot/sprout-sprout' - var template = new (require('./../lib/template'))(sprout, name, src) + var template = new (require('./../lib/template'))({ sprout: sprout, name: name, src: src }) return template.save().catch( function (error) { error.toString().should.eq('Error: make sure that you are connected to the internet!') @@ -554,7 +553,7 @@ describe('template', function (done) { var name = 'noLocal' var src = path.join(saveTemplateFixturesPath, name) - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().catch( function (error) { error.toString().should.eq('Error: there is no sprout template located at ' + src) @@ -568,7 +567,7 @@ describe('template', function (done) { var name = 'noGit' var src = path.join(saveTemplateFixturesPath, name) - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().catch( function (error) { error.toString().should.eq('Error: ' + src + ' is not a git repository') @@ -582,7 +581,7 @@ describe('template', function (done) { var name = 'noInit' var src = path.join(saveTemplateFixturesPath, name) - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -601,7 +600,7 @@ describe('template', function (done) { var name = 'noRoot' var src = path.join(saveTemplateFixturesPath, name) - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -634,7 +633,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = 'https://github.com/carrot/sprout-sprout' var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -665,7 +664,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -673,7 +672,7 @@ describe('template', ).then( function (template) { fs.existsSync(template.path).should.be.true - return rimraf(template.root) + return rimraf(template.rootPath) } ).then( function () { @@ -682,8 +681,8 @@ describe('template', ).catch( function (error) { error.toString().should.eq("Error: root path doesn't exist in " + name) - fs.mkdirSync(template.root) - fs.writeFileSync(path.join(template.root, '.keep'), '') + fs.mkdirSync(template.rootPath) + fs.writeFileSync(path.join(template.rootPath, '.keep'), '') return template.remove().then( function () { done() @@ -698,7 +697,7 @@ describe('template', function (done) { var name = 'noRoot' var src = 'https://github.com/carrot/sprout-sprout' - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -723,7 +722,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = 'https://github.com/carrot/sprout-sprout' var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function () { fs.existsSync(template.path).should.be.true @@ -752,7 +751,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = 'https://github.com/carrot/sprout-sprout' var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function () { fs.existsSync(template.path).should.be.true @@ -777,7 +776,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = 'https://github.com/carrot/sprout-sprout' var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -803,7 +802,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -832,7 +831,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -861,7 +860,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -905,7 +904,7 @@ describe('template', fs.writeFileSync(srcInit, 'module.exports={};') fs.mkdirSync(srcRoot) fs.writeFileSync(path.join(srcRoot, '.keep'), '') - template = new Template(new Sprout(sproutPath), name, src) + template = new Template({ sprout: new Sprout(sproutPath), name: name, src: src }) } ).then( function () { @@ -926,7 +925,7 @@ describe('template', } ).then( function () { - fs.writeFileSync(path.join(template.root, 'foo'), '', 'utf8') + fs.writeFileSync(path.join(template.rootPath, 'foo'), '', 'utf8') return gitCommitAdd(template.path) } ).then( @@ -976,7 +975,7 @@ describe('template', fs.writeFileSync(srcInit, 'module.exports={};') fs.mkdirSync(srcRoot) fs.writeFileSync(path.join(srcRoot, '.keep'), '') - template = new Template(new Sprout(sproutPath), name, src) + template = new Template({ sprout: new Sprout(sproutPath), name: name, src: src }) } ).then( function () { @@ -992,7 +991,7 @@ describe('template', } ).then( function () { - fs.writeFileSync(path.join(template.root, 'foo'), '', 'utf8') + fs.writeFileSync(path.join(template.rootPath, 'foo'), '', 'utf8') return gitCommitAdd(template.path) } ) @@ -1002,7 +1001,7 @@ describe('template', } ).then( function () { - fs.writeFileSync(path.join(template.root, 'foo2'), '', 'utf8') + fs.writeFileSync(path.join(template.rootPath, 'foo2'), '', 'utf8') return gitCommitAdd(template.path) } ).then( @@ -1037,7 +1036,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = 'https://github.com/carrot/sprout-sprout' var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -1062,7 +1061,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1091,7 +1090,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1120,7 +1119,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1149,7 +1148,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1178,7 +1177,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) var q = function () { return W.resolve({ foo: 'bar' }) } @@ -1211,7 +1210,7 @@ describe('template', var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') var configPath = path.join(fixture, 'foobar') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1240,7 +1239,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1269,7 +1268,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1298,7 +1297,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1328,7 +1327,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1357,7 +1356,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1386,7 +1385,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1415,7 +1414,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1444,7 +1443,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1473,7 +1472,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1508,7 +1507,7 @@ describe('template', var fixture = path.join(initTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1547,7 +1546,7 @@ describe('template', function (done) { var name = 'update' var src = 'https://github.com/carrot/sprout-sprout' - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function (template) { fs.existsSync(template.path).should.be.true @@ -1569,7 +1568,7 @@ describe('template', function (done) { var name = 'noGit' var src = path.join(updateTemplateFixturesPath, name) - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1614,7 +1613,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1646,7 +1645,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1679,7 +1678,7 @@ describe('template', var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') var fakeTarget = path.join(fixture, 'doge/doge/doge/doge/doge') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1711,7 +1710,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1743,7 +1742,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1775,7 +1774,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1807,7 +1806,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1839,7 +1838,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1871,7 +1870,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1903,7 +1902,7 @@ describe('template', var fixture = path.join(runTemplateFixturesPath, name) var src = path.join(fixture, 'src') var target = path.join(fixture, 'target') - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return gitInit(src).then( function () { return template.save() @@ -1937,7 +1936,7 @@ describe('template', function (done) { var name = 'remove' var src = 'https://github.com/carrot/sprout-sprout' - var template = new Template(sprout, name, src) + var template = new Template({ sprout: sprout, name: name, src: src }) return template.save().then( function () { return template.remove() From 0460a777b730af7cb058e92216f0058306ac768c Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Wed, 23 Mar 2016 16:48:46 -0400 Subject: [PATCH 12/14] remove more redundant error checks --- lib/template.js | 12 ++---------- test/test.js | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/template.js b/lib/template.js index 552abef..f1e1f63 100644 --- a/lib/template.js +++ b/lib/template.js @@ -94,6 +94,8 @@ class Template { questionnaire: Joi.func() }) + Joi.assert(target, Joi.string().required().label('target')) + return node.call(Joi.validate, opts, schema).with(this) .then((res) => { opts = res }) .then(this.update.bind(this)) @@ -246,16 +248,6 @@ function validateTemplate () { } function validateTarget (target) { - // TODO: remove this, checked already in `save`. - if (!fs.existsSync(this.rootPath)) { - throw new Error(`root path doesn't exist in ${this.name}`) - } - - // If target not passed, throw an error. - // TODO: remove this, validate with joi - if (!target) { throw new Error('target path required') } - - // If target directory exists, throw an error. if (fs.existsSync(target)) { throw new Error(`${target} already exists`) } } diff --git a/test/test.js b/test/test.js index 776d9ff..a88c837 100644 --- a/test/test.js +++ b/test/test.js @@ -680,7 +680,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.eq("Error: root path doesn't exist in " + name) + error.toString().should.match(/ENOENT: no such file or directory/) fs.mkdirSync(template.rootPath) fs.writeFileSync(path.join(template.rootPath, '.keep'), '') return template.remove().then( @@ -705,7 +705,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.eq('Error: target path required') + error.toString().should.match(/"target" must be a string/) return template.remove().then( function () { done() From 02a12f7606d60d8f4c6f09f1cad0ebe5044f7173 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Wed, 23 Mar 2016 17:38:53 -0400 Subject: [PATCH 13/14] validate init file --- lib/template.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/template.js b/lib/template.js index f1e1f63..e002d28 100644 --- a/lib/template.js +++ b/lib/template.js @@ -298,8 +298,21 @@ function loadConfigFile () { throw new Error('neither init.coffee nor init.js exist') } - this.sprout.emit('msg', `requiring ${initPath}`) - this.init = require(initPath) + this.sprout.emit('msg', `requiring and validating ${initPath}`) + + const schema = Joi.object().keys({ + before: Joi.func(), + configure: Joi.array().items(Joi.object()), + beforeRender: Joi.func(), + after: Joi.func(), + ignore: Joi.array().single(), + defaults: Joi.object() + }) + + const result = Joi.validate(require(initPath), schema) + if (result.error) { throw result.error } + + this.init = result.value } function runBeforeHook (utils) { @@ -333,8 +346,7 @@ function mergeConfig (opts) { // If questionnaire function exists, run it to get answers. // Omitting keys already set in config return answers merged with config values. function runPrompts (opts) { - // TODO remove type check here after further joi additions - if (opts.questionnaire && lodash.isArray(this.init.configure)) { + if (opts.questionnaire && this.init.configure) { this.sprout.emit('msg', 'running questionnaire function') return opts.questionnaire(this.init.configure, lodash.keys(this.config)) .then((answers) => { return lodash.assign(this.config, answers) }) From f888089b64f73aa854d8ea18ab475fcd2559e9ea Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 24 Mar 2016 12:54:31 -0400 Subject: [PATCH 14/14] remove coffeescript dep in all forms --- lib/template.js | 50 +++++----------------- package.json | 1 - test/test.js | 107 ++---------------------------------------------- 3 files changed, 15 insertions(+), 143 deletions(-) diff --git a/lib/template.js b/lib/template.js index e002d28..d4a8884 100644 --- a/lib/template.js +++ b/lib/template.js @@ -25,13 +25,6 @@ const fs = node.liftAll(_fs, (pfs, lifted, name) => { }) const dns = node.liftAll(_dns) -/* - * Register CoffeeScript `init` file in template can be either CoffeeScript or - * JavaScript. - */ - -require('coffee-script/register') - /* * Given a Sprout instance and a name, returns a Template instance. * @param {Function} sprout - Sprout instance. @@ -75,7 +68,6 @@ class Template { .then(ensureInternetConnection) .then(removeTemplate) .then(moveSourceToSproutPath) - .then(validateTemplate) .yield(this) } @@ -99,6 +91,7 @@ class Template { return node.call(Joi.validate, opts, schema).with(this) .then((res) => { opts = res }) .then(this.update.bind(this)) + .then(validateTemplate) .then(validateTarget.bind(this, target)) .then(handleBranchOrTagOption.bind(this, opts)) .then(createTargetDirectory.bind(this, target)) @@ -233,18 +226,13 @@ function moveSourceToSproutPath () { } function validateTemplate () { - return W.resolve().then(() => { - if (!fs.existsSync(path.join(this.path, 'init.js')) && !fs.existsSync(path.join(this.path, 'init.coffee'))) { - throw new Error('neither init.coffee nor init.js exist in this template') - } + if (!fs.existsSync(path.join(this.path, 'init.js'))) { + throw new Error('init.js does not exist in this template') + } - // Check for root path. - if (!fs.existsSync(this.rootPath)) { - throw new Error('root path doesn\'t exist in template') - } - }).catch((error) => { - return rimraf(this.path).then(() => { throw error }) - }) + if (!fs.existsSync(this.rootPath)) { + throw new Error('root path does not exist in template') + } } function validateTarget (target) { @@ -286,17 +274,7 @@ function installDependenciesIfPresent () { } function loadConfigFile () { - const initCoffee = path.join(this.path, 'init.coffee') - const initJS = path.join(this.path, 'init.js') - let initPath - - if (fs.existsSync(initJS)) { - initPath = initJS - } else if (fs.existsSync(initCoffee)) { - initPath = initCoffee - } else { - throw new Error('neither init.coffee nor init.js exist') - } + const initPath = path.join(this.path, 'init.js') this.sprout.emit('msg', `requiring and validating ${initPath}`) @@ -409,15 +387,9 @@ function runAfterHook (utils) { } function validateGenerator (generator) { - const generatorCoffee = path.join(this.generatorsPath, `${generator}.coffee`) - const generatorJs = path.join(this.generatorsPath, `${generator}.js`) - let generatorPath - - if (fs.existsSync(generatorJs)) { - generatorPath = generatorJs - } else if (fs.existsSync(generatorCoffee)) { - generatorPath = generatorCoffee - } else { + const generatorPath = path.join(this.generatorsPath, `${generator}.js`) + + if (!fs.existsSync(generatorPath)) { throw new Error(`'${generator}' is not a generator in this template`) } diff --git a/package.json b/package.json index 4825ff2..b5d637f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ ], "dependencies": { "argparse": "^1.0.2", - "coffee-script": "^1.9.3", "ejs": "^2.3.1", "isbinaryfile": "^3.0.0", "joi": "^8.0.5", diff --git a/test/test.js b/test/test.js index a88c837..e55d1d3 100644 --- a/test/test.js +++ b/test/test.js @@ -576,44 +576,6 @@ describe('template', ) } ) - - it("should throw and remove template when init.coffee and init.js don't exist in template", - function (done) { - var name = 'noInit' - var src = path.join(saveTemplateFixturesPath, name) - var template = new Template({ sprout: sprout, name: name, src: src }) - return gitInit(src).then( - function () { - return template.save() - } - ).catch( - function (error) { - fs.existsSync(template.path).should.be.false - error.toString().should.eq('Error: neither init.coffee nor init.js exist in this template') - done() - } - ) - } - ) - - it("should throw and remove template when root path doesn't exist in template", - function (done) { - var name = 'noRoot' - var src = path.join(saveTemplateFixturesPath, name) - var template = new Template({ sprout: sprout, name: name, src: src }) - return gitInit(src).then( - function () { - return template.save() - } - ).catch( - function (error) { - fs.existsSync(template.path).should.be.false - error.toString().should.eq("Error: root path doesn't exist in template") - done() - } - ) - } - ) } ) @@ -680,7 +642,7 @@ describe('template', } ).catch( function (error) { - error.toString().should.match(/ENOENT: no such file or directory/) + error.toString().should.eq('Error: root path does not exist in template') fs.mkdirSync(template.rootPath) fs.writeFileSync(path.join(template.rootPath, '.keep'), '') return template.remove().then( @@ -770,7 +732,7 @@ describe('template', } ) - it('should throw when no init.js or init.coffee provided', + it('should throw when no init.js provided', function (done) { var name = 'init' var fixture = path.join(initTemplateFixturesPath, name) @@ -780,12 +742,12 @@ describe('template', return template.save().then( function (template) { fs.existsSync(template.path).should.be.true - fs.unlinkSync(path.join(template.path, 'init.coffee')) + fs.unlinkSync(path.join(template.path, 'init.js')) return template.init(target) } ).catch( function (error) { - error.toString().should.eq('Error: neither init.coffee nor init.js exist') + error.toString().should.eq('Error: init.js does not exist in this template') return template.remove().then( function () { rimraf(target, done) @@ -854,35 +816,6 @@ describe('template', } ) - it('should use init.coffee', - function (done) { - var name = 'initCoffee' - var fixture = path.join(initTemplateFixturesPath, name) - var src = path.join(fixture, 'src') - var target = path.join(fixture, 'target') - var template = new Template({ sprout: sprout, name: name, src: src }) - return gitInit(src).then( - function () { - return template.save() - } - ).then( - function (template) { - fs.existsSync(template.path).should.be.true - return template.init(target) - } - ).then( - function (template) { - fs.readFileSync(path.join(target, 'foo'), 'utf8').should.eq('bar\n') - return template.remove() - } - ).then( - function () { - return rimraf(target, done) - } - ) - } - ) - it('should use a different git branch if specified', function (done) { var name = 'branch' @@ -1800,38 +1733,6 @@ describe('template', } ) - it("should run generator if it's a .coffee file", - function (done) { - var name = 'generatorCoffee' - var fixture = path.join(runTemplateFixturesPath, name) - var src = path.join(fixture, 'src') - var target = path.join(fixture, 'target') - var template = new Template({ sprout: sprout, name: name, src: src }) - return gitInit(src).then( - function () { - return template.save() - } - ).then( - function () { - return template.init(target) - } - ).then( - function (template) { - return template.run(target, 'foo') - } - ).then( - function (template) { - fs.readFileSync(path.join(target, 'foo'), 'utf8').should.eq('bar') - return template.remove(name) - } - ).then( - function () { - return rimraf(target, done) - } - ) - } - ) - it('should throw error if require returns error', function (done) { var name = 'requireError'