UNPKG

kitchensink

Version:

Dispatch's awesome components and style guide

286 lines (242 loc) 7.74 kB
var fs = require('fs'); var Vow = require('vow'); var Table = require('cli-table'); var prompt = require('prompt'); var chalk = require('chalk'); var assign = require('lodash').assign; var Checker = require('../checker'); var utils = require('../utils'); prompt.message = ''; prompt.delimiter = ''; prompt.start(); /** * Script that walks the user through the autoconfig flow * * @type {String[]} * @private */ var prompts = [ { name: chalk.green('Please choose a preset number:'), require: true, pattern: /\d+/ }, { name: 'Create an (e)xception for this rule, or (f)ix the errors yourself?', require: true, pattern: /(e|f)/ } ]; /** * JSCS Configuration Generator * * @name Generator */ function Generator() { this._config = {}; } /** * Generates a configuration object based for the given path * based on the best fitting preset * * @param {String} path - The path containing file(s) used to guide the configuration * * @return {Promise} Resolved with the generated, JSCS configuration */ Generator.prototype.generate = function(path) { var checker = getChecker(); var _path = utils.normalizePath(path, checker.getConfiguration().getBasePath()); var presetNames = Object.keys(checker.getConfiguration().getRegisteredPresets()); var statsForPresets; console.log('Checking', _path, 'against the presets'); return Vow .all(presetNames.map(this._checkAgainstPreset.bind(this, _path))) .then(function(resultsPerPreset) { statsForPresets = this._generateStatsForPresets(resultsPerPreset, presetNames); return statsForPresets; }.bind(this)) .then(this._showErrorCounts.bind(this)) .then(this._getUserPresetChoice.bind(this, prompts[0])) .then(function showViolatedRules(choiceObj) { var presetIndex = choiceObj[prompts[0].name] - 1; var presetName = statsForPresets[presetIndex].name; console.log('You chose the ' + presetName + ' preset'); this._config.preset = presetName; var errorStats = getErrorsByRuleName(statsForPresets[presetIndex].errors); var violatedRuleCount = Object.keys(errorStats).length; if (!violatedRuleCount) { return this._config; } console.log(_path + ' violates ' + violatedRuleCount + ' rule' + (violatedRuleCount > 1 ? 's' : '')); var errorPrompts = generateRuleHandlingPrompts(errorStats); return this._getUserViolationChoices(errorPrompts) .then(this._handleViolatedRules.bind(this, errorPrompts)) .then(function() { return this._config; }.bind(this)); }.bind(this)) .then(function flushConfig() { fs.writeFileSync(process.cwd() + '/.jscsrc', JSON.stringify(this._config, null, '\t')); console.log('Generated a .jscsrc configuration file in ' + process.cwd()); }.bind(this)); }; /** * @private * @param {Array.<Object[]>} resultsPerPreset - List of error objects for each preset's run of checkPath * @param {String[]} presetNames * @return {Object[]} Aggregated datapoints for each preset */ Generator.prototype._generateStatsForPresets = function(resultsPerPreset, presetNames) { return resultsPerPreset.map(function(presetResults, idx) { var errorCollection = [].concat.apply([], presetResults); var presetStats = { name: presetNames[idx], sum: 0, errors: [] }; errorCollection.forEach(function(error) { presetStats.sum += error.getErrorCount(); presetStats.errors = presetStats.errors.concat(error.getErrorList()); }); return presetStats; }); }; /** * @private * @param {Object[]} statsForPresets */ Generator.prototype._showErrorCounts = function(statsForPresets) { var table = getTable(); statsForPresets.forEach(function(presetStats, idx) { table.push([idx + 1, presetStats.name, presetStats.sum, getUniqueErrorNames(presetStats.errors).length]); }); console.log(table.toString()); }; /** * Prompts the user to choose a preset * * @private * @param {Object} prompt * @return {Promise} */ Generator.prototype._getUserPresetChoice = function(prompt) { return this._showPrompt(prompt); }; /** * Prompts the user to nullify rules or fix violations themselves * * @private * @param {Object[]} errorPrompts * @return {Promise} */ Generator.prototype._getUserViolationChoices = function(errorPrompts) { return this._showPrompt(errorPrompts); }; /** @private */ Generator.prototype._showPrompt = utils.promisify(prompt.get.bind(prompt)); /** * @private * @param {Object[]} errorPrompts * @param {Object} choices */ Generator.prototype._handleViolatedRules = function(errorPrompts, choices) { errorPrompts.forEach(function(errorPrompt) { var userChoice = choices[errorPrompt.name]; if (userChoice.toLowerCase() === 'e') { this._config[errorPrompt.associatedRuleName] = null; } }, this); }; /** * @private * @param {String} path * @param {String} presetName * @return {Promise} */ Generator.prototype._checkAgainstPreset = function(path, presetName) { var checker = getChecker(); checker.configure({preset: presetName, maxErrors: Infinity}); return checker.checkPath(path); }; /** * @private * @return {module:lib/Checker} */ function getChecker() { var checker = new Checker(); checker.registerDefaultRules(); return checker; } /** * @private * @param {Object} errors * @return {Object[]} */ function generateRuleHandlingPrompts(errors) { // Generate list of rule names, sorted by violation count (descending) var violatedRuleNames = Object.keys(errors); violatedRuleNames.sort(function(a, b) { return errors[b].violations - errors[a].violations; }); return violatedRuleNames.map(function(ruleName) { var violationCount = errors[ruleName].violations; var fileCount = Object.keys(errors[ruleName].files).length; var prompt = assign({}, prompts[1]); prompt.associatedRuleName = ruleName; prompt.name = chalk.green(ruleName) + ' (' + violationCount + ' violation' + (violationCount > 1 ? 's' : '') + ' in ' + fileCount + ' file' + (fileCount > 1 ? 's' : '') + '):\n ' + prompt.name; return prompt; }); } /** * @private * @param {Object[]} errorsList * @return {Object} */ function getErrorsByRuleName(errorsList) { var errors = {}; errorsList.forEach(function(error) { var rulename = error.rule; errors[rulename] = errors[rulename] || { files: {}, violations: 0 }; errors[rulename].violations += 1; errors[rulename].files[error.filename] = true; }); return errors; } /** * @private * @param {Object[]} errorsList * @return {String[]} */ function getUniqueErrorNames(errorsList) { var errorNameLUT = {}; errorsList.forEach(function(error) { errorNameLUT[error.rule] = true; }); return Object.keys(errorNameLUT); } /** * @private * @return {Object} */ function getTable() { return new Table({ chars: { top: '', 'top-mid': '', 'top-left': '', 'top-right': '', bottom: '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '', left: '', 'left-mid': '', mid: '', 'mid-mid': '', right: '', 'right-mid': '' , middle: ' ' }, style: { 'padding-left': 0, 'padding-right': 0 }, head: ['', 'Preset', '#Errors', '#Rules'] }); } module.exports = Generator;