diff --git a/lib/init.js b/lib/init.js index 50e3bbc47..3e2dc1624 100755 --- a/lib/init.js +++ b/lib/init.js @@ -3,6 +3,8 @@ var fs = require('fs'); var util = require('util'); var path = require('path'); var glob = require('glob'); +var exec = require('child_process').exec; +var EventEmitter = require('events').EventEmitter; var helper = require('./helper'); var logger = require('./logger'); @@ -43,6 +45,48 @@ var COLORS_OFF = { } }; +var logQueue = []; + +var printLogQueue = function() { + while (logQueue.length) { + logQueue.shift()(); + } +}; + +var NODE_MODULES_DIR = path.resolve(__dirname, '../..'); + +// Karma is not in node_modules, probably a symlink, +// use current working dir. +if (!/node_modules$/.test(NODE_MODULES_DIR)) { + NODE_MODULES_DIR = path.resolve('node_modules'); +} + +var installPackage = function(pkgName) { + log.debug('Missing plugin "%s". Installing...', pkgName); + + var options = { + cwd: path.resolve(NODE_MODULES_DIR, '..') + }; + + exec('npm install ' + pkgName + ' --save-dev', options, function(err, stdout, stderr) { + // Put the logs into the queue and print them after answering current question. + // Otherwise the log would clobber the interactive terminal. + logQueue.push(function() { + if (!err) { + log.debug('%s successfully installed.', pkgName); + } else if (/is not in the npm registry/.test(stderr)) { + log.warn('Failed to install "%s". It is not in the NPM registry!\n' + + ' Please install it manually.', pkgName); + } else if (/Error: EACCES/.test(stderr)) { + log.warn('Failed to install "%s". No permissions to write in %s!\n' + + ' Please install it manually.', pkgName, options.cwd); + } else { + log.warn('Failed to install "%s"\n Please install it manually.', pkgName); + } + }); + }); +}; + var validatePattern = function(value) { if (!glob.sync(value).length) { log.warn('There is no file matching this pattern.\n' + colors.NYAN); @@ -51,24 +95,23 @@ var validatePattern = function(value) { var validateBrowser = function(name) { - var moduleName = 'karma-' + name.toLowerCase().replace('canary', '') + '-launcher'; + var pkgName = 'karma-' + name.toLowerCase().replace('canary', '') + '-launcher'; try { - require(moduleName); + require(NODE_MODULES_DIR + '/' + pkgName); } catch (e) { - log.warn('Missing "%s" plugin.\n npm install %s --save-dev' + colors.NYAN, moduleName, - moduleName); + installPackage(pkgName); } // TODO(vojta): check if the path resolves to a binary }; var validateFramework = function(name) { + var pkgName = 'karma-' + name; try { - require('karma-' + name); + require(NODE_MODULES_DIR + '/' + pkgName); } catch (e) { - log.warn('Missing "karma-%s" plugin.\n npm install karma-%s --save-dev' + colors.NYAN, name, - name); + installPackage(pkgName); } }; @@ -141,6 +184,8 @@ var StateMachine = function(rli) { var pendingQuestionId; var done; + EventEmitter.call(this); + this.onKeypress = function(key) { if (!currentOptions || !key) { return; @@ -221,6 +266,8 @@ var StateMachine = function(rli) { currentQuestion = questions.shift(); } + this.emit('next_question', currentQuestion); + if (currentQuestion) { pendingQuestionId = null; @@ -251,6 +298,8 @@ var StateMachine = function(rli) { }; }; +util.inherits(StateMachine, EventEmitter); + var quote = function(value) { return '\'' + value + '\''; @@ -355,6 +404,8 @@ exports.init = function(config) { process.exit(0); }); + sm.on('next_question', printLogQueue); + sm.process(questions, function(answers) { var cwd = process.cwd(); var configFile = config.configFile || 'karma.conf.js';