From 6553965b4cca20e300b75a9f5535cbec27a0638a Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 12 Feb 2017 03:43:04 +0700 Subject: [PATCH] More ES2015ification --- gulpfile.js | 8 +- lib/actions/help.js | 38 ++--- lib/actions/install.js | 20 +-- lib/actions/spawn-command.js | 4 +- lib/actions/user.js | 23 ++- lib/index.js | 6 +- lib/util/binary-diff.js | 58 +++---- lib/util/conflicter.js | 310 +++++++++++++++++----------------- lib/util/deprecate.js | 16 +- lib/util/prompt-suggestion.js | 43 +++-- lib/util/storage.js | 187 ++++++++++---------- package.json | 1 - test/base.js | 2 +- test/generators.js | 10 +- 14 files changed, 353 insertions(+), 373 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 498e60a9..5d9bffc5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -11,13 +11,13 @@ gulp.task('nsp', cb => { nsp({package: path.resolve('package.json')}, cb); }); -gulp.task('pre-test', () => { - return gulp.src([ +gulp.task('pre-test', () => + gulp.src([ 'lib/**/*.js' ]) .pipe(istanbul({includeUntested: true})) - .pipe(istanbul.hookRequire()); -}); + .pipe(istanbul.hookRequire()) +); gulp.task('test', ['pre-test'], cb => { let mochaErr; diff --git a/lib/actions/help.js b/lib/actions/help.js index 34b81eaa..a417cc31 100644 --- a/lib/actions/help.js +++ b/lib/actions/help.js @@ -3,7 +3,6 @@ const path = require('path'); const fs = require('fs'); const _ = require('lodash'); const table = require('text-table'); -const pathExists = require('path-exists'); /** * @mixin @@ -13,12 +12,12 @@ const help = module.exports; /** * Tries to get the description from a USAGE file one folder above the - * source root otherwise uses a default description. + * source root otherwise uses a default description */ - help.help = function () { - const filepath = path.join(this.sourceRoot(), '../USAGE'); - const exists = pathExists.sync(filepath); + const filepath = path.resolve(this.sourceRoot(), '../USAGE'); + const exists = fs.existsSync(filepath); + let out = [ 'Usage:', ' ' + this.usage(), @@ -52,10 +51,10 @@ help.help = function () { }; function formatArg(config) { - let arg = '<' + config.name + '>'; + let arg = `<${config.name}>`; if (!config.required) { - arg = '[' + arg + ']'; + arg = `[${arg}]`; } return arg; @@ -63,12 +62,11 @@ function formatArg(config) { /** * Output usage information for this given generator, depending on its arguments - * or options. + * or options */ - help.usage = function () { const options = Object.keys(this._options).length ? '[options]' : ''; - let name = ' ' + this.options.namespace; + let name = this.options.namespace; let args = ''; if (this._arguments.length > 0) { @@ -76,7 +74,7 @@ help.usage = function () { } name = name.replace(/^yeoman:/, ''); - let out = 'yo' + name + ' ' + options + ' ' + args; + let out = `yo ${name} ${options} ${args}`; if (this.description) { out += '\n\n' + this.description; @@ -105,9 +103,9 @@ help.argumentsHelp = function () { return [ '', config.name ? config.name : '', - config.description ? '# ' + config.description : '', - config.type ? 'Type: ' + config.type.name : '', - 'Required: ' + config.required + config.description ? `# ${config.description}` : '', + config.type ? `Type: ${config.type.name}` : '', + `Required: ${config.required}` ]; }); @@ -119,17 +117,15 @@ help.argumentsHelp = function () { * @returns {String} Text of options in formatted table */ help.optionsHelp = function () { - const options = _.reject(this._options, el => { - return el.hide; - }); + const options = _.reject(this._options, x => x.hide); const rows = options.map(opt => { return [ '', - opt.alias ? '-' + opt.alias + ', ' : '', - '--' + opt.name, - opt.description ? '# ' + opt.description : '', - (opt.default !== undefined && opt.default !== null && opt.default !== '') ? 'Default: ' + opt.default : '' + opt.alias ? `-${opt.alias}, ` : '', + `--${opt.name}`, + opt.description ? `# ${opt.description}` : '', + (opt.default !== undefined && opt.default !== '') ? 'Default: ' + opt.default : '' ]; }); diff --git a/lib/actions/install.js b/lib/actions/install.js index 914c6416..175af7e3 100644 --- a/lib/actions/install.js +++ b/lib/actions/install.js @@ -33,12 +33,12 @@ install.runInstall = function (installer, paths, options, cb, spawnOptions) { options = options || {}; spawnOptions = spawnOptions || {}; - cb = cb || function () {}; + cb = cb || (() => {}); paths = Array.isArray(paths) ? paths : (paths && paths.split(' ')) || []; let args = ['install'].concat(paths).concat(dargs(options)); - // Yarn uses the `add` command to specifically add a package to a project. + // Yarn uses the `add` command to specifically add a package to a project if (installer === 'yarn' && paths.length > 0) { args[0] = 'add'; } @@ -48,14 +48,14 @@ install.runInstall = function (installer, paths, options, cb, spawnOptions) { args = args.concat(['--cache-min', 24 * 60 * 60]); } - // Return early if we're skipping installation. + // Return early if we're skipping installation if (this.options.skipInstall) { cb(); return this; } this.env.runLoop.add('install', done => { - this.emit(installer + 'Install', paths); + this.emit(`${installer}Install`, paths); this.spawnCommand(installer, args, spawnOptions) .on('error', err => { console.log(chalk.red('Could not finish installation. \n') + @@ -65,11 +65,14 @@ install.runInstall = function (installer, paths, options, cb, spawnOptions) { cb(err); }) .on('exit', err => { - this.emit(installer + 'Install:end', paths); + this.emit(`${installer}Install:end`, paths); cb(err); done(); }); - }, {once: installer + ' ' + args.join(' '), run: false}); + }, { + once: installer + ' ' + args.join(' '), + run: false + }); return this; }; @@ -94,7 +97,6 @@ install.runInstall = function (installer, paths, options, cb, spawnOptions) { * @param {Boolean} [options.skipMessage=false] - whether to log the used commands * @param {Function} [options.callback] - call once all commands have run */ - install.installDependencies = function (options) { options = options || {}; const commands = []; @@ -157,7 +159,6 @@ install.installDependencies = function (options) { * @param {Function} [cb] * @param {Object} [spawnOptions] Options to pass `child_process.spawn`. */ - install.bowerInstall = function (cmpnt, options, cb, spawnOptions) { return this.runInstall('bower', cmpnt, options, cb, spawnOptions); }; @@ -172,10 +173,10 @@ install.bowerInstall = function (cmpnt, options, cb, spawnOptions) { * @param {Function} [cb] * @param {Object} [spawnOptions] Options to pass `child_process.spawn`. */ - install.npmInstall = function (pkgs, options, cb, spawnOptions) { return this.runInstall('npm', pkgs, options, cb, spawnOptions); }; + /** * Receives a list of `packages` and an `options` object to install through npm. * @@ -186,7 +187,6 @@ install.npmInstall = function (pkgs, options, cb, spawnOptions) { * @param {Function} [cb] * @param {Object} [spawnOptions] Options to pass `child_process.spawn`. */ - install.yarnInstall = function (pkgs, options, cb, spawnOptions) { return this.runInstall('yarn', pkgs, options, cb, spawnOptions); }; diff --git a/lib/actions/spawn-command.js b/lib/actions/spawn-command.js index 3ddb3c9a..8156d5c1 100644 --- a/lib/actions/spawn-command.js +++ b/lib/actions/spawn-command.js @@ -15,7 +15,7 @@ const spawnCommand = module.exports; * @param {Array} args * @param {object} [opt] */ -spawnCommand.spawnCommand = function (command, args, opt) { +spawnCommand.spawnCommand = (command, args, opt) => { opt = opt || {}; return spawn(command, args, _.defaults(opt, {stdio: 'inherit'})); }; @@ -27,7 +27,7 @@ spawnCommand.spawnCommand = function (command, args, opt) { * @param {Array} args * @param {object} [opt] */ -spawnCommand.spawnCommandSync = function (command, args, opt) { +spawnCommand.spawnCommandSync = (command, args, opt) => { opt = opt || {}; return spawn.sync(command, args, _.defaults(opt, {stdio: 'inherit'})); }; diff --git a/lib/actions/user.js b/lib/actions/user.js index 81c8d8d5..aa7d557a 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -2,8 +2,8 @@ const shell = require('shelljs'); const githubUsername = require('github-username'); -const nameCache = {}; -const emailCache = {}; +const nameCache = new Map(); +const emailCache = new Map(); /** * @mixin @@ -18,9 +18,8 @@ user.github = {}; * Retrieves user's name from Git in the global scope or the project scope * (it'll take what Git will use in the current context) */ - -user.git.name = function () { - let name = nameCache[process.cwd()]; +user.git.name = () => { + let name = nameCache.get(process.cwd()); if (name) { return name; @@ -28,7 +27,7 @@ user.git.name = function () { if (shell.which('git')) { name = shell.exec('git config --get user.name', {silent: true}).stdout.trim(); - nameCache[process.cwd()] = name; + nameCache.set(process.cwd(), name); } return name; @@ -38,9 +37,8 @@ user.git.name = function () { * Retrieves user's email from Git in the global scope or the project scope * (it'll take what Git will use in the current context) */ - -user.git.email = function () { - let email = emailCache[process.cwd()]; +user.git.email = () => { + let email = emailCache.get(process.cwd()); if (email) { return email; @@ -48,17 +46,16 @@ user.git.email = function () { if (shell.which('git')) { email = shell.exec('git config --get user.email', {silent: true}).stdout.trim(); - emailCache[process.cwd()] = email; + emailCache.set(process.cwd(), email); } return email; }; /** - * Retrieves GitHub's username from the GitHub API. + * Retrieves GitHub's username from the GitHub API */ - -user.github.username = function (cb) { +user.github.username = cb => { const promise = githubUsername(user.git.email()); if (cb) { diff --git a/lib/index.js b/lib/index.js index 1ef95f06..f7479cf2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,7 +1,7 @@ 'use strict'; const util = require('util'); const path = require('path'); -const events = require('events'); +const EventEmitter = require('events'); const assert = require('assert'); const _ = require('lodash'); const findUp = require('find-up'); @@ -59,7 +59,7 @@ const EMPTY = '@@_YEOMAN_EMPTY_MARKER_@@'; */ const Base = function (args, options) { - events.EventEmitter.call(this); + EventEmitter.call(this); if (!Array.isArray(args)) { options = args; @@ -142,7 +142,7 @@ const Base = function (args, options) { this.sourceRoot(path.join(path.dirname(this.resolved), 'templates')); }; -util.inherits(Base, events.EventEmitter); +util.inherits(Base, EventEmitter); // Mixin the actions modules _.extend(Base.prototype, require('./actions/install')); diff --git a/lib/util/binary-diff.js b/lib/util/binary-diff.js index 66705f08..8c34183f 100644 --- a/lib/util/binary-diff.js +++ b/lib/util/binary-diff.js @@ -6,40 +6,38 @@ const dateFormat = require('dateformat'); const prettyBytes = require('pretty-bytes'); const Table = require('cli-table'); -module.exports = { - isBinary(existingFilePath, newFileContents) { - const existingHeader = readChunk.sync(existingFilePath, 0, 512); - return istextorbinary.isBinarySync(undefined, existingHeader) || istextorbinary.isBinarySync(undefined, newFileContents); - }, +exports.isBinary = (existingFilePath, newFileContents) => { + const existingHeader = readChunk.sync(existingFilePath, 0, 512); + return istextorbinary.isBinarySync(undefined, existingHeader) || istextorbinary.isBinarySync(undefined, newFileContents); +}; - diff(existingFilePath, newFileContents) { - const existingStat = fs.statSync(existingFilePath); - const table = new Table({ - head: ['', 'Existing', 'Replacement', 'Diff'] - }); +exports.diff = (existingFilePath, newFileContents) => { + const existingStat = fs.statSync(existingFilePath); + const table = new Table({ + head: ['', 'Existing', 'Replacement', 'Diff'] + }); - let sizeDiff; + let sizeDiff; - if (existingStat.size > newFileContents.length) { - sizeDiff = '-'; - } else { - sizeDiff = '+'; - } + if (existingStat.size > newFileContents.length) { + sizeDiff = '-'; + } else { + sizeDiff = '+'; + } - sizeDiff += prettyBytes(Math.abs(existingStat.size - newFileContents.length)); + sizeDiff += prettyBytes(Math.abs(existingStat.size - newFileContents.length)); - table.push([ - 'Size', - prettyBytes(existingStat.size), - prettyBytes(newFileContents.length), - sizeDiff - ], [ - 'Last modified', - dateFormat(existingStat.mtime), - '', - '' - ]); + table.push([ + 'Size', + prettyBytes(existingStat.size), + prettyBytes(newFileContents.length), + sizeDiff + ], [ + 'Last modified', + dateFormat(existingStat.mtime), + '', + '' + ]); - return table.toString(); - } + return table.toString(); }; diff --git a/lib/util/conflicter.js b/lib/util/conflicter.js index b19c86b2..8454b11d 100644 --- a/lib/util/conflicter.js +++ b/lib/util/conflicter.js @@ -4,7 +4,6 @@ const path = require('path'); const async = require('async'); const detectConflict = require('detect-conflict'); const _ = require('lodash'); -const pathExists = require('path-exists'); const typedError = require('error/typed'); const binaryDiff = require('./binary-diff'); @@ -28,176 +27,177 @@ const AbortedError = typedError({ * @param {Boolean} force - When set to true, we won't check for conflict. (the * conflicter become a passthrough) */ -const Conflicter = function (adapter, force) { - this.force = force === true; - this.adapter = adapter; - this.conflicts = []; -}; - -/** - * Add a file to conflicter queue. - * - * @param {String} filepath - File destination path - * @param {String} contents - File new contents - * @param {Function} callback - callback to be called once we know if the user want to - * proceed or not. - */ -Conflicter.prototype.checkForCollision = function (filepath, contents, callback) { - this.conflicts.push({ - file: { - path: path.resolve(filepath), - contents - }, - callback - }); -}; +class Conflicter { + constructor(adapter, force) { + this.force = force === true; + this.adapter = adapter; + this.conflicts = []; + } -/** - * Process the _potential conflict_ queue and ask the user to resolve conflict when they - * occur. - * - * The user is presented with the following options: - * - * - `Y` Yes, overwrite - * - `n` No, do not overwrite - * - `a` All, overwrite this and all others - * - `q` Quit, abort - * - `d` Diff, show the differences between the old and the new - * - `h` Help, show this help - * - * @param {Function} cb Callback once every conflict are resolved. (note that each - * file can specify it's own callback. See `#checkForCollision()`) - */ -Conflicter.prototype.resolve = function (cb) { - cb = cb || _.noop; - - const self = this; - const resolveConflicts = function (conflict) { - return function (next) { - if (!conflict) { - next(); - return; - } + /** + * Add a file to conflicter queue + * + * @param {String} filepath - File destination path + * @param {String} contents - File new contents + * @param {Function} callback - callback to be called once we know if the user want to + * proceed or not. + */ + checkForCollision(filepath, contents, callback) { + this.conflicts.push({ + file: { + path: path.resolve(filepath), + contents + }, + callback + }); + } - self.collision(conflict.file, status => { - // Remove the resolved conflict from the queue - _.pull(self.conflicts, conflict); - conflict.callback(null, status); - next(); - }); + /** + * Process the _potential conflict_ queue and ask the user to resolve conflict when they + * occur + * + * The user is presented with the following options: + * + * - `Y` Yes, overwrite + * - `n` No, do not overwrite + * - `a` All, overwrite this and all others + * - `q` Quit, abort + * - `d` Diff, show the differences between the old and the new + * - `h` Help, show this help + * + * @param {Function} cb Callback once every conflict are resolved. (note that each + * file can specify it's own callback. See `#checkForCollision()`) + */ + resolve(cb) { + cb = cb || (() => {}); + + const resolveConflicts = conflict => { + return next => { + if (!conflict) { + next(); + return; + } + + this.collision(conflict.file, status => { + // Remove the resolved conflict from the queue + _.pull(this.conflicts, conflict); + conflict.callback(null, status); + next(); + }); + }; }; - }; - - async.series(this.conflicts.map(resolveConflicts), cb.bind(this)); -}; - -/** - * Check if a file conflict with the current version on the user disk. - * - * A basic check is done to see if the file exists, if it does: - * - * 1. Read its content from `fs` - * 2. Compare it with the provided content - * 3. If identical, mark it as is and skip the check - * 4. If diverged, prepare and show up the file collision menu - * - * @param {Object} file File object respecting this interface: { path, contents } - * @param {Function} cb Callback receiving a status string ('identical', 'create', - * 'skip', 'force') - * @return {null} nothing - */ -Conflicter.prototype.collision = function (file, cb) { - const rfilepath = path.relative(process.cwd(), file.path); - if (!pathExists.sync(file.path)) { - this.adapter.log.create(rfilepath); - cb('create'); - return; + async.series(this.conflicts.map(resolveConflicts), cb.bind(this)); } - if (this.force) { - this.adapter.log.force(rfilepath); - cb('force'); - return; - } + /** + * Check if a file conflict with the current version on the user disk + * + * A basic check is done to see if the file exists, if it does: + * + * 1. Read its content from `fs` + * 2. Compare it with the provided content + * 3. If identical, mark it as is and skip the check + * 4. If diverged, prepare and show up the file collision menu + * + * @param {Object} file File object respecting this interface: { path, contents } + * @param {Function} cb Callback receiving a status string ('identical', 'create', + * 'skip', 'force') + * @return {null} nothing + */ + collision(file, cb) { + const rfilepath = path.relative(process.cwd(), file.path); + + if (!fs.existsSync(file.path)) { + this.adapter.log.create(rfilepath); + cb('create'); + return; + } - if (detectConflict(file.path, file.contents)) { - this.adapter.log.conflict(rfilepath); - this._ask(file, cb); - } else { - this.adapter.log.identical(rfilepath); - cb('identical'); - } -}; + if (this.force) { + this.adapter.log.force(rfilepath); + cb('force'); + return; + } -/** - * Actual prompting logic - * @private - * @param {Object} file - * @param {Function} cb - */ -Conflicter.prototype._ask = function (file, cb) { - const rfilepath = path.relative(process.cwd(), file.path); - const prompt = { - name: 'action', - type: 'expand', - message: 'Overwrite ' + rfilepath + '?', - choices: [{ - key: 'y', - name: 'overwrite', - value: 'write' - }, { - key: 'n', - name: 'do not overwrite', - value: 'skip' - }, { - key: 'a', - name: 'overwrite this and all others', - value: 'force' - }, { - key: 'x', - name: 'abort', - value: 'abort' - }] - }; - - // Only offer diff option for files - if (fs.statSync(file.path).isFile()) { - prompt.choices.push({ - key: 'd', - name: 'show the differences between the old and the new', - value: 'diff' - }); + if (detectConflict(file.path, file.contents)) { + this.adapter.log.conflict(rfilepath); + this._ask(file, cb); + } else { + this.adapter.log.identical(rfilepath); + cb('identical'); + } } - this.adapter.prompt([prompt], result => { - if (result.action === 'abort') { - this.adapter.log.writeln('Aborting ...'); - throw new AbortedError(); + /** + * Actual prompting logic + * @private + * @param {Object} file + * @param {Function} cb + */ + _ask(file, cb) { + const rfilepath = path.relative(process.cwd(), file.path); + const prompt = { + name: 'action', + type: 'expand', + message: `Overwrite ${rfilepath}?`, + choices: [{ + key: 'y', + name: 'overwrite', + value: 'write' + }, { + key: 'n', + name: 'do not overwrite', + value: 'skip' + }, { + key: 'a', + name: 'overwrite this and all others', + value: 'force' + }, { + key: 'x', + name: 'abort', + value: 'abort' + }] + }; + + // Only offer diff option for files + if (fs.statSync(file.path).isFile()) { + prompt.choices.push({ + key: 'd', + name: 'show the differences between the old and the new', + value: 'diff' + }); } - if (result.action === 'diff') { - if (binaryDiff.isBinary(file.path, file.contents)) { - this.adapter.log.writeln(binaryDiff.diff(file.path, file.contents)); - } else { - const existing = fs.readFileSync(file.path); - this.adapter.diff(existing.toString(), (file.contents || '').toString()); + this.adapter.prompt([prompt], result => { + if (result.action === 'abort') { + this.adapter.log.writeln('Aborting ...'); + throw new AbortedError(); } - return this._ask(file, cb); - } + if (result.action === 'diff') { + if (binaryDiff.isBinary(file.path, file.contents)) { + this.adapter.log.writeln(binaryDiff.diff(file.path, file.contents)); + } else { + const existing = fs.readFileSync(file.path); + this.adapter.diff(existing.toString(), (file.contents || '').toString()); + } - if (result.action === 'force') { - this.force = true; - } + return this._ask(file, cb); + } - if (result.action === 'write') { - result.action = 'force'; - } + if (result.action === 'force') { + this.force = true; + } + + if (result.action === 'write') { + result.action = 'force'; + } - this.adapter.log[result.action](rfilepath); - return cb(result.action); - }); -}; + this.adapter.log[result.action](rfilepath); + return cb(result.action); + }); + } +} module.exports = Conflicter; diff --git a/lib/util/deprecate.js b/lib/util/deprecate.js index 1c73a0ec..dc76e2d8 100644 --- a/lib/util/deprecate.js +++ b/lib/util/deprecate.js @@ -2,36 +2,36 @@ const _ = require('lodash'); const chalk = require('chalk'); -const deprecate = function (message, fn) { +const deprecate = (message, fn) => { return function () { deprecate.log(message); return fn.apply(this, arguments); }; }; -deprecate.log = function (message) { +deprecate.log = message => { console.log(chalk.yellow('(!) ') + message); }; -deprecate.object = function (message, object) { +deprecate.object = (message, object) => { const msgTpl = _.template(message); const mirror = []; - Object.keys(object).forEach(name => { + for (const name of Object.keys(object)) { const func = object[name]; - if (!_.isFunction(func)) { + if (typeof func !== 'function') { mirror[name] = func; - return; + continue; } mirror[name] = deprecate(msgTpl({name}), func); - }); + } return mirror; }; -deprecate.property = function (message, object, property) { +deprecate.property = (message, object, property) => { const original = object[property]; Object.defineProperty(object, property, { get() { diff --git a/lib/util/prompt-suggestion.js b/lib/util/prompt-suggestion.js index 8aa2b63e..96c10ae2 100644 --- a/lib/util/prompt-suggestion.js +++ b/lib/util/prompt-suggestion.js @@ -9,47 +9,40 @@ const _ = require('lodash'); const promptSuggestion = module.exports; /** - * Returns the default value for a checkbox. + * Returns the default value for a checkbox * * @param {Object} question Inquirer prompt item * @param {*} defaultValue The stored default value * @return {*} Default value to set * @private */ -const getCheckboxDefault = function (question, defaultValue) { - // For simplicity we uncheck all boxes and - // use .default to set the active ones. - _.each(question.choices, choice => { +const getCheckboxDefault = (question, defaultValue) => { + // For simplicity we uncheck all boxes and use .default to set the active ones + for (const choice of question.choices) { if (typeof choice === 'object') { choice.checked = false; } - }); + } return defaultValue; }; /** - * Returns the default value for a list. + * Returns the default value for a list * * @param {Object} question Inquirer prompt item * @param {*} defaultValue The stored default value * @return {*} Default value to set * @private */ -const getListDefault = function (question, defaultValue) { - const choiceValues = _.map(question.choices, choice => { - if (typeof choice === 'object') { - return choice.value; - } - return choice; - }); - +const getListDefault = (question, defaultValue) => { + const choiceValues = question.choices.map(choice => typeof choice === 'object' ? choice.value : choice); return choiceValues.indexOf(defaultValue); }; /** * Return true if the answer should be store in - * the global store, otherwise false. + * the global store, otherwise false * * @param {Object} question Inquirer prompt item * @param {String|Array} answer The inquirer answer @@ -57,13 +50,15 @@ const getListDefault = function (question, defaultValue) { * @return {Boolean} Answer to be stored * @private */ -const storeListAnswer = function (question, answer, storeAll) { - const choiceValues = _.map(question.choices, choice => { +const storeListAnswer = (question, answer, storeAll) => { + const choiceValues = question.choices.map(choice => { if (Object.prototype.hasOwnProperty.call(choice, 'value')) { return choice.value; } + return choice; }); + const choiceIndex = choiceValues.indexOf(answer); // Check if answer is not equal to default value @@ -76,7 +71,7 @@ const storeListAnswer = function (question, answer, storeAll) { /** * Return true if the answer should be store in - * the global store, otherwise false. + * the global store, otherwise false * * @param {Object} question Inquirer prompt item * @param {String|Array} answer The inquirer answer @@ -84,7 +79,7 @@ const storeListAnswer = function (question, answer, storeAll) { * @return {Boolean} Answer to be stored * @private */ -const storeAnswer = function (question, answer, storeAll) { +const storeAnswer = (question, answer, storeAll) => { // Check if answer is not equal to default value or is undefined if (answer !== undefined && (storeAll || question.default !== answer)) { return true; @@ -94,13 +89,13 @@ const storeAnswer = function (question, answer, storeAll) { }; /** - * Prefill the defaults with values from the global store. + * Prefill the defaults with values from the global store * * @param {Store} store `.yo-rc-global` global config * @param {Array|Object} questions Original prompt questions * @return {Array} Prompt questions array with prefilled values. */ -promptSuggestion.prefillQuestions = function (store, questions) { +promptSuggestion.prefillQuestions = (store, questions) => { assert(store, 'A store parameter is required'); assert(questions, 'A questions parameter is required'); @@ -143,14 +138,14 @@ promptSuggestion.prefillQuestions = function (store, questions) { }; /** - * Store the answers in the global store. + * Store the answers in the global store * * @param {Store} store `.yo-rc-global` global config * @param {Array|Object} questions Original prompt questions * @param {Object} answers The inquirer answers * @param {Boolean} storeAll Should store default values */ -promptSuggestion.storeAnswers = function (store, questions, answers, storeAll) { +promptSuggestion.storeAnswers = (store, questions, answers, storeAll) => { assert(store, 'A store parameter is required'); assert(answers, 'A answers parameter is required'); assert(questions, 'A questions parameter is required'); diff --git a/lib/util/storage.js b/lib/util/storage.js index 8033e626..ced545a5 100644 --- a/lib/util/storage.js +++ b/lib/util/storage.js @@ -19,110 +19,105 @@ const _ = require('lodash'); * } * }); */ +class Storage { + constructor(name, fs, configPath) { + assert(name, 'A name parameter is required to create a storage'); + assert(configPath, 'A config filepath is required to create a storage'); + + this.path = configPath; + this.name = name; + this.fs = fs; + this.existed = Object.keys(this._store).length > 0; + } -const Storage = function (name, fs, configPath) { - assert(name, 'A name parameter is required to create a storage'); - assert(configPath, 'A config filepath is required to create a storage'); - - this.path = configPath; - this.name = name; - this.fs = fs; - this.existed = Object.keys(this._store()).length > 0; -}; - -/** - * Return the current store as JSON object - * @private - * @return {Object} the store content - */ -Storage.prototype._store = function () { - return this.fs.readJSON(this.path, {})[this.name] || {}; -}; - -/** - * Persist a configuration to disk - * @param {Object} val - current configuration values - */ -Storage.prototype._persist = function (val) { - const fullStore = this.fs.readJSON(this.path, {}); - fullStore[this.name] = val; - this.fs.write(this.path, JSON.stringify(fullStore, null, ' ')); -}; - -/** - * Save a new object of values - * @return {null} - */ - -Storage.prototype.save = function () { - this._persist(this._store()); -}; - -/** - * Get a stored value - * @param {String} key The key under which the value is stored. - * @return {*} The stored value. Any JSON valid type could be returned - */ - -Storage.prototype.get = function (key) { - return this._store()[key]; -}; - -/** - * Get all the stored values - * @return {Object} key-value object - */ - -Storage.prototype.getAll = function () { - return _.cloneDeep(this._store()); -}; - -/** - * Assign a key to a value and schedule a save. - * @param {String} key The key under which the value is stored - * @param {*} val Any valid JSON type value (String, Number, Array, Object). - * @return {*} val Whatever was passed in as val. - */ - -Storage.prototype.set = function (key, val) { - assert(!_.isFunction(val), 'Storage value can\'t be a function'); + /** + * Return the current store as JSON object + * @private + * @return {Object} the store content + */ + get _store() { + return this.fs.readJSON(this.path, {})[this.name] || {}; + } - const store = this._store(); + /** + * Persist a configuration to disk + * @param {Object} val - current configuration values + */ + _persist(val) { + const fullStore = this.fs.readJSON(this.path, {}); + fullStore[this.name] = val; + this.fs.write(this.path, JSON.stringify(fullStore, null, ' ')); + } - if (_.isObject(key)) { - val = _.extend(store, key); - } else { - store[key] = val; + /** + * Save a new object of values + * @return {null} + */ + save() { + this._persist(this._store); } - this._persist(store); - return val; -}; + /** + * Get a stored value + * @param {String} key The key under which the value is stored. + * @return {*} The stored value. Any JSON valid type could be returned + */ + get(key) { + return this._store[key]; + } -/** - * Delete a key from the store and schedule a save. - * @param {String} key The key under which the value is stored. - * @return {null} - */ + /** + * Get all the stored values + * @return {Object} key-value object + */ + getAll() { + return _.cloneDeep(this._store); + } -Storage.prototype.delete = function (key) { - const store = this._store(); - delete store[key]; - this._persist(store); -}; + /** + * Assign a key to a value and schedule a save. + * @param {String} key The key under which the value is stored + * @param {*} val Any valid JSON type value (String, Number, Array, Object). + * @return {*} val Whatever was passed in as val. + */ + set(key, val) { + assert(!_.isFunction(val), 'Storage value can\'t be a function'); + + const store = this._store; + + if (_.isObject(key)) { + val = _.extend(store, key); + } else { + store[key] = val; + } + + this._persist(store); + return val; + } -/** - * Setup the store with defaults value and schedule a save. - * If keys already exist, the initial value is kept. - * @param {Object} defaults Key-value object to store. - * @return {*} val Returns the merged options. - */ + /** + * Delete a key from the store and schedule a save. + * @param {String} key The key under which the value is stored. + * @return {null} + */ + delete(key) { + const store = this._store; + delete store[key]; + this._persist(store); + } -Storage.prototype.defaults = function (defaults) { - assert(_.isObject(defaults), 'Storage `defaults` method only accept objects'); - const val = _.defaults(this.getAll(), defaults); - this.set(val); - return val; -}; + /** + * Setup the store with defaults value and schedule a save. + * If keys already exist, the initial value is kept. + * @param {Object} defaults Key-value object to store. + * @return {*} val Returns the merged options. + */ + defaults(defaults) { + assert(_.isObject(defaults), 'Storage `defaults` method only accept objects'); + const val = _.defaults(this.getAll(), defaults); + this.set(val); + return val; + } +} module.exports = Storage; diff --git a/package.json b/package.json index 0c196b86..4fcc4373 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "error": "^7.0.2", "find-up": "^2.1.0", "github-username": "^3.0.0", - "glob": "^7.0.3", "istextorbinary": "^2.1.0", "lodash": "^4.11.1", "mem-fs-editor": "^3.0.0", diff --git a/test/base.js b/test/base.js index b7d4c70e..139d25d8 100644 --- a/test/base.js +++ b/test/base.js @@ -111,7 +111,7 @@ describe('Base', () => { }); it('set options with false values', done => { - var generator = helpers + const generator = helpers .run(path.join(__dirname, './fixtures/options-generator')) .withOptions({testOption: false}).on('end', () => { assert.equal(generator.options.testOption, false); diff --git a/test/generators.js b/test/generators.js index 34d35a2a..9b8a92cc 100644 --- a/test/generators.js +++ b/test/generators.js @@ -1,8 +1,8 @@ 'use strict'; -const events = require('events'); +const EventEmitter = require('events'); const Environment = require('yeoman-environment'); -const Base = require('..'); const assert = require('yeoman-assert'); +const Base = require('..'); describe('Generators module', () => { describe('Base', () => { @@ -15,9 +15,9 @@ describe('Generators module', () => { }); it('is an EventEmitter', function (done) { - assert.ok(this.generator instanceof events.EventEmitter); - assert.ok(typeof this.generator.on === 'function'); - assert.ok(typeof this.generator.emit === 'function'); + assert.ok(this.generator instanceof EventEmitter); + assert.strictEqual(typeof this.generator.on, 'function'); + assert.strictEqual(typeof this.generator.emit, 'function'); this.generator.on('yay-o-man', done); this.generator.emit('yay-o-man'); });