| /*
+ * prompt.js: Simple prompt for prompting information from the command line
+ *
+ * (C) 2010, Nodejitsu Inc.
+ *
+ */
+
+var events = require('events'),
+ async = require('async'),
+ colors = require('colors'),
+ winston = require('winston'),
+ stdio = process.binding('stdio'); |
@private function capitalize (str)
+
+str {string} String to capitalize
+
+Capitalizes the string supplied. | function capitalize(str) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
+var prompt = module.exports = Object.create(events.EventEmitter.prototype);
+
+var logger = prompt.logger = new winston.Logger({
+ transports: [
+ new (winston.transports.Console)()
+ ]
+});
+
+prompt.started = false;
+prompt.paused = false;
+prompt.allowEmpty = false;
+
+var stdin, stdout; |
Create an empty object for the properties
+known to prompt | |
Setup the default winston logger to use
+the cli levels and colors. | |
function start (options)
+
+@options {Object} Optional Options to consume by prompt
+
+Starts the prompt by listening to the appropriate events on options.stdin
+and options.stdout . If no streams are supplied, then process.stdin
+and process.stdout are used, respectively. | prompt.start = function (options) {
+ if (prompt.started) {
+ return;
+ }
+
+ options = options || {};
+ stdin = options.stdin || process.openStdin();
+ stdout = options.stdout || process.stdout;
+
+ prompt.allowEmpty = options.allowEmpty || false;
+
+ process.on('SIGINT', function () {
+ stdout.write('\n');
+ process.exit(1);
+ })
+
+ prompt.emit('start');
+ prompt.started = true;
+ return prompt;
+}; |
function pause ()
+
+Pauses input coming in from stdin | prompt.pause = function () {
+ if (!prompt.started || prompt.paused) {
+ return;
+ }
+
+ stdin.pause();
+ prompt.emit('pause');
+ prompt.paused = true;
+ return prompt;
+}; |
function resume ()
+
+Resumes input coming in from stdin | prompt.resume = function () {
+ if (!prompt.started || !prompt.paused) {
+ return;
+ }
+
+ stdin.resume();
+ prompt.emit('resume');
+ prompt.paused = false;
+ return prompt;
+}; |
function get (msg, [validator,] callback)
+
+@msg {Array|Object|string} Set of variables to get input for.
+
+@callback {function} Continuation to pass control to when complete.
+
+Gets input from the user via stdin for the specified message(s) msg . | prompt.get = function (msg, callback) {
+ var vars = !Array.isArray(msg) ? [msg] : msg,
+ result = {};
+
+ vars = vars.map(function (v) {
+ if (typeof v === 'string') {
+ v = v.toLowerCase();
+ }
+
+ return prompt.properties[v] || v;
+ });
+
+ function get(target, next) {
+ prompt.getInput(target, function (err, line) {
+ if (err) {
+ return next(err);
+ }
+
+ var name = target.name || target;
+ result[name] = line;
+ next();
+ });
+ }
+
+ async.forEachSeries(vars, get, function (err) {
+ return err ? callback(err) : callback(null, result);
+ });
+
+ return prompt;
+}; |
function getInput (msg, validator, callback)
+
+@msg {Object|string} Variable to get input for.
+
+@callback {function} Continuation to pass control to when complete.
+
+Gets input from the user via stdin for the specified message msg . | prompt.getInput = function (prop, callback) {
+ var name = prop.message || prop.name || prop,
+ raw = ['prompt', ': ' + name.grey, ': '.grey],
+ read = prop.hidden ? prompt.readLineHidden : prompt.readLine,
+ length, msg;
+
+ if (prop.default) {
+ raw.splice(2, -1, ' (' + prop.default + ')');
+ }
+ |
Calculate the raw length and colorize the prompt | length = raw.join('').length;
+ raw[0] = raw[0];
+ msg = raw.join('');
+
+ if (prop.help) {
+ prop.help.forEach(function (line) {
+ logger.help(line);
+ });
+ }
+
+ stdout.write(msg);
+ prompt.emit('prompt', prop);
+
+ read.call(null, function (err, line) {
+ if (err) {
+ return callback(err);
+ }
+
+ if (!line || line === '') {
+ line = prop.default || line;
+ }
+
+ if (prop.validator || prop.empty === false) {
+ var valid;
+
+ if (prop.validator) {
+ valid = prop.validator.test
+ ? prop.validator.test(line)
+ : prop.validator(line);
+ }
+
+ if (prop.empty === false && valid) {
+ valid = line.length > 0;
+ prop.warning = prop.warning || 'You must supply a value.';
+ }
+
+ if (!valid) {
+ logger.error('Invalid input for ' + name.grey);
+ if (prop.warning) {
+ logger.error(prop.warning);
+ }
+
+ prompt.emit('invalid', prop, line);
+ return prompt.getInput(prop, callback);
+ }
+ }
+
+ logger.input(line.yellow);
+ callback(null, line);
+ });
+
+ return prompt;
+}; |
function addProperties (obj, properties, callback)
+
+@obj {Object} Object to add properties to
+
+@properties {Array} List of properties to get values for
+
+@callback {function} Continuation to pass control to when complete.
+
+Prompts the user for values each of the properties if obj does not already
+have a value for the property. Responds with the modified object. | prompt.addProperties = function (obj, properties, callback) {
+ properties = properties.filter(function (prop) {
+ return typeof obj[prop] === 'undefined';
+ });
+
+ if (properties.length === 0) {
+ return callback(obj);
+ }
+
+ prompt.get(properties, function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+ else if (!results) {
+ return callback(null, obj);
+ }
+
+ function putNested (obj, path, value) {
+ var last = obj, key;
+
+ while (path.length > 1) {
+ key = path.shift();
+ if (!last[key]) {
+ last[key] = {};
+ }
+
+ last = last[key];
+ }
+
+ last[path.shift()] = value;
+ }
+
+ Object.keys(results).forEach(function (key) {
+ putNested(obj, key.split('.'), results[key]);
+ });
+
+ callback(null, obj);
+ });
+
+ return prompt;
+}; |
function readLine (callback)
+
+@callback {function} Continuation to respond to when complete
+
+Gets a single line of input from the user. | prompt.readLine = function (callback) {
+ var value = '', buffer = '';
+ prompt.resume();
+ stdin.setEncoding('utf8');
+ stdin.on('error', callback);
+ stdin.on('data', function data (chunk) {
+ value += buffer + chunk;
+ buffer = '';
+ value = value.replace(/\r/g, '');
+ if (value.indexOf('\n') !== -1) {
+ if (value !== '\n') {
+ value = value.replace(/^\n+/, '');
+ }
+
+ buffer = value.substr(value.indexOf('\n'));
+ val = value.substr(0, value.indexOf('\n'));
+ prompt.pause();
+ stdin.removeListener('data', data);
+ stdin.removeListener('error', callback);
+ value = value.trim();
+ callback(null, value);
+ }
+ });
+
+ return prompt;
+}; |
function readLineHidden (callback)
+
+@callback {function} Continuation to respond to when complete
+
+Gets a single line of hidden input (i.e. rawMode = true ) from the user. | prompt.readLineHidden = function (callback) {
+ var value = '', buffer = '';
+ stdio.setRawMode(true);
+ prompt.resume();
+ stdin.on('error', callback);
+ stdin.on('data', function data (c) {
+ c = '' + c;
+ switch (c) {
+ case '\n': case '\r': case '\r\n': case '\u0004':
+ stdio.setRawMode(false);
+ stdin.removeListener('data', data);
+ stdin.removeListener('error', callback);
+ value = value.trim();
+ stdout.write('\n');
+ stdout.flush();
+ prompt.pause();
+ return callback(null, value)
+ case '\u0003': case '\0':
+ stdout.write('\n');
+ process.exit(1);
+ break;
+ default:
+ value += buffer + c
+ buffer = '';
+ break;
+ }
+ });
+
+ return prompt;
+};
+
+ |