Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

question-cache

Package Overview
Dependencies
Maintainers
2
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

question-cache - npm Package Compare versions

Comparing version 0.3.5 to 0.4.0

lib/question.js

662

index.js

@@ -1,22 +0,25 @@

/*!
* question-cache <https://github.com/jonschlinkert/question-cache>
*
* Copyright (c) 2015, Jon Schlinkert.
* Licensed under the MIT License.
*/
'use strict';
var use = require('use');
var util = require('util');
var debug = require('debug')('questions');
var Options = require('option-cache');
var Question = require('./lib/question');
var utils = require('./lib/utils');
/**
* Create an instance of `Questions` with the
* given `options`.
* Answer cache, for caching answers during a session,
* and potentially across instances
*/
var sessionAnswers = {};
var forced = {};
/**
* Create an instance of `Questions` with the given `options`.
*
* ```js
* var inquirer = require('inquirer')
* var questions = new Questions({inquirer: inquirer});
* var Questions = new Questions(options);
* ```
*
* @param {Object} `options` Pass your instance of [inquirer] on the `inquirer` option.
* @param {Object} `options` question cache options
* @api public

@@ -29,60 +32,135 @@ */

}
if (isObject(options) && 'prompt' in options) {
options = {inquirer: options};
}
this.options = options || {};
var inquirer = this.options.inquirer;
if (typeof inquirer === 'undefined') {
inquirer = utils.inquirer();
}
Options.call(this, options);
use(this);
this.options = utils.omitEmpty(options || {});
this.initQuestions(this.options);
}
define(this, 'inquirer', inquirer);
delete this.options.inquirer;
/**
* Inherit `options-cache`
*/
util.inherits(Questions, Options);
/**
* Intialize question-cache
*/
Questions.prototype.initQuestions = function(opts) {
debug('initializing question-cache');
this.answers = sessionAnswers;
this.inquirer = opts.inquirer || utils.inquirer();
this.project = opts.project || utils.project(process.cwd());
this.data = opts.data || {};
this.cache = {};
this.queue = [];
}
};
/**
* Store a question object by `key`.
* Calls [addQuestion](#addQuestion), with the only difference being that `.set`
* returns the `questions` instance and `.addQuestion` returns the question object.
* So use `.set` if you want to chain questions, or `.addQuestion` if you need
* the created question object.
*
* ```js
* questions.set('name', {
* questions
* .set('drink', 'What is your favorite beverage?')
* .set('color', 'What is your favorite color?')
* .set('season', 'What is your favorite season?');
*
* // or
* questions.set('drink', {
* type: 'input',
* message: 'Project name?',
* default: 'undefined'
* message: 'What is your favorite beverage?'
* });
*
* // or
* questions.set({
* name: 'drink'
* type: 'input',
* message: 'What is your favorite beverage?'
* });
* ```
* @param {Object|String} `name` Question name, message (string), or question/options object.
* @param {Object|String} `value` Question message (string), or question/options object.
* @param {Object|String} `options` Question/options object.
* @api public
*/
Questions.prototype.set = function(name, val, options) {
this.addQuestion.apply(this, arguments);
return this;
};
/**
* Add a question to be asked at a later point. Creates an instance of
* [Question](#question), so any `Question` options or settings may be used.
* Also, the default `type` is `input` if not defined by the user.
*
* @param {String} `key` Unique question id.
* @param {Object} `value` Question object that follows [inquirer] conventions.
* ```js
* questions.addQuestion('drink', 'What is your favorite beverage?');
*
* // or
* questions.addQuestion('drink', {
* type: 'input',
* message: 'What is your favorite beverage?'
* });
*
* // or
* questions.addQuestion({
* name: 'drink'
* type: 'input',
* message: 'What is your favorite beverage?'
* });
* ```
* @param {Object|String} `name` Question name, message (string), or question/options object.
* @param {Object|String} `value` Question message (string), or question/options object.
* @param {Object|String} `options` Question/options object.
* @api public
*/
Questions.prototype.set = function(key, value) {
if (typeof key === 'undefined') {
throw new TypeError('expected set to be a string or object.');
Questions.prototype.addQuestion = function(name, val, options) {
if (utils.isObject(name) && !utils.isQuestion(name)) {
return this.visit('set', name);
}
if (typeof key === 'object') {
value = key;
key = value.name;
}
var question = new Question(name, val, options);
debug('questions#set "%s"', name);
if (typeof value === 'string') {
value = {message: value};
} else if (typeof key === 'string' && !value) {
value = {message: addQmark(key)};
}
this.emit('set', question.name, question);
this.cache[question.name] = question;
value = value || {};
if (isObject(key)) {
value = utils.merge({}, value, key);
}
utils.union(this.queue, [question.name]);
this.run(question);
return question;
};
value.type = value.type || 'input';
value.name = value.name || value.key || key;
/**
* Create a "choices" question from an array of values.
*
* ```js
* questions.choices('foo', ['a', 'b', 'c']);
*
* // or
* questions.choices('foo', {
* message: 'Favorite letter?',
* choices: ['a', 'b', 'c']
* });
* ```
* @param {String} `key` Question key
* @param {String} `msg` Question message
* @param {Array} `items` Choice items
* @param {Object|Function} `options` Question options or callback function
* @param {Function} `callback` callback function
* @api public
*/
utils.set(this.cache, key, value);
this.queue.push(value);
Questions.prototype.choices = function(key, msg, items, options) {
var choices = utils.toChoices();
var question = choices.apply(null, arguments);
if (!question.hasOwnProperty('save')) {
question.save = false;
}
this.set(question.name, question);
return this;

@@ -92,270 +170,390 @@ };

/**
* Get a question by `key`.
* Create a "list" question from an array of values.
*
* ```js
* questions.get('name');
* //=> {type: 'input', message: 'What is your name?', default: ''}
* questions.list('foo', ['a', 'b', 'c']);
*
* // or
* questions.list('foo', {
* message: 'Favorite letter?',
* choices: ['a', 'b', 'c']
* });
* ```
*
* @param {String} `key` Unique question id.
* @param {Object} `value` Question object that follows [inquirer] conventions.
* @param {String} `key` Question key
* @param {String} `msg` Question message
* @param {Array} `list` List items
* @param {String|Array} `queue` Name or array of question names.
* @param {Object|Function} `options` Question options or callback function
* @param {Function} `callback` callback function
* @api public
*/
Questions.prototype.get = function(key) {
return utils.get(this.cache, key);
Questions.prototype.list = function(key, msg, list, options) {
var choices = utils.toChoices({type: 'list'});
var question = choices.apply(null, arguments);
if (options) {
question.options = options;
}
this.set(question.name, question);
return this;
};
/**
* Return true if the given question name is stored on
* question-cache.
* Create a "rawlist" question from an array of values.
*
* ```js
* var exists = questions.has('name');
* //=> true or false
* questions.rawlist('foo', ['a', 'b', 'c']);
*
* // or
* questions.rawlist('foo', {
* message: 'Favorite letter?',
* choices: ['a', 'b', 'c']
* });
* ```
*
* @param {String} `key` Unique question id.
* @param {Object} `value` Question object that follows [inquirer] conventions.
* @param {String} `key` Question key
* @param {String} `msg` Question message
* @param {Array} `list` List items
* @param {String|Array} `queue` Name or array of question names.
* @param {Object|Function} `options` Question options or callback function
* @param {Function} `callback` callback function
* @api public
*/
Questions.prototype.has = function(key) {
return utils.has(this.cache, key);
Questions.prototype.rawlist = function(key, msg, list, options) {
var choices = utils.toChoices({type: 'rawlist'});
var question = choices.apply(null, arguments);
if (options) {
question.options = options;
}
this.set(question.name, question);
return this;
};
/**
* Returns an array of question objects from an array of keys. Keys
* may use dot notation.
* Create an "expand" question from an array of values.
*
* @param {Array} `keys` Question names or object paths.
* @return {Array} Array of question objects.
* ```js
* questions.expand('foo', ['a', 'b', 'c']);
*
* // or
* questions.expand('foo', {
* message: 'Favorite letter?',
* choices: ['a', 'b', 'c']
* });
* ```
* @param {String} `key` Question key
* @param {String} `msg` Question message
* @param {Array} `list` List items
* @param {String|Array} `queue` Name or array of question names.
* @param {Object|Function} `options` Question options or callback function
* @param {Function} `callback` callback function
* @api public
*/
Questions.prototype.resolve = function(keys) {
keys = arrayify(keys);
var len = keys.length, i = -1;
var questions = [];
Questions.prototype.expand = function(name, msg, list, options) {
var choices = utils.toChoices({type: 'expand'});
var question = choices.apply(null, arguments);
if (options) {
question.options = options;
}
this.set(question.name, question);
return this;
};
while (++i < len) {
var question = {};
var key = keys[i];
/**
* Create a "choices" question from an array of values.
*
* ```js
* questions.choices('foo', ['a', 'b', 'c']);
* // or
* questions.choices('foo', {
* message: 'Favorite letter?',
* choices: ['a', 'b', 'c']
* });
* ```
* @param {String|Array} `queue` Name or array of question names.
* @param {Object|Function} `options` Question options or callback function
* @param {Function} `callback` callback function
* @api public
*/
if (typeof key === 'string') {
question = this.get(key);
} else if (isObject(key)) {
question = this.normalizeObject(key);
}
Questions.prototype.confirm = function() {
var question = this.addQuestion.apply(this, arguments);
question.type = 'confirm';
return this;
};
if (!question) {
question = this.toQuestion(key);
}
/**
* Get question `name`, or group `name` if question is not found.
* You can also do a direct lookup using `quesions.cache['foo']`.
*
* ```js
* var name = questions.get('name');
* //=> question object
* ```
* @param {String} `name`
* @return {Object} Returns the question object.
* @api public
*/
if (question.hasOwnProperty('type')) {
questions.push(question);
continue;
}
Questions.prototype.get = function(key) {
return !utils.isQuestion(key) ? this.cache[key] : key;
};
for (var prop in question) {
this.set(prop, question[prop]);
var val = this.get(prop);
/**
* Returns true if `questions.cache` or `questions.groups` has
* question `name`.
*
* ```js
* var name = questions.has('name');
* //=> true
* ```
* @return {String} The name of the question to check
* @api public
*/
if (question.hasOwnProperty(prop)) {
questions.push(val);
}
}
Questions.prototype.has = function(key) {
for (var prop in this.cache) {
if (prop.indexOf(key) === 0) return true;
}
return questions;
return false;
};
/**
* Normalize questions into a consistent object format
* following [inquirer][] conventions.
* Delete the given question or any questions that have the given
* namespace using dot-notation.
*
* @param {Object} `questions`
* @param {Object} `options`
* @return {Object}
* ```js
* questions.del('name');
* questions.get('name');
* //=> undefined
*
* // using dot-notation
* questions.del('author');
* questions.get('author.name');
* //=> undefined
* ```
* @return {String} The name of the question to delete
* @api public
*/
Questions.prototype.normalizeObject = function(questions) {
var res = [];
for (var key in questions) {
if (questions.hasOwnProperty(key)) {
var val = questions[key];
var question;
if (typeof val === 'string') {
question = this.toQuestion(key, val);
} else if (typeof val === 'object') {
question = this.toQuestion(key, val);
}
if (question) res = res.concat(question);
Questions.prototype.del = function(key) {
for (var prop in this.cache) {
if (prop.indexOf(key) === 0) {
delete this.cache[prop];
}
}
return res;
};
/**
* Create a question object from a string. Uses the `input` question type,
* and does the following basic normalization:
* Clear all cached answers.
*
* - when `foo` is passed, a `?` is added to the question. e.g. `foo?`
* - when `foo?` is passed, `?` is removed on the question key, so the answer to `foo?` is
* `{foo: 'bar'}`
* ```js
* questions.clearAnswers();
* ```
*
* @param {String} `key`
* @return {Object} Returns a question object.
* @api public
*/
Questions.prototype.toQuestion = function(key, value) {
var obj = {};
if (isReserved(key) && typeof value === 'string') {
obj[key] = value;
} else if (typeof key === 'string') {
obj.name = key;
} else if (typeof value === 'string') {
obj.message = value;
}
if (isObject(value)) {
obj = utils.merge({}, obj, value);
}
obj.name = stripQmark(obj.name || key);
if (!obj.message) {
obj.message = addQmark(obj.name);
}
obj.type = obj.type || 'input';
return obj;
Questions.prototype.clearAnswers = function() {
this.answers = sessionAnswers = {};
this.data = {};
};
/**
* Ask a question or array of questions.
* Clear all questions from the cache.
*
* ```js
* questions.ask(['name', 'homepage']);
* //=> { name: 'foo', homepage: 'https://github/foo' }
* questions.clearQuestions();
* ```
*
* @param {String} `key` Unique question id.
* @param {Object} `value` Question object that follows [inquirer] conventions.
* @api public
*/
Questions.prototype.ask = function(keys, cb) {
if (isObject(keys)) keys = [keys];
var questions = [];
if (typeof keys === 'function') {
cb = keys;
questions = this.queue;
} else {
questions = this.resolve(keys);
}
if (questions.length === 0) {
return cb(new Error('no questions found.'));
}
try {
this.prompt(questions, function(answers) {
cb(null, setEach({}, answers));
});
} catch(err) {
cb(err);
}
Questions.prototype.clearQuestions = function() {
this.cache = {};
this.queue = [];
};
/**
* Exposes the `prompt` method on [inquirer] as a convenience.
* Clear all cached questions and answers.
*
* ```js
* questions.prompt({
* type: 'list',
* name: 'chocolate',
* message: 'What\'s your favorite chocolate?',
* choices: ['Mars', 'Oh Henry', 'Hershey']
* }, function(answers) {
* //=> {chocolate: 'Hershey'}
* });
* questions.clear();
* ```
*
* @param {Object|Array} `question` Question object or array of question objects.
* @param {Object} `callback` Callback function.
* @api public
*/
Questions.prototype.prompt = function() {
return this.inquirer.prompt.apply(this.inquirer, arguments);
Questions.prototype.clear = function() {
this.clearQuestions();
this.clearAnswers();
};
/**
* Utility for setting values on properties defined using
* dot notation (object paths).
* Ask one or more questions, with the given `options` and callback.
*
* @param {object} `obj` Object to store values on.
* @param {object} `answers` Answers object.
* ```js
* questions.ask(['name', 'description'], function(err, answers) {
* console.log(answers);
* });
* ```
* @param {String|Array} `queue` Name or array of question names.
* @param {Object|Function} `options` Question options or callback function
* @param {Function} `callback` callback function
* @api public
*/
function setEach(obj, answers) {
for (var key in answers) {
if (answers.hasOwnProperty(key)) {
utils.set(obj, key, answers[key]);
}
Questions.prototype.ask = function(queue, config, cb) {
if (typeof queue === 'function') {
return this.ask.call(this, this.queue, {}, queue);
}
return obj;
}
function addQmark(str) {
if (str && str.slice(-1) !== '?') {
return str + '?';
if (typeof config === 'function') {
return this.ask.call(this, queue, {}, config);
}
return str;
}
function stripQmark(str) {
if (str && str.slice(-1) === '?') {
return str.slice(0, -1);
}
return str;
}
var questions = this.buildQueue(queue);
var self = this;
function isReserved(key) {
return ['name', 'input', 'message'].indexOf(key) > -1;
}
utils.async.reduce(questions, this.answers, function(answers, key, next) {
debug('asking question "%s"', key);
/**
* Utility for casting a value to an array.
*/
try {
var opts = utils.merge({}, self.options, config);
var data = utils.merge({}, self.data, opts);
function arrayify(val) {
return val ? (Array.isArray(val) ? val : [val]) : [];
}
var question = self.get(key);
var options = question._options = question.opts(opts);
var val = question.answer(answers, data, self);
debug('using answer %j', val);
// emit question before building options
self.emit('ask', val, key, question, answers);
// get val again after emitting `ask`
val = question.answer(answers, data, self);
// re-build options object after emitting ask, to allow
// user to update question options from a listener
options = question._options = question.opts(opts, question.options);
debug('using options %j', options);
if (options.enabled('skip')) {
debug('skipping question "%s", using answer "%j"', key, val);
question.next(val, self, answers, next);
return;
}
var force = options.get('force');
var isForced = force === true || utils.matchesKey(force, key);
if (!forced.hasOwnProperty(key)) {
forced[key] = true;
} else {
isForced = false;
}
if (!isForced && utils.isAnswer(val)) {
debug('question "%s", using answer "%j"', key, val);
utils.set(answers, key, val);
self.emit('answer', val, key, question, answers);
question.next(val, self, answers, next);
return;
}
self.inquirer.prompt([question], function(answer) {
debug('answered "%s" with "%j"', key, answer);
try {
var val = answer[key];
if (question.type === 'checkbox') {
val = utils.flatten(val);
}
if (!utils.isAnswer(val)) {
next(null, answers);
return;
}
// set answer on 'answers' cache
utils.set(answers, key, val);
// emit answer
self.emit('answer', val, key, question, answers);
// next question
question.next(val, self, answers, next);
} catch (err) {
self.emit('error', err);
next(err);
}
});
} catch (err) {
self.emit('error', err);
next(err);
}
}, function(err, answers) {
if (err) return cb(err);
self.emit('answers', answers);
cb(null, answers);
});
};
/**
* Utility for casting a value to an array.
* Build an array of names of questions to ask.
*
* @param {Array|String} keys
* @return {Object}
*/
function isObject(val) {
return utils.typeOf(val) === 'object';
}
Questions.prototype.buildQueue = function(questions) {
questions = utils.arrayify(questions);
var len = questions.length;
var queue = [];
var idx = -1;
if (len === 0) {
queue = this.queue;
}
while (++idx < len) {
utils.union(queue, this.normalize(questions[idx]));
}
return queue;
};
/**
* Utility for definining a non-enumerable property.
* Normalize the given value to return an array of question keys.
*
* @param {[type]} key
* @return {[type]}
* @api public
*/
function define(obj, prop, val) {
Object.defineProperty(obj, prop, {
configurable: true,
enumerable: false,
writable: true,
value: val
});
}
Questions.prototype.normalize = function(name) {
debug('normalizing %j', name);
// get `name` from question object
if (utils.isQuestion(name)) {
return [name.name];
}
if (this.cache.hasOwnProperty(name)) {
return [name];
}
// filter keys with dot-notation
var matched = 0;
var keys = [];
for (var prop in this.cache) {
if (this.cache.hasOwnProperty(prop)) {
if (prop.indexOf(name) === 0) {
keys.push(prop);
matched++;
}
}
}
return keys;
};
/**

@@ -362,0 +560,0 @@ * Expose `Questions`

'use strict';
/**
* Module dependencies
*/
var utils = require('lazy-cache')(require);
var fn = require;
require = utils;
/**
* Module dependencies
* Lazily required module dependencies
*/
require = utils;
require('arr-flatten', 'flatten');
require('arr-union', 'union');
require('async');
require('define-property', 'define');
require('get-value', 'get');
require('has-value', 'has');
require('inquirer2', 'inquirer');
require('kind-of', 'typeOf');
require('is-answer');
require('isobject', 'isObject');
require('mixin-deep', 'merge');
require('get-value', 'get');
require('has-value', 'has');
require('omit-empty');
require('project-name', 'project');
require('set-value', 'set');
require('to-choices');
require = fn;
utils.decorate = function(opts) {
utils.define(opts, 'set', function(prop, val) {
utils.set(this, prop, val);
return this;
}.bind(opts));
utils.define(opts, 'get', function(prop) {
return utils.get(this, prop);
}.bind(opts));
utils.define(opts, 'enabled', function(prop) {
return this.get(prop) === true;
}.bind(opts));
utils.define(opts, 'disabled', function(prop) {
return this.get(prop) === false;
}.bind(opts));
};
utils.isEmpty = function(answer) {
return !utils.isAnswer(answer);
};
utils.matchesKey = function(prop, key) {
if (typeof key !== 'string' || typeof prop !== 'string') {
return false;
}
if (prop === key) {
return true;
}
var len = prop.length;
var ch = key.charAt(len);
return key.indexOf(prop) === 0 && ch === '.';
};
/**
* Restore `require`
* Cast val to an array
*/
require = fn;
utils.arrayify = function(val) {
if (!val) return [];
return Array.isArray(val) ? val : [val];
};
/**
* Expose `utils` modules
* Returns true if a value is an object and appears to be a
* question object.
*/
utils.isQuestion = function(val) {
return utils.isObject(val) && (val.isQuestion || !utils.isOptions(val));
};
/**
* Returns true if a value is an object and appears to be an
* options object.
*/
utils.isOptions = function(val) {
if (!utils.isObject(val)) {
return false;
}
if (val.hasOwnProperty('locale')) {
return true;
}
if (val.hasOwnProperty('force')) {
return true;
}
if (val.hasOwnProperty('type')) {
return false;
}
if (val.hasOwnProperty('message')) {
return false;
}
if (val.hasOwnProperty('choices')) {
return false;
}
if (val.hasOwnProperty('name')) {
return false;
}
};
/**
* Expose `utils`
*/
module.exports = utils;
{
"name": "question-cache",
"description": "A wrapper around inquirer that makes it easy to create and selectively reuse questions.",
"version": "0.3.5",
"version": "0.4.0",
"homepage": "https://github.com/jonschlinkert/question-cache",

@@ -14,3 +14,3 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)",

"index.js",
"lib/"
"lib"
],

@@ -22,9 +22,21 @@ "main": "index.js",

"dependencies": {
"get-value": "^2.0.2",
"has-value": "^0.3.0",
"inquirer2": "github:jonschlinkert/inquirer2",
"kind-of": "^3.0.2",
"arr-flatten": "^1.0.1",
"arr-union": "^3.1.0",
"async": "1.5.2",
"debug": "^2.2.0",
"define-property": "^0.2.5",
"get-value": "^2.0.5",
"has-value": "^0.3.1",
"inquirer2": "^0.1.1",
"is-answer": "^0.1.0",
"isobject": "^2.0.0",
"lazy-cache": "^1.0.3",
"mixin-deep": "^1.1.3",
"set-value": "^0.3.2"
"omit-empty": "^0.3.6",
"option-cache": "^3.3.5",
"os-homedir": "^1.0.1",
"project-name": "^0.2.4",
"set-value": "^0.3.3",
"to-choices": "^0.2.0",
"use": "^1.1.2"
},

@@ -36,6 +48,8 @@ "devDependencies": {

"gulp-eslint": "^1.0.0",
"gulp-format-md": "^0.1.7",
"gulp-istanbul": "^0.10.2",
"gulp-mocha": "^2.1.3",
"mocha": "*",
"should": "*"
"gulp-unused": "^0.1.2",
"helper-example": "^0.1.0",
"mocha": "^2.4.5"
},

@@ -56,2 +70,11 @@ "keywords": [

"verb": {
"run": true,
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"related": {

@@ -71,7 +94,9 @@ "list": [

],
"layout": "default",
"plugins": [
"gulp-format-md"
"lint": {
"reflinks": true
},
"helpers": [
"helper-example"
]
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc