UNPKG

grunt-init

Version:

Generate project scaffolding from a template.

169 lines (157 loc) 5.58 kB
/* * grunt-init * https://gruntjs.com/ * * Copyright (c) 2014 "Cowboy" Ben Alman, contributors * Licensed under the MIT license. */ 'use strict'; // External lib. var _ = require('lodash'); var async = require('async'); var prompt = require('prompt'); prompt.message = '[' + '?'.green + ']'; prompt.delimiter = ' '; exports.init = function(grunt, helpers) { var exports = {}; // Expose prompts object so that prompts can be added or modified. exports.prompts = {}; var useDefaults = grunt.option('default'); // Prompt user to override default values passed in obj. exports.process = function(defaults, options, done) { // If defaults are omitted, shuffle arguments a bit. if (grunt.util.kindOf(defaults) === 'array') { done = options; options = defaults; defaults = {}; } // Keep track of any "sanitize" functions for later use. var sanitize = {}; options.forEach(function(option) { if (option.sanitize) { sanitize[option.name] = option.sanitize; } }); // Add one final "are you sure?" prompt. if (options.length > 0) { options.push({ message: 'Do you need to make any changes to the above before continuing?'.green, name: 'ANSWERS_VALID', default: 'y/N' }); } // Ask user for input. This is in an IIFE because it has to execute at least // once, and might be repeated. (function ask() { if (useDefaults) { grunt.log.subhead('Using defaults...'); } else { grunt.log.subhead('Please answer the following:'); } var result = _.clone(defaults); // Loop over each prompt option. async.forEachSeries(options, function(option, done) { var defaultValue; async.forEachSeries(['default', 'altDefault'], function(prop, next) { if (typeof option[prop] === 'function') { // If the value is a function, execute that function, using the // value passed into the return callback as the new default value. option[prop](defaultValue, result, function(err, value) { defaultValue = String(value); next(); }); } else { // Otherwise, if the value actually exists, use it. if (prop in option) { defaultValue = option[prop]; } next(); } }, function() { // Handle errors (there should never be errors). option.default = defaultValue; delete option.altDefault; // Wrap validator so that answering '?' always fails. var validator = option.validator; option.validator = function(line, next) { if (line === '?') { return next(false); } else if (validator) { if (validator.test) { return next(validator.test(line)); } else if (typeof validator === 'function') { return validator.length < 2 ? next(validator(line)) : validator(line, next); } } next(true); }; // Actually get user input. if (useDefaults) { result[option.name] = option.default; done(); } else { prompt.start(); prompt.getInput(option, function(err, line) { if (err) { return done(err); } option.validator = validator; result[option.name] = line; done(); }); } }); }, function() { // After all prompt questions have been answered... if (/n/i.test(result.ANSWERS_VALID)) { // User accepted all answers. Suspend prompt. prompt.pause(); // Clean up. delete result.ANSWERS_VALID; // Iterate over all results. async.forEachSeries(Object.keys(result), function(name, next) { // If this value needs to be sanitized, process it now. if (sanitize[name]) { sanitize[name](result[name], result, function(err, value) { if (err) { result[name] = err; } else if (arguments.length === 2) { result[name] = value === 'none' ? '' : value; } next(); }); } else { if (result[name] === 'none') { result[name] = ''; } next(); } }, function(err) { // Done! grunt.log.writeln(); done(err, result); }); } else { // Otherwise update the default value for each user prompt option... options.slice(0, -1).forEach(function(option) { option.default = result[option.name]; }); // ...and start over again. ask(); } }); }()); }; // Commonly-used prompt options with meaningful default values. exports.prompt = function(name, altDefault) { // Clone the option so the original options object doesn't get modified. var option = _.clone(exports.prompts[name] || {}); option.name = name; var defaults = helpers.readDefaults('defaults.json'); if (name in defaults) { // A user default was specified for this option, so use its value. option.default = defaults[name]; } else if (arguments.length === 2) { // An alternate default was specified, so use it. option.altDefault = altDefault; } return option; }; return exports; };