From edeb75cf4b63d5d60c6e292742df48aea2413b8c Mon Sep 17 00:00:00 2001 From: Martin Heidegger Date: Sun, 19 Jan 2020 09:59:57 +0900 Subject: [PATCH] Started using new code style. --- adventure.js | 180 ++++--- default/fail.js | 6 +- default/footer.js | 4 +- default/header.js | 7 +- default/help.js | 6 +- default/pass.js | 6 +- i18n.js | 109 ++-- index.js | 910 +++++++++++++++++----------------- lib/commands/completed.js | 10 +- lib/commands/credits.js | 58 +-- lib/commands/current.js | 6 +- lib/commands/help.js | 4 +- lib/commands/language-list.js | 7 +- lib/commands/language.js | 43 +- lib/commands/list.js | 7 +- lib/commands/menu.js | 66 ++- lib/commands/next.js | 4 +- lib/commands/print.js | 10 +- lib/commands/repo.js | 2 +- lib/commands/reset.js | 2 +- lib/commands/run.js | 23 +- lib/commands/select.js | 4 +- lib/commands/update.js | 46 +- lib/commands/verify.js | 23 +- lib/commands/version.js | 2 +- lib/createExerciseMeta.js | 26 +- lib/langFilter.js | 4 +- lib/modifiers/language.js | 2 +- lib/modifiers/version.js | 2 +- lib/msee.js | 4 +- lib/mseePipe.js | 10 +- lib/print.js | 250 +++++----- util.js | 16 +- workshopper.js | 65 ++- 34 files changed, 965 insertions(+), 959 deletions(-) diff --git a/adventure.js b/adventure.js index f6befe7..e1cfdbd 100644 --- a/adventure.js +++ b/adventure.js @@ -1,9 +1,5 @@ -const inherits = require('util').inherits - -/* jshint -W079 */ -const Core = require('./index') +const WorkshopperAdventure = require('./index') const error = require('./lib/print').error -/* jshint +W079 */ function legacyCommands (item) { if (!item.aliases) { @@ -15,96 +11,146 @@ function legacyCommands (item) { return item } -function LegacyAdventure (options) { - if (!(this instanceof LegacyAdventure)) { - return new LegacyAdventure(options) +class LegacyAdventure extends WorkshopperAdventure { + constructor (options = {}) { + if (typeof options === 'string') { + options = { name: options } + } + if (typeof options !== 'object') { + return error('You need to provide an options object') + } + if (!options.commands) { + options.commands = options.menuItems + } + if (options.commands) { + options.commands = options.commands.map(legacyCommands) + } + if (options.modifiers) { + options.modifiers = options.modifiers.map(legacyCommands) + } + + if (options.helpFile) { + options.help = { file: options.helpFile } + } + + if (!options.footer) { + if (options.footerFile) { + options.footer = { file: options.footerFile } + } + } + + if (!options.defaultOutputType) { + options.defaultOutputType = 'txt' + } + + if (options.hideSolutions === undefined) { + options.hideSolutions = true + } + + if (options.hideRemaining === undefined) { + options.hideRemaining = true + } + + super(options) + // an `onComplete` hook function *must* call the callback given to it when it's finished, async or not + if (typeof options.onComplete === 'function') { + this.onComplete = options.onComplete + } + + if (options.execute === 'now') { + this.execute(process.argv.slice(2)) + } else if (options.execute === 'immediatly') { + setImmediate(this.execute.bind(this, process.argv.slice(2))) + } + } + + // backwards compatibility support + get title () { + return this.__('title') } - if (typeof options === 'string') { - options = { name: options } + + get subtitle () { + return this.__('subtitle') } - if (!options) { - options = {} + + get name () { + return this.__('name') } - if (typeof options !== 'object') { - return error('You need to provide an options object') + + get appName () { + return this.__('name') } - if (!options.commands) { - options.commands = options.menuItems + + get appname () { + return this.__('name') } - if (options.commands) { - options.commands = options.commands.map(legacyCommands) + + get lang () { + return this.i18n.lang('lang') } - if (options.modifiers) { - options.modifiers = options.modifiers.map(legacyCommands) + + get width () { + return this.menuFactory.options.width } - if (options.helpFile) { - options.help = { file: options.helpFile } + get helpFile () { + return this.options.helpFile } - if (!options.footer) { - if (options.footerFile) { - options.footer = { file: options.footerFile } - } + get footer () { + return this.options.footer } - if (!options.defaultOutputType) { - options.defaultOutputType = 'txt' + get defaultLang () { + return this.options.defaultLang } - if (options.hideSolutions === undefined) { - options.hideSolutions = true + get languages () { + return this.options.languages } - if (options.hideRemaining === undefined) { - options.hideRemaining = true + get globalDataDir () { + return this.globalStorage.dir } - // an `onComplete` hook function *must* call the callback given to it when it's finished, async or not - if (typeof options.onComplete === 'function') { - this.onComplete = options.onComplete + get dataDir () { + return this.appStorage.dir } - Core.call(this, options) - if (options.execute === 'now') { - this.execute(process.argv.slice(2)) - } else if (options.execute === 'immediatly') { - setImmediate(this.execute.bind(this, process.argv.slice(2))) + // adventure + get datadir () { + return this.appStorage.dir } - // backwards compatibility support - this.__defineGetter__('title', this.__.bind(this, 'title')) - this.__defineGetter__('subtitle', this.__.bind(this, 'subtitle')) - this.__defineGetter__('name', this.__.bind(this, 'name')) - this.__defineGetter__('appName', this.__.bind(this, 'name')) - this.__defineGetter__('appname', this.__.bind(this, 'name')) - this.__defineGetter__('lang', this.i18n.lang.bind(this.i18n, 'lang')) - this.__defineGetter__('width', function () { return this.menuFactory.options.width }.bind(this)) - this.__defineGetter__('helpFile', function () { return this.options.helpFile }.bind(this)) - this.__defineGetter__('footer', function () { return this.options.footer }.bind(this)) - this.__defineGetter__('defaultLang', function () { return this.options.defaultLang }.bind(this)) - this.__defineGetter__('languages', function () { return this.options.languages }.bind(this)) - this.__defineGetter__('globalDataDir', function () { return this.globalStorage.dir }.bind(this)) - this.__defineGetter__('dataDir', function () { return this.appStorage.dir }.bind(this)) - this.__defineGetter__('datadir', function () { return this.appStorage.dir }.bind(this)) // adventure - this.__defineGetter__('appDir', function () { return this.options.appDir }.bind(this)) - this.__defineGetter__('exerciseDir', function () { return this.options.exerciseDir }.bind(this)) - this.__defineGetter__('current', function () { return this.appStorage.get('current') }.bind(this)) - this.__defineGetter__('_adventures', function () { return this.exercises }.bind(this)) - this.__defineGetter__('state', function () { + get appDir () { + return this.options.appDir + } + + get exerciseDir () { + return this.options.exerciseDir + } + + get current () { + return this.appStorage.get('current') + } + + get _adventures () { + return this.exercises + } + + get state () { return { completed: this.appStorage.get('completed'), current: this.appStorage.get('current') } - }) -} + } -inherits(LegacyAdventure, Core) -LegacyAdventure.prototype.processResult = function (result, stream) { - if (result) { - stream.append(['```', result, '```']) + processResult (result, stream) { + if (result) { + stream.append(['```', result, '```']) + } + return stream } - return stream } module.exports = LegacyAdventure diff --git a/default/fail.js b/default/fail.js index aa6a900..5eb2c44 100644 --- a/default/fail.js +++ b/default/fail.js @@ -1,4 +1,6 @@ module.exports = { - text: '\n{bold}{red}# {solution.fail.title}{/red}{/bold}\n' + - '{solution.fail.message}\n' + text: ` +{bold}{red}# {solution.fail.title}{/red}{/bold} +{solution.fail.message} +` } diff --git a/default/footer.js b/default/footer.js index be9bc27..905c634 100644 --- a/default/footer.js +++ b/default/footer.js @@ -1,8 +1,8 @@ -var path = require('path') +const { join } = require('path') module.exports = [ '---', { - file: path.join(__dirname, '../i18n/footer/{lang}.md') + file: join(__dirname, '../i18n/footer/{lang}.md') } ] diff --git a/default/header.js b/default/header.js index 7f873d8..da0d72b 100644 --- a/default/header.js +++ b/default/header.js @@ -1,6 +1,7 @@ module.exports = { - text: '# {title}' + - '\n## __{currentExercise.name}__ (_{progress.state_resolved}_)' + - '\n\n', + text: `# {title} +## __{currentExercise.name}__ (_{progress.state_resolved}_) + +`, type: 'md' } diff --git a/default/help.js b/default/help.js index 23595dc..dc5b027 100644 --- a/default/help.js +++ b/default/help.js @@ -1,8 +1,8 @@ -var path = require('path') +const { join } = require('path') module.exports = { file: [ - path.join(__dirname, '../i18n/usage/{lang}.md'), - path.join(__dirname, '../i18n/usage/en.md') + join(__dirname, '../i18n/usage/{lang}.md'), + join(__dirname, '../i18n/usage/en.md') ] } diff --git a/default/pass.js b/default/pass.js index 121ad2a..f009ddc 100644 --- a/default/pass.js +++ b/default/pass.js @@ -1,4 +1,6 @@ module.exports = { - text: '\n{bold}{green}# {solution.pass.title}{/green}{/bold}\n' + - '{bold}{solution.pass.message}{/bold}\n' + text: ` +{bold}{green}# {solution.pass.title}{/green}{/bold} +{bold}{solution.pass.message}{/bold} +` } diff --git a/i18n.js b/i18n.js index 85cc9be..9215535 100644 --- a/i18n.js +++ b/i18n.js @@ -13,7 +13,7 @@ function commandify (s) { function chooseLang (globalStorage, appStorage, defaultLang, availableLangs, lang) { if (!!lang && typeof lang !== 'string') { - throw new Error('Please supply a language. Available languages are: ' + availableLangs.join(', ')) + throw new Error(`Please supply a language. Available languages are: ${availableLangs.join(', ')}`) } if (lang) { @@ -21,14 +21,14 @@ function chooseLang (globalStorage, appStorage, defaultLang, availableLangs, lan } if (availableLangs.indexOf(defaultLang) === -1) { - throw new Error('The default language "' + defaultLang + ' is not one of the available languages?! Available languages are: ' + availableLangs.join(', ')) + throw new Error(`The default language "${defaultLang}" is not one of the available languages?! Available languages are: ${availableLangs.join(', ')}`) } if (lang && availableLangs.indexOf(lang) === -1) { - throw new Error('The language "' + lang + '" is not available.\nAvailable languages are ' + availableLangs.join(', ') + '.\n\nNote: the language is not case-sensitive ("en", "EN", "eN", "En" will become "en") and you can use "_" instead of "-" for seperators.') + throw new Error(`The language "${lang}" is not available.\nAvailable languages are ${availableLangs.join(', ')}.\n\nNote: the language is not case-sensitive ("en", "EN", "eN", "En" will become "en") and you can use "_" instead of "-" for seperators.`) } - var data = ((appStorage && appStorage.get('lang')) || globalStorage.get('lang') || {}) + const data = ((appStorage && appStorage.get('lang')) || globalStorage.get('lang') || {}) if (availableLangs.indexOf(data.selected) === -1) { // The stored data is not available so lets use one of the other languages @@ -44,68 +44,61 @@ function chooseLang (globalStorage, appStorage, defaultLang, availableLangs, lan return data.selected } -module.exports = { - init: function (options, globalStorage, appStorage) { - var lookup = i18nChain( - options.appDir ? i18nFs(path.resolve(options.appDir, './i18n')) : null - , i18nFs(path.resolve(__dirname, './i18n')) - ) - var root = i18n(lookup) - var choose = chooseLang.bind(null, globalStorage, appStorage, options.defaultLang, options.languages) - var lang = choose(null) - var translator = root.section(lang, true) - // TODO: _excercises is unused... is this ok? - // eslint-disable-next-line - var result = i18n(i18nExtend(translator, function (key) { - if (options[key]) { - return options[key] - } +exports.init = (options, globalStorage, appStorage) => { + const lookup = i18nChain( + options.appDir ? i18nFs(path.resolve(options.appDir, './i18n')) : null + , i18nFs(path.resolve(__dirname, './i18n')) + ) + const root = i18n(lookup) + root.fallback = key => `?${key}?` + const choose = chooseLang.bind(null, globalStorage, appStorage, options.defaultLang, options.languages) + let lang = choose(null) + const translator = root.section(lang, true) + // TODO: _excercises is unused... is this ok? + // eslint-disable-next-line + const result = i18n(i18nExtend(translator, key => { + if (options[key]) { + return options[key] + } - // legacy -- start - if (key === 'title') { - return options.name.toUpperCase() - } + // legacy -- start + if (key === 'title') { + return options.name.toUpperCase() + } - if (key === 'appName' || key === 'appname' || key === 'ADVENTURE_NAME') { - return options.name - } + if (key === 'appName' || key === 'appname' || key === 'ADVENTURE_NAME') { + return options.name + } - if (key === 'rootdir') { - return options.appDir - } + if (key === 'rootdir') { + return options.appDir + } - if (key === 'COMMAND' || key === 'ADVENTURE_COMMAND') { - return commandify(options.name) - // legacy -- end - } + if (key === 'COMMAND' || key === 'ADVENTURE_COMMAND') { + return commandify(options.name) + // legacy -- end + } - var exercisePrefix = 'exercise.' - if (key.indexOf(exercisePrefix) === 0) { - return key.substr(exercisePrefix.length) - } - if (key.length > UNDERLINE.length) { - var end = key.length - UNDERLINE.length - if (key.indexOf(UNDERLINE) === end) { - return util.repeat('\u2500', stripColor(result.__(key.substr(0, end))).length + 2) - } + var exercisePrefix = 'exercise.' + if (key.indexOf(exercisePrefix) === 0) { + return key.substr(exercisePrefix.length) + } + if (key.length > UNDERLINE.length) { + var end = key.length - UNDERLINE.length + if (key.indexOf(UNDERLINE) === end) { + return util.repeat('\u2500', stripColor(result.__(key.substr(0, end))).length + 2) } - })) - - root.fallback = function (key) { - return '?' + key + '?' } - result.change = function (lng) { + })) + return { + ...result, + change: lng => { lang = choose(lng) translator.changeSection(lang) - } - result.extend = function (obj) { - return i18n(i18nExtend(result, function (key) { - return obj[key] - })) - } - result.lang = function () { - return lang - } - return result + }, + extend: obj => { + return i18n(i18nExtend(result, key => obj[key])) + }, + lang: () => lang } } diff --git a/index.js b/index.js index 7e85fe3..8f961eb 100644 --- a/index.js +++ b/index.js @@ -1,579 +1,573 @@ const path = require('path') const commandico = require('commandico') -const inherits = require('util').inherits -const EventEmitter = require('events').EventEmitter +const { EventEmitter } = require('events') const util = require('./util') const PrintStream = require('./lib/print') const storage = require('workshopper-adventure-storage') -function WA (options) { - if (!(this instanceof WA)) { - return new WA(options) - } - - if (!options) { - options = {} - } +class WorkshopperAdventure extends EventEmitter { + constructor (options = {}) { + super() + if (options.appDir) { + options.appDir = util.getDir(options.appDir, '.') + if (!options.name) { + try { + options.name = require(path.join(options.appDir, 'package.json')).name + } catch (e) {} + } + } - if (options.appDir) { - options.appDir = util.getDir(options.appDir, '.') if (!options.name) { - try { - options.name = require(path.join(options.appDir, 'package.json')).name - } catch (e) {} + throw new Error('The workshopper needs a name to store the progress.') } - } - if (!options.name) { - throw new Error('The workshopper needs a name to store the progress.') - } + if (!options.languages) { + options.languages = ['en'] + } - if (!options.languages) { - options.languages = ['en'] - } + if (!options.defaultLang) { + options.defaultLang = options.languages[0] + } - if (!options.defaultLang) { - options.defaultLang = options.languages[0] - } + if (!options.defaultOutputType) { + options.defaultOutputType = 'md' + } - if (!options.defaultOutputType) { - options.defaultOutputType = 'md' - } + if (!options.pkg && options.appDir) { + try { + options.pkg = require(path.join(options.appDir, 'package.json')) + } catch (e) {} + } - if (!options.pkg && options.appDir) { - try { - options.pkg = require(path.join(options.appDir, 'package.json')) - } catch (e) {} - } + if (!options.appRepo && options.pkg) { + options.appRepo = options.pkg.repository.url + } - if (!options.appRepo && options.pkg) { - options.appRepo = options.pkg.repository.url - } + if (!options.version && options.pkg) { + options.version = options.pkg.version + } - if (!options.version && options.pkg) { - options.version = options.pkg.version - } + if (options.appDir) { + options.exerciseDir = util.getDir(options.exerciseDir || 'exercises', options.appDir) + } - if (options.appDir) { - options.exerciseDir = util.getDir(options.exerciseDir || 'exercises', options.appDir) - } + if (!options.menu) { + options.menu = { + width: 73, + x: 2, + y: 2 + } + } - if (!options.menu) { - options.menu = { - width: 73, - x: 2, - y: 2 + if (options.requireSubmission === undefined) { + options.requireSubmission = false } - } - if (options.requireSubmission === undefined) { - options.requireSubmission = false - } + if (!options.menuFactory) { + options.menuFactory = require('simple-terminal-menu/factory')(options.menu, {}) + } - if (!options.menuFactory) { - options.menuFactory = require('simple-terminal-menu/factory')(options.menu, {}) - } + EventEmitter.call(this) - EventEmitter.call(this) + this.options = options - this.options = options + const globalStorage = storage(storage.userDir, '.config', 'workshopper') + this.appStorage = storage(storage.userDir, '.config', options.name) - var globalStorage = storage(storage.userDir, '.config', 'workshopper') - this.appStorage = storage(storage.userDir, '.config', options.name) + this.exercises = [] + this._meta = {} - this.exercises = [] - this._meta = {} + try { + this.i18n = require('./i18n').init(options, globalStorage, this.appStorage) + } catch (e) { + console.log(e.message) + process.exit(1) + } + this.__ = this.i18n.__ + this.__n = this.i18n.__n - try { - this.i18n = require('./i18n').init(options, globalStorage, this.appStorage) - } catch (e) { - console.log(e.message) - process.exit(1) - } - this.__ = this.i18n.__ - this.__n = this.i18n.__n + this.cli = commandico(this, 'menu') + .loadCommands(path.resolve(__dirname, './lib/commands')) + .loadModifiers(path.resolve(__dirname, './lib/modifiers')) - this.cli = commandico(this, 'menu') - .loadCommands(path.resolve(__dirname, './lib/commands')) - .loadModifiers(path.resolve(__dirname, './lib/modifiers')) + if (options.commands) { + this.cli.addCommands(options.commands) + } - if (options.commands) { - this.cli.addCommands(options.commands) + if (options.modifiers) { + this.cli.addModifiers(options.modifiers) + } } - if (options.modifiers) { - this.cli.addModifiers(options.modifiers) + execute (args) { + return this.cli.execute(args) } -} -inherits(WA, EventEmitter) + add (nameOrObject, fnOrObject, fn) { + try { + const meta = require('./lib/createExerciseMeta')(this.options.exerciseDir, nameOrObject, fnOrObject, fn) + return this.addExercise(meta) + } catch (e) { + console.log(e) + return new Error(this.__(`error.exercise.${e.id}`, e)) + } + } -WA.prototype.execute = function (args) { - return this.cli.execute(args) -} + addAll (list) { + return list.map(entry => this.add(entry)) + } -WA.prototype.add = function (nameOrObject, fnOrObject, fn) { - var meta - try { - meta = require('./lib/createExerciseMeta')(this.options.exerciseDir, nameOrObject, fnOrObject, fn) - } catch (e) { - console.log(e) - return new Error(this.__('error.exercise.' + e.id, e)) + addExercise (meta) { + this.exercises.push(meta.name) + this._meta[meta.id] = meta + meta.number = this.exercises.length + return this } - return this.addExercise(meta) -} -WA.prototype.addAll = function (list) { - return list.map(this.add.bind(this)) -} + getVersionString () { + return `${this.options.name}@${this.options.version}` + } -WA.prototype.addExercise = function (meta) { - this.exercises.push(meta.name) - this._meta[meta.id] = meta - meta.number = this.exercises.length - return this -} + countRemaining () { + const completed = this.appStorage.get('completed') + return this.exercises.length - (completed ? completed.length : 0) + } -WA.prototype.getVersionString = function () { - return this.options.name + '@' + this.options.version -} + markCompleted (exerciseName, cb) { + const completed = this.appStorage.get('completed') || [] -WA.prototype.countRemaining = function () { - var completed = this.appStorage.get('completed') - return this.exercises.length - (completed ? completed.length : 0) -} + if (completed.indexOf(exerciseName) === -1) { + completed.push(exerciseName) + } -WA.prototype.markCompleted = function (exerciseName, cb) { - var completed = this.appStorage.get('completed') || [] + this.appStorage.save('completed', completed) - if (completed.indexOf(exerciseName) === -1) { - completed.push(exerciseName) + if (this.onComplete.length === 0) { + throw new Error('The workshoppers `.onComplete` method must have at least one callback argument') + } + return this.onComplete(cb) } - this.appStorage.save('completed', completed) - - if (this.onComplete.length === 0) { - throw new Error('The workshoppers `.onComplete` method must have at least one callback argument') - } - return this.onComplete(cb) -} + getNext () { + const current = this.appStorage.get('current') + const remainingAfterCurrent = this.exercises.slice(this.exercises.indexOf(current) + 1) + const completed = this.appStorage.get('completed') || [] -WA.prototype.getNext = function () { - var current = this.appStorage.get('current') - var remainingAfterCurrent = this.exercises.slice(this.exercises.indexOf(current) + 1) - var completed = this.appStorage.get('completed') || [] + const incompleteAfterCurrent = remainingAfterCurrent.filter(function (elem) { + return completed.indexOf(elem) < 0 + }) - var incompleteAfterCurrent = remainingAfterCurrent.filter(function (elem) { - return completed.indexOf(elem) < 0 - }) + if (incompleteAfterCurrent.length === 0) { + return new Error('error.no_uncomplete_left') + } - if (incompleteAfterCurrent.length === 0) { - return new Error('error.no_uncomplete_left') + return incompleteAfterCurrent[0] } - return incompleteAfterCurrent[0] -} - -WA.prototype.onComplete = function (cb) { - setImmediate(cb) -} - -// overall exercise fail -WA.prototype.exerciseFail = function (mode, exercise, stream, cb) { - ( - stream.append(exercise.fail, exercise.failType || this.options.defaultOutputType) || - stream.append(this.options.fail, this.options.failType || this.options.defaultOutputType) - ) && - stream.append('\n') + // abstract, to be overwritten by extending classes + onComplete (cb) { + setImmediate(cb) + } - cb() -} + exerciseFail (exercise, stream, cb) { + ( + stream.append(exercise.fail, exercise.failType || this.options.defaultOutputType) || + stream.append(this.options.fail, this.options.failType || this.options.defaultOutputType) + ) && + stream.append('\n') -WA.prototype.getExerciseFiles = function (exercise, callback) { - if (!exercise.hideSolutions && typeof exercise.getSolutionFiles === 'function') { - return exercise.getSolutionFiles(callback) + cb() } - setImmediate(callback.bind(null, null, exercise.solutionFiles || [])) -} - -// overall exercise pass -WA.prototype.exercisePass = function (mode, exercise, stream, cb) { - this.getExerciseFiles(exercise, function (err, files) { - if (err) { - return cb(this.__('solution.notes.load_error', { err: err.message || err }), false, stream) + getExerciseFiles (exercise, callback) { + if (!exercise.hideSolutions && typeof exercise.getSolutionFiles === 'function') { + return exercise.getSolutionFiles(callback) } - this.markCompleted(exercise.meta.name, function (err, completeMessage) { + setImmediate(() => callback(null, exercise.solutionFiles || [])) + } + + // overall exercise pass + exercisePass (exercise, stream, cb) { + this.getExerciseFiles(exercise, (err, files) => { if (err) { - return cb(err, false) + return cb(this.__('solution.notes.load_error', { err: err.message || err }), false, stream) } - var appended = stream.append(completeMessage, this.options.defaultOutputType) || - stream.append(exercise.pass, exercise.passType || this.options.defaultOutputType) || - stream.append(this.options.pass, this.options.passType || this.options.defaultPassType) - - var hideSolutions = exercise.hideSolutions - if (hideSolutions === undefined) { - hideSolutions = this.options.hideSolutions - } - if (hideSolutions !== true) { - if ((files && files.length > 0) || exercise.solution) { - stream.append('{solution.notes.compare}') - if (exercise.solutionPath) { - stream.append({ files: [exercise.solutionPath] }) - } + this.markCompleted(exercise.meta.name, (err, completeMessage) => { + if (err) { + return cb(err, false) } - files && files.length > 0 - ? stream.append({ files: files }) - : stream.append(exercise.solution, exercise.solutionType || this.options.defaultSolutionType) + let appended = stream.append(completeMessage, this.options.defaultOutputType) || + stream.append(exercise.pass, exercise.passType || this.options.defaultOutputType) || + stream.append(this.options.pass, this.options.passType || this.options.defaultPassType) - appended = true - } - - var hideRemaining = exercise.hideRemaining - if (hideRemaining === undefined) { - hideRemaining = this.options.hideRemaining - } - - if (hideRemaining !== true) { - var remaining = this.countRemaining() - remaining > 0 - ? stream.append( - '{progress.remaining#' + remaining + '}\n\n' + - '{ui.return}\n') - : stream.append('{progress.finished}\n') - appended = true - } - - if (appended) { - stream.append('\n') - } + let hideSolutions = exercise.hideSolutions + if (hideSolutions === undefined) { + hideSolutions = this.options.hideSolutions + } + if (hideSolutions !== true) { + if ((files && files.length > 0) || exercise.solution) { + stream.append('{solution.notes.compare}') + if (exercise.solutionPath) { + stream.append({ files: [exercise.solutionPath] }) + } + } - cb(null, true) - }.bind(this)) - }.bind(this)) -} + files && files.length > 0 + ? stream.append({ files: files }) + : stream.append(exercise.solution, exercise.solutionType || this.options.defaultSolutionType) -WA.prototype.verify = function (args, specifier, contentOnly, cb) { - return this.process('verify', args, specifier, contentOnly, cb) -} + appended = true + } -WA.prototype.run = function (args, specifier, cb) { - return this.process('run', args, specifier, cb) -} + let hideRemaining = exercise.hideRemaining + if (hideRemaining === undefined) { + hideRemaining = this.options.hideRemaining + } -WA.prototype.process = function (mode, args, specifier, contentOnly, cb) { - var exercise = this.loadExercise(specifier) - var stream = this.createMarkdownStream(exercise) + if (hideRemaining !== true) { + const remaining = this.countRemaining() + remaining > 0 + ? stream.append( + `{progress.remaining#${remaining}}\n\n` + + '{ui.return}\n') + : stream.append('{progress.finished}\n') + appended = true + } - if (!cb && typeof contentOnly === 'function') { - cb = contentOnly - contentOnly = false - } + if (appended) { + stream.append('\n') + } - var _cb = cb - cb = function (err, pass) { - // The method that creates the stream is supposed to know - // when it will surely never add anything more to the - // the stream - stream.stopWaiting() - // Zalgo protection - setImmediate(function () { - _cb(err, pass) + cb(null, true) + }) }) } - // The stream we return needs to be "not finished yet". Which - // is why we mark it as "waiting". It will already push out the - // stream of data but before `.stopWaiting` it will not send - // the end event. - stream.startWaiting() - - if (!exercise) { - cb(this.__('error.exercise.missing', { name: specifier }), false) - return stream - } - var requireSubmission = exercise.requireSubmission - if (requireSubmission === undefined) { - requireSubmission = this.options.requireSubmission + verify (args, specifier, contentOnly, cb) { + return this.process('verify', args, specifier, contentOnly, cb) } - if (requireSubmission !== false && args.length === 0) { - cb(this.__('ui.usage', { appName: this.options.name, mode: mode }), false, stream) - return stream + run (args, specifier, cb) { + return this.process('run', args, specifier, cb) } - var method = exercise[mode] - if (!method) { - cb(this.__('error.exercise.method_not_required', { method: mode }), false, stream) - return stream - } + process (mode, args, specifier, contentOnly, cb) { + const exercise = this.loadExercise(specifier) + let stream = this.createMarkdownStream(exercise) - if (typeof method !== 'function') { - // eslint-disable-next-line standard/no-callback-literal - cb('The `.' + mode + '` object of the exercise `' + exercise.meta.id + ' is a `' + typeof method + '`. It should be a `function` instead.', false, stream) - return stream - } + if (!cb && typeof contentOnly === 'function') { + cb = contentOnly + contentOnly = false + } + + const cbSafe = (err, pass) => { + // The method that creates the stream is supposed to know + // when it will surely never add anything more to the + // the stream + stream.stopWaiting() + // Zalgo protection + setImmediate(() => cb(err, pass)) + } + // The stream we return needs to be "not finished yet". Which + // is why we mark it as "waiting". It will already push out the + // stream of data but before `.stopWaiting` it will not send + // the end event. + stream.startWaiting() + + if (!exercise) { + cbSafe(this.__('error.exercise.missing', { name: specifier }), false) + return stream + } + + var requireSubmission = exercise.requireSubmission + if (requireSubmission === undefined) { + requireSubmission = this.options.requireSubmission + } + + if (requireSubmission !== false && args.length === 0) { + cbSafe(this.__('ui.usage', { appName: this.options.name, mode: mode }), false, stream) + return stream + } + + var method = exercise[mode] + if (!method) { + cbSafe(this.__('error.exercise.method_not_required', { method: mode }), false, stream) + return stream + } - stream = this.executeExercise(exercise, mode, method, args, stream, contentOnly, cb) - if (typeof exercise.on === 'function') { - exercise.on('pass', function (message) { - stream.append({ - text: require('chalk').green.bold('\u2713 '), - type: (message && message.type) || 'md', - skipNewline: true + if (typeof method !== 'function') { + // eslint-disable-next-line standard/no-callback-literal + cbSafe(`The \`.${mode}\` object of the exercise \`${exercise.meta.id} is a \`${typeof method}\`. It should be a \`function\` instead.`, false, stream) + return stream + } + + stream = this.executeExercise(exercise, mode, method, args, stream, contentOnly, cbSafe) + if (typeof exercise.on === 'function') { + exercise.on('pass', message => { + stream.append({ + text: require('chalk').green.bold('\u2713 '), + type: (message && message.type) || 'md', + skipNewline: true + }) + stream.append(message, this.options.defaultOutputType) }) - stream.append(message, this.options.defaultOutputType) - }.bind(this)) - exercise.on('fail', function (message) { - stream.append({ - text: require('chalk').red.bold('\u2717 '), - type: (message && message.type) || 'md', - skipNewline: true + exercise.on('fail', message => { + stream.append({ + text: require('chalk').red.bold('\u2717 '), + type: (message && message.type) || 'md', + skipNewline: true + }) + stream.append(message, this.options.defaultOutputType) }) - stream.append(message, this.options.defaultOutputType) - }.bind(this)) - exercise.on('pass', this.emit.bind(this, 'pass', exercise, mode)) - exercise.on('fail', this.emit.bind(this, 'fail', exercise, mode)) + exercise.on('pass', (...args) => this.emit('pass', exercise, mode, ...args)) + exercise.on('fail', (...args) => this.emit('fail', exercise, mode, ...args)) + } + return stream } - return stream -} -WA.prototype.executeExercise = function (exercise, mode, method, args, stream, contentOnly, cb) { - if (!cb && typeof contentOnly === 'function') { - cb = contentOnly - contentOnly = false - } - if (!contentOnly && mode === 'verify') { - (stream.append(exercise.header, this.options.defaultOutputType) || - stream.append(this.options.header, this.options.defaultOutputType)) - } - var result - var finished = false - var cleanup = function cleanup (err, pass, message, messageType) { - if (finished) { - return // TODO: make this easier to debug ... bad case of zalgo + executeExercise (exercise, mode, method, args, stream, contentOnly, cb) { + if (!cb && typeof contentOnly === 'function') { + cb = contentOnly + contentOnly = false + } + if (!contentOnly && mode === 'verify') { + (stream.append(exercise.header, this.options.defaultOutputType) || + stream.append(this.options.header, this.options.defaultOutputType)) } + let result + let finished = false + const cleanup = (err, pass, message, messageType) => { + if (finished) { + return // TODO: make this easier to debug ... bad case of zalgo + } - finished = true + finished = true - if (message) { - if (typeof message === 'string') { - message = { - text: message, - type: messageType || this.options.defaultOutputType + if (message) { + if (typeof message === 'string') { + message = { + text: message, + type: messageType || this.options.defaultOutputType + } + } + if (pass) { + exercise.pass = [ + exercise.pass || this.options.pass, + message + ] + } else { + exercise.fail = [ + exercise.fail || this.options.fail, + message + ] } } - if (pass) { - exercise.pass = [ - exercise.pass || this.options.pass, - message - ] - } else { - exercise.fail = [ - exercise.fail || this.options.fail, - message - ] + + if (err) { + return cb(this.__('error.exercise.unexpected_error', { mode: mode, err: (err.stack || err) }), false) } - } - if (err) { - return cb(this.__('error.exercise.unexpected_error', { mode: mode, err: (err.stack || err) }), false) - } + const writeFooter = () => { + if (!contentOnly && mode === 'verify') { + // TODO: Make this footer great again once we fixed workshopper-exercise + if (stream.append(exercise.footer, this.options.defaultOutputType) || + stream.append(this.options.footer, this.options.defaultOutputType)) { + stream.append('\n') + } + return true + } + return false + } - var writeFooter = function () { - if (!contentOnly && mode === 'verify') { - // TODO: Make this footer great again once we fixed workshopper-exercise - if (stream.append(exercise.footer, this.options.defaultOutputType) || - stream.append(this.options.footer, this.options.defaultOutputType)) { - stream.append('\n') + const end = err => { + if (typeof exercise.end !== 'function') { + writeFooter() + return cb(null, pass) } - return true + + exercise.end(mode, pass, cleanupErr => { + if (cleanupErr) { + return cb(this.__('error.cleanup', { err: cleanupErr.message || cleanupErr }), false) + } + + writeFooter() + cb(err, pass) + }) } - return false - }.bind(this) - var end = function (err) { - if (typeof exercise.end !== 'function') { - writeFooter() - return cb(null, pass) + if (mode === 'run') { + return setImmediate(end) } - exercise.end(mode, pass, function (cleanupErr) { - if (cleanupErr) { - return cb(this.__('error.cleanup', { err: cleanupErr.message || cleanupErr }), false) - } + if (pass) { + this.exercisePass(exercise, stream, end) + } else { + this.exerciseFail(exercise, stream, end) + } + } - writeFooter() - cb(err, pass) - }.bind(this)) - }.bind(this) + try { + result = method.length <= 1 + ? cleanup(null, true, method.call(exercise, args)) + : method.call(exercise, args, (err, pass, message) => { + /* + err ... Error that occured + pass ... true = The run has worked + message ... message to Append after the output + + callback(true) -> err=null, pass=true + callback(false) -> err=null, pass=false + callback() -> err=null, pass=null + callback(null) -> err=null, pass=null + callback(true, true) -> err=true, pass="x" + callback(false, "x") -> err=false, pass="x" + callback(null, "x") -> err=null, pass="x" + callback("x", false) -> err="x", pass=false + callback("x", true) -> err="x", pass=true ... pass should be ignored + */ + if (pass === undefined && (err === true || err === false || err === undefined || err === null)) { + pass = err + err = null + } - if (mode === 'run') { - return setImmediate(end) + pass = (mode === 'run' || (pass && !exercise.fail)) + err + ? cleanup(err, null, message) + : cleanup(null, pass, message) + }) + } catch (e) { + cleanup(e, false) + return stream } + return this.processResult(result, stream) + } - if (pass) { - this.exercisePass(mode, exercise, stream, end) - } else { - this.exerciseFail(mode, exercise, stream, end) - } - }.bind(this) - - try { - result = method.length <= 1 - ? cleanup(null, true, method.call(exercise, args)) - : method.call(exercise, args, function callback (err, pass, message) { - /* - err ... Error that occured - pass ... true = The run has worked - message ... message to Append after the output - - callback(true) -> err=null, pass=true - callback(false) -> err=null, pass=false - callback() -> err=null, pass=null - callback(null) -> err=null, pass=null - callback(true, true) -> err=true, pass="x" - callback(false, "x") -> err=false, pass="x" - callback(null, "x") -> err=null, pass="x" - callback("x", false) -> err="x", pass=false - callback("x", true) -> err="x", pass=true ... pass should be ignored - */ - if (pass === undefined && (err === true || err === false || err === undefined || err === null)) { - pass = err - err = null - } - - pass = (mode === 'run' || (pass && !exercise.fail)) - err - ? cleanup(err, null, message) - : cleanup(null, pass, message) - }) - } catch (e) { - cleanup(e, false) + processResult (result, stream) { + stream.append(result, this.options.defaultOutputType) return stream } - return this.processResult(result, stream) -} -WA.prototype.processResult = function (result, stream) { - stream.append(result, this.options.defaultOutputType) - return stream -} -WA.prototype.loadExercise = function (specifier) { - var id - if (specifier) { - id = this.specifierToId(specifier) - } else { - id = util.idFromName(this.appStorage.get('current')) - } - if (!id) { - return null - } - var meta = this._meta[id] - if (!meta) { - return null - } + loadExercise (specifier) { + var id + if (specifier) { + id = this.specifierToId(specifier) + } else { + id = util.idFromName(this.appStorage.get('current')) + } - var exercise = meta.fn() - exercise.meta = meta + if (!id) { + return null + } + var meta = this._meta[id] + if (!meta) { + return null + } - if (typeof exercise.init === 'function') { - exercise.init(this, meta.id, meta.name, meta.dir, meta.number) - } + var exercise = meta.fn() + exercise.meta = meta - return exercise -} -WA.prototype.specifierToId = function (specifier) { - if (!isNaN(specifier)) { - var number = parseInt(specifier, 10) - if (number >= 0 && number < this.exercises.length) { - specifier = this.exercises[number] - } else { - specifier = '' + if (typeof exercise.init === 'function') { + exercise.init(this, meta.id, meta.name, meta.dir, meta.number) } - } - return util.idFromName(specifier) -} -WA.prototype.selectExercise = function (specifier) { - var id = this.specifierToId(specifier) - if (!id) { - throw new Error(this.__('error.exercise.missing', { name: specifier })) + return exercise } - var meta = this._meta[id] - if (!meta) { - throw new Error(this.__('error.exercise.missing', { name: specifier })) + specifierToId (specifier) { + if (!isNaN(specifier)) { + const number = parseInt(specifier, 10) + if (number >= 0 && number < this.exercises.length) { + specifier = this.exercises[number] + } else { + specifier = '' + } + } + + return util.idFromName(specifier) } - this.appStorage.save('current', meta.name) - return meta.id -} -WA.prototype.createMarkdownStream = function (exercise) { - var context = exercise ? this.createExerciseContext(exercise) : this.i18n - return new PrintStream(context, this.i18n.lang()) -} -WA.prototype.createExerciseContext = function (exercise) { - return this.i18n.extend({ - 'currentExercise.name': this.__('exercise.' + exercise.meta.name), - 'progress.count': exercise.meta.number, - 'progress.total': this.exercises.length, - 'progress.state_resolved': this.__('progress.state', { count: exercise.meta.number, amount: this.exercises.length }) - }) -} -WA.prototype.getExerciseText = function printExercise (specifier, contentOnly, callback) { - var exercise = this.loadExercise(specifier) - var prepare + selectExercise (specifier) { + const id = this.specifierToId(specifier) + if (!id) { + throw new Error(this.__('error.exercise.missing', { name: specifier })) + } - if (arguments.length === 2) { - callback = contentOnly - contentOnly = false + const meta = this._meta[id] + if (!meta) { + throw new Error(this.__('error.exercise.missing', { name: specifier })) + } + + this.appStorage.save('current', meta.name) + return meta.id + } + + createMarkdownStream (exercise) { + const context = exercise ? this.createExerciseContext(exercise) : this.i18n + return new PrintStream(context, this.i18n.lang()) } - if (!exercise) { - callback(this.__('error.exercise.none_active')) + createExerciseContext (exercise) { + return this.i18n.extend({ + 'currentExercise.name': this.__(`exercise.${exercise.meta.name}`), + 'progress.count': exercise.meta.number, + 'progress.total': this.exercises.length, + 'progress.state_resolved': this.__('progress.state', { count: exercise.meta.number, amount: this.exercises.length }) + }) } - prepare = (typeof exercise.prepare === 'function') ? exercise.prepare.bind(exercise) : setImmediate - prepare(function (err) { - if (err) { - return callback(this.__('error.exercise.preparing', { err: err.message || err })) + getExerciseText (specifier, contentOnly, callback) { + const exercise = this.loadExercise(specifier) + + if (arguments.length === 2) { + callback = contentOnly + contentOnly = false } - var getExerciseText = (typeof exercise.getExerciseText === 'function') ? exercise.getExerciseText.bind(exercise) : setImmediate - getExerciseText(function (err, exerciseTextType, exerciseText) { + if (!exercise) { + return callback(this.__('error.exercise.none_active')) + } + + const prepare = (typeof exercise.prepare === 'function') ? exercise.prepare.bind(exercise) : setImmediate + prepare(err => { if (err) { - return callback(this.__('error.exercise.loading', { err: err.message || err })) + return callback(this.__('error.exercise.preparing', { err: err.message || err })) } - var stream = this.createMarkdownStream(exercise) - var found = false - if (!contentOnly) { - stream.append(exercise.header, this.options.defaultOutputType) || - stream.append(this.options.header, this.options.defaultOutputType) - } - if (stream.append(exercise.problem, exercise.problemType || this.options.defaultOutputType)) { - found = true - } - if (stream.append(exerciseText, exerciseTextType || this.options.defaultOutputType)) { - found = true - } - if (!found) { - // eslint-disable-next-line standard/no-callback-literal - return callback('The exercise "' + exercise.meta.name + '" is missing a problem definition!') - } - if (!contentOnly) { - stream.append(exercise.footer, this.options.defaultOutputType) || - (stream.append(this.options.footer, this.options.defaultOutputType) && - stream.append('\n')) - } - callback(null, stream) - }.bind(this)) - }.bind(this)) + var getExerciseText = (typeof exercise.getExerciseText === 'function') ? exercise.getExerciseText.bind(exercise) : setImmediate + getExerciseText((err, exerciseTextType, exerciseText) => { + if (err) { + return callback(this.__('error.exercise.loading', { err: err.message || err })) + } + const stream = this.createMarkdownStream(exercise) + let found = false + + if (!contentOnly) { + stream.append(exercise.header, this.options.defaultOutputType) || + stream.append(this.options.header, this.options.defaultOutputType) + } + if (stream.append(exercise.problem, exercise.problemType || this.options.defaultOutputType)) { + found = true + } + if (stream.append(exerciseText, exerciseTextType || this.options.defaultOutputType)) { + found = true + } + if (!found) { + // eslint-disable-next-line standard/no-callback-literal + return callback(`The exercise "${exercise.meta.name}" is missing a problem definition!`) + } + if (!contentOnly) { + stream.append(exercise.footer, this.options.defaultOutputType) || + (stream.append(this.options.footer, this.options.defaultOutputType) && + stream.append('\n')) + } + callback(null, stream) + }) + }) + } } -module.exports = WA +module.exports = WorkshopperAdventure diff --git a/lib/commands/completed.js b/lib/commands/completed.js index 859e9ed..23edf13 100644 --- a/lib/commands/completed.js +++ b/lib/commands/completed.js @@ -1,9 +1,9 @@ exports.menu = false -exports.handler = function current (shop) { - var completed = shop.appStorage.get('completed') +exports.handler = shop => { + const completed = shop.appStorage.get('completed') if (completed) { - completed.forEach(function (completed) { - console.log(shop.__('exercise.' + completed)) - }) + completed.forEach(completed => + console.log(shop.__(`exercise.${completed}`)) + ) } } diff --git a/lib/commands/credits.js b/lib/commands/credits.js index c56c630..766dfbc 100644 --- a/lib/commands/credits.js +++ b/lib/commands/credits.js @@ -1,36 +1,36 @@ -exports.filter = function (shop) { - return shop.options.pkg && Array.isArray(shop.options.pkg.contributors) && shop.options.hideCredits !== true -} -exports.handler = function credits (shop) { - var table = '# {{title}}\n' + - '## {{credits.title}}\n' + - '\n' + - '| {{credits.name}} | {{credits.user}} |\n' + - '|------------------|------------------|\n' +exports.filter = shop => + shop.options.pkg && Array.isArray(shop.options.pkg.contributors) && shop.options.hideCredits !== true - table += shop.options.pkg.contributors.reduce(function createRow (result, line) { - if (typeof line === 'string') { - var data = /^([^(<]+)\s*(<([^>]*)>)?\s*(\((https?:\/\/[^)]+)\))?/.exec(line) - line = { - name: data[1], - email: data[3], - url: data[5] - } +exports.handler = shop => { + const table = `# {{title}} +## {{credits.title}} + +| {{credits.name}} | {{credits.user}} | +|------------------|------------------| +${shop.options.pkg.contributors.reduce((result, line) => { + if (typeof line === 'string') { + var data = /^([^(<]+)\s*(<([^>]*)>)?\s*(\((https?:\/\/[^)]+)\))?/.exec(line) + line = { + name: data[1], + email: data[3], + url: data[5] } - if (line) { - result.push('| ') - result.push(line.name) - result.push(' | ') - if (line.url) { - var github = /^https?:\/\/(www\.)?github\.(com|io)\/([^)/]+)/.exec(line.url) - if (github) { - result.push('@' + github[3]) - } + } + if (line) { + result.push('| ') + result.push(line.name) + result.push(' | ') + if (line.url) { + var github = /^https?:\/\/(www\.)?github\.(com|io)\/([^)/]+)/.exec(line.url) + if (github) { + result.push('@' + github[3]) } - result.push(' |\n') } - return result - }, []).join('') + result.push(' |\n') + } + return result +}, []).join('')} +` shop.createMarkdownStream() .appendChain(table, 'md') diff --git a/lib/commands/current.js b/lib/commands/current.js index f62737b..baa834e 100644 --- a/lib/commands/current.js +++ b/lib/commands/current.js @@ -1,8 +1,8 @@ exports.menu = false -exports.handler = function current (shop) { - var current = shop.appStorage.get('current') +exports.handler = shop => { + const current = shop.appStorage.get('current') if (current) { - console.log(shop.__('exercise.' + current)) + console.log(shop.__(`exercise.${current}`)) } else { console.log(shop.__('error.exercise.none_active')) process.exit(1) diff --git a/lib/commands/help.js b/lib/commands/help.js index fb18a96..0e29bf8 100644 --- a/lib/commands/help.js +++ b/lib/commands/help.js @@ -1,6 +1,6 @@ exports.order = 2 -exports.handler = function printHelp (shop) { - var stream = shop.createMarkdownStream() +exports.handler = shop => { + const stream = shop.createMarkdownStream() stream.append(Object.hasOwnProperty.call(shop.options, 'help') ? shop.options.help : require('../../default/help')) || stream.append('No help available.') diff --git a/lib/commands/language-list.js b/lib/commands/language-list.js index 9804a48..5b9c734 100644 --- a/lib/commands/language-list.js +++ b/lib/commands/language-list.js @@ -1,8 +1,5 @@ exports.menu = false exports.filter = require('../langFilter') exports.aliases = ['lang-list', 'langlist'] -exports.handler = function printLanguageMenu (shop) { - shop.options.languages.forEach(function (language) { - console.log(language) - }) -} +exports.handler = shop => + shop.options.languages.forEach(language => console.log(language)) diff --git a/lib/commands/language.js b/lib/commands/language.js index d6afa7d..0022836 100644 --- a/lib/commands/language.js +++ b/lib/commands/language.js @@ -1,16 +1,16 @@ -function renderNonTty (shop, menuOptions) { - var __ = shop.i18n.__ - var stream = shop.createMarkdownStream() +function renderNonTty (shop) { + const { __ } = shop.i18n + const stream = shop.createMarkdownStream() - stream.append('\n' + - '# {error.notty}\n' + - '---\n' + - '# {title}\n' + - '---\n', 'md') + stream.append(` +# {error.notty} +--- +# {title} +---`, 'md') - shop.options.languages.forEach(function (language, no) { - stream.append(' `{appName} lang ' + language + '`: ' + __('language.' + language) + ((shop.i18n.lang() === language) ? ' ... [' + __('language._current') + ']' : ''), 'md') - }) + shop.options.languages.forEach(language => + stream.append(` \`{appName} lang ${language}\`: ${__(`language.${language}`)}${((shop.i18n.lang() === language) ? ` ... [${__('language._current')}]` : '')}`, 'md') + ) stream .appendChain('---\n', 'md') .pipe(require('../mseePipe')()) @@ -20,17 +20,17 @@ function renderNonTty (shop, menuOptions) { exports.order = 1 exports.filter = require('../langFilter') exports.aliases = ['lang'] -exports.handler = function printLanguageMenu (shop, args) { +exports.handler = (shop, args) => { const chalk = require('chalk') - const __ = shop.i18n.__ + const { __ } = shop.i18n const current = shop.i18n.lang() const menuOptions = { title: __('title'), subtitle: shop.i18n.has('subtitle') && __('subtitle'), menu: shop.options.languages.map(function (language) { return { - label: chalk.bold('» ' + __('language.' + language)), - marker: (current === language) ? ' [' + __('language._current') + ']' : '', + label: chalk.bold(`» ${__(`language.${language}`)}`), + marker: (current === language) ? ` [${__('language._current')}]` : '', handler: function () { shop.i18n.change(language) shop.execute(['menu']) @@ -50,15 +50,16 @@ exports.handler = function printLanguageMenu (shop, args) { try { shop.i18n.change(args[0]) } catch (e) {} - console.log(shop.i18n.__('language._current') + ': ' + __('language.' + shop.i18n.lang())) + console.log(`${shop.i18n.__('language._current')}: ${__(`language.${shop.i18n.lang()}`)}`) process.exit() } - shop.options.menuFactory.options.selected = shop.options.languages.reduce(function (selected, language, count) { - return language === current ? count : selected - }, undefined) - var menu = shop.options.menuFactory.create(menuOptions) + shop.options.menuFactory.options.selected = shop.options.languages.reduce( + (selected, language, count) => language === current ? count : selected + , undefined + ) + const menu = shop.options.menuFactory.create(menuOptions) if (!menu) { - renderNonTty(shop, menuOptions) + renderNonTty(shop) } } diff --git a/lib/commands/list.js b/lib/commands/list.js index 2ec5cb8..8dd604d 100644 --- a/lib/commands/list.js +++ b/lib/commands/list.js @@ -1,6 +1,3 @@ exports.menu = false -exports.handler = function (shop) { - shop.exercises.forEach(function (name) { - console.log(shop.__('exercise.' + name)) - }) -} +exports.handler = shop => + shop.exercises.forEach(name => shop.__(`exercise.${name}`)) diff --git a/lib/commands/menu.js b/lib/commands/menu.js index 3b99370..3ba9ae3 100644 --- a/lib/commands/menu.js +++ b/lib/commands/menu.js @@ -1,7 +1,7 @@ function getExtras (shop) { return shop.cli.commands.concat() .reverse() - .filter(function isCommandInMenu (extra) { + .filter(extra => { if (typeof extra.filter === 'function' && !extra.filter(shop)) { return false } @@ -9,26 +9,26 @@ function getExtras (shop) { }) } -function renderNonTty (shop, menuOptions) { - var completed = shop.appStorage.get('completed') || [] - var __ = shop.i18n.__ - var stream = shop.createMarkdownStream() +function renderNonTty (shop) { + const completed = shop.appStorage.get('completed') || [] + const { __ } = shop.i18n + const stream = shop.createMarkdownStream() - stream.append('\n' + - '# {error.notty}\n' + - '---\n' + - '# {title}\n' + - '---\n', 'md') + stream.append(` +# {error.notty} +--- +# {title} +---`, 'md') - shop.exercises.forEach(function (exercise, no) { - stream.append(' `{appName} select ' + no + '`: ' + shop.i18n.__('exercise.' + exercise) + (completed.indexOf(exercise) >= 0 ? ' [' + shop.i18n.__('menu.completed') + ']' : ''), 'md') - }) + shop.exercises.forEach((exercise, no) => + stream.append(` \`{appName} select ${no}\`: ${shop.i18n.__(`exercise.${exercise}`)}${completed.indexOf(exercise) >= 0 ? ` [${shop.i18n.__('menu.completed')}]` : ''}`, 'md') + ) stream.append('---\n', 'md') getExtras(shop) - .forEach(function (command) { - stream.append(' `{appName} ' + command.aliases[0] + '`: ' + __('menu.' + command.aliases[0]) + '\n', 'md') - }) + .forEach(command => + stream.append(` \`{appName ${command.aliases[0]}\`: ${__(`menu.${command.aliases[0]}`)}\n`, 'md') + ) stream .appendChain('\n---\n', 'md') .pipe(require('../mseePipe')()) @@ -38,39 +38,37 @@ function renderNonTty (shop, menuOptions) { exports.handler = function (shop) { const chalk = require('chalk') - var __ = shop.i18n.__ - var __n = shop.i18n.__n - var completed = shop.appStorage.get('completed') || [] - var current = shop.appStorage.get('current') - var menuOptions = { + const { __, __n } = shop.i18n + const completed = shop.appStorage.get('completed') || [] + let current = shop.appStorage.get('current') + if (completed.indexOf(current) >= 0) { + current = shop.getNext() + } + const menuOptions = { title: __('title'), subtitle: shop.i18n.has('subtitle') && __('subtitle'), menu: shop.exercises.map(function (exercise) { return { - label: chalk.bold('» ' + __('exercise.' + exercise)), + label: chalk.bold(`» ${__(`exercise.${exercise}`)}`), marker: (completed.indexOf(exercise) >= 0) ? '[' + __('menu.completed') + ']' : '', handler: shop.execute.bind(shop, ['select', exercise]) } }), extras: getExtras(shop) - .map(function (command) { - return { - label: chalk.bold(__n('menu.' + command.aliases[0], command.info)), - handler: command.handler.bind(command, shop, []) - } - }) + .map(command => ({ + label: chalk.bold(__n(`menu.${command.aliases[0]}`, command.info)), + handler: command.handler.bind(command, shop, []) + })) .concat({ label: chalk.bold(__('menu.exit')), handler: process.exit.bind(process, 0) }) } - if (completed.indexOf(current) >= 0) { - current = shop.getNext() - } - shop.options.menuFactory.options.selected = shop.exercises.reduce(function (selected, exercise, count) { - return exercise === current ? count : selected - }, undefined) + shop.options.menuFactory.options.selected = shop.exercises.reduce( + (selected, exercise, count) => exercise === current ? count : selected, + undefined + ) shop.options.menuFactory.create(menuOptions) || renderNonTty(shop, menuOptions) } exports.menu = false diff --git a/lib/commands/next.js b/lib/commands/next.js index 7767f5a..d4c5710 100644 --- a/lib/commands/next.js +++ b/lib/commands/next.js @@ -1,6 +1,6 @@ exports.menu = false -exports.handler = function next (shop) { - var next = shop.getNext() +exports.handler = shop => { + const next = shop.getNext() if (next instanceof Error) { return console.log(shop.__(next.message) + '\n') } diff --git a/lib/commands/print.js b/lib/commands/print.js index 6f80cd3..4a28771 100644 --- a/lib/commands/print.js +++ b/lib/commands/print.js @@ -1,7 +1,7 @@ exports.menu = false -exports.handler = function print (shop, args) { - var specifier = args.join(' ') - shop.getExerciseText(specifier, function (err, stream) { +exports.handler = (shop, args) => { + const specifier = args.join(' ') + shop.getExerciseText(specifier, (err, stream) => { if (err) { console.log(err) process.exit(1) @@ -9,8 +9,6 @@ exports.handler = function print (shop, args) { stream .pipe(require('../mseePipe')()) .pipe(process.stdout) - .on('end', function () { - process.exit() - }) + .on('end', () => process.exit()) }) } diff --git a/lib/commands/repo.js b/lib/commands/repo.js index be1ff97..bbeece4 100644 --- a/lib/commands/repo.js +++ b/lib/commands/repo.js @@ -1,5 +1,5 @@ exports.menu = false -exports.handler = function printVersion (shop) { +exports.handler = shop => { console.log(shop.options.appRepo) process.exit() } diff --git a/lib/commands/reset.js b/lib/commands/reset.js index 5352f3a..3356f63 100644 --- a/lib/commands/reset.js +++ b/lib/commands/reset.js @@ -1,5 +1,5 @@ exports.menu = false -exports.handler = function (shop) { +exports.handler = shop => { shop.appStorage.reset() return console.log(shop.__('progress.reset', { title: shop.__('title') })) } diff --git a/lib/commands/run.js b/lib/commands/run.js index 22e7051..eac7a8e 100644 --- a/lib/commands/run.js +++ b/lib/commands/run.js @@ -1,20 +1,21 @@ -var after = require('after') +const after = require('after') exports.menu = false -exports.handler = function (shop, args) { - var error - var passed - var exit = after(2, function () { +exports.handler = (shop, args) => { + let error + let passed + const exit = after(2, () => { if (error) { console.log(error.stack || error) } process.exit(passed && !error ? 0 : 1) }) - var stream = shop.run(args, null, function (err, pass) { - error = err - passed = pass - exit() - }) - stream = stream.pipe(require('../mseePipe')()) + const stream = shop + .run(args, null, (err, pass) => { + error = err + passed = pass + exit() + }) + .pipe(require('../mseePipe')()) stream.on('end', exit) stream.pipe(process.stdout, { end: false }) diff --git a/lib/commands/select.js b/lib/commands/select.js index 34ba654..f511015 100644 --- a/lib/commands/select.js +++ b/lib/commands/select.js @@ -1,6 +1,6 @@ exports.menu = false -exports.handler = function (shop, args) { - var specifier = args.join(' ') +exports.handler = (shop, args) => { + const specifier = args.join(' ') try { shop.selectExercise(specifier) } catch (e) { diff --git a/lib/commands/update.js b/lib/commands/update.js index 719debd..f70b954 100644 --- a/lib/commands/update.js +++ b/lib/commands/update.js @@ -1,43 +1,43 @@ -var spawn = require('child_process').spawn +const { spawn } = require('child_process') function getVersions (shop, callback) { - var path = require('path') - var pkgFile = path.resolve(shop.options.appDir, 'package.json') + const path = require('path') + const pkgFile = path.resolve(shop.options.appDir, 'package.json') try { - var pkg = require(pkgFile) + const pkg = require(pkgFile) + require('latest-version')(pkg.name) + .then(latestVersion => { + exports.info = { + version: latestVersion + } + callback(null, pkg.name, pkg.version, latestVersion) + }) + .catch(err => { + console.log(err) + callback(err) + }) } catch (e) { return callback(e) } - require('latest-version')(pkg.name).then(function (latestVersion) { - exports.info = { - version: latestVersion - } - callback(null, pkg.name, pkg.version, latestVersion) - }).catch(function (err) { - console.log(err) - callback(err) - }) -} -exports.filter = function (shop) { - return shop.options.appDir } +exports.filter = shop => shop.options.appDir exports.menu = true -exports.handler = function (shop) { - getVersions(shop, function (error, name, current, latest) { - var stream = shop.createMarkdownStream() - var __ = shop.i18n.__ +exports.handler = shop => { + getVersions(shop, (error, name, current, latest) => { + const stream = shop.createMarkdownStream() + const { __ } = shop.i18n stream.append('# {{title}}\n') if (error) { - stream.append('Error while trying to evaluate package: ' + error) + stream.append(`Error while trying to evaluate package: ${error}`) } else if (current === latest) { stream.append(__('update.latest_version', { version: current, name: name })) } else { - var cmd = 'npm install ' + name + '@latest -g' + var cmd = `npm install ${name}@latest -g` stream.append(__('update.now', { current: current, latest: latest, cmd: cmd, name: name })) } stream .pipe(require('../mseePipe')()) - .on('end', function () { + .on('end', () => { if (current !== latest) { spawn('npm', ['install', name, '-g'], { stdio: [process.stdin, process.stdout, process.stderr] diff --git a/lib/commands/verify.js b/lib/commands/verify.js index ca5b4db..821fcb3 100644 --- a/lib/commands/verify.js +++ b/lib/commands/verify.js @@ -1,27 +1,22 @@ -var after = require('after') +const after = require('after') exports.menu = false -exports.handler = exports.handler = function (shop, args) { - var passed - var error - var exit = after(2, function () { +exports.handler = exports.handler = (shop, args) => { + let passed + let error + const exit = after(2, () => { if (error) { console.log(error.stack || error) } process.exit(passed && !error ? 0 : 1) }) - var stream = shop.verify(args, null, function (err, pass) { + const stream = shop.verify(args, null, (err, pass) => { error = err passed = pass exit() }) - var msee = require('../msee') + const msee = require('../msee') stream - .on('data', function (data) { - process.stdout.write(msee('\n' + data)) - }) - .on('end', function () { - exit() - }) - + .on('data', data => process.stdout.write(msee('\n' + data))) + .on('end', () => exit()) stream.resume() } diff --git a/lib/commands/version.js b/lib/commands/version.js index 9c8b81c..a473e34 100644 --- a/lib/commands/version.js +++ b/lib/commands/version.js @@ -1,6 +1,6 @@ exports.aliases = ['v'] exports.menu = false -exports.handler = function printVersion (shop) { +exports.handler = shop => { console.log(shop.getVersionString()) process.exit() } diff --git a/lib/createExerciseMeta.js b/lib/createExerciseMeta.js index 70200c5..23c5540 100644 --- a/lib/createExerciseMeta.js +++ b/lib/createExerciseMeta.js @@ -2,21 +2,16 @@ var fs = require('fs') var util = require('../util') var path = require('path') -function fileError (id, file) { - var error = new Error(id) - error.id = id - error.exerciseFile = file - error.toString = function () { - return '[WorkshopperFileError: ' + id + ' @ ' + error.exerciseFile + ']' - } - return error +function fileError (id, exerciseFile) { + return Object.assign(new Error(id), { + id, + exerciseFile, + toString: () => `[WorkshopperFileError: ${id} @ ${exerciseFile}]` + }) } module.exports = function createExerciseMeta (exerciseDir, nameOrObject, fnOrObject, fn) { - var meta - var stat - - meta = (typeof nameOrObject === 'object') + const meta = (typeof nameOrObject === 'object') ? nameOrObject : (typeof fnOrObject === 'object') ? fnOrObject @@ -51,7 +46,8 @@ module.exports = function createExerciseMeta (exerciseDir, nameOrObject, fnOrObj } if (!meta.fn && meta.exerciseFile) { - meta.fn = function () { + meta.fn = () => { + let stat try { stat = fs.statSync(meta.exerciseFile) } catch (err) { @@ -62,9 +58,9 @@ module.exports = function createExerciseMeta (exerciseDir, nameOrObject, fnOrObj throw fileError('missing_file', meta.exerciseFile) } - var exercise = require(meta.exerciseFile) + const exercise = require(meta.exerciseFile) if (typeof exercise === 'function') { - exercise = exercise() + return exercise() } return exercise } diff --git a/lib/langFilter.js b/lib/langFilter.js index c14d485..30a0478 100644 --- a/lib/langFilter.js +++ b/lib/langFilter.js @@ -1,3 +1 @@ -module.exports = function langFilter (shop) { - return shop.options.languages.length > 1 -} +exports.langFilter = shop => shop.options.languages.length > 1 diff --git a/lib/modifiers/language.js b/lib/modifiers/language.js index f0038eb..fe9fbf0 100644 --- a/lib/modifiers/language.js +++ b/lib/modifiers/language.js @@ -1,6 +1,6 @@ exports.aliases = ['l', 'lang'] exports.filter = require('../langFilter') -exports.handler = function (shop, lang) { +exports.handler = (shop, lang) => { try { shop.i18n.change(lang) } catch (e) { diff --git a/lib/modifiers/version.js b/lib/modifiers/version.js index 0498cad..7c3e3ab 100644 --- a/lib/modifiers/version.js +++ b/lib/modifiers/version.js @@ -1,4 +1,4 @@ exports.aliases = ['v'] -exports.handler = function (shop, lang) { +exports.handler = (shop, lang) => { shop.execute(['version']) } diff --git a/lib/msee.js b/lib/msee.js index 1fcbf99..70ac8fc 100644 --- a/lib/msee.js +++ b/lib/msee.js @@ -16,6 +16,4 @@ const mseeOptions = { maxWidth: 78 } -module.exports = function (content) { - return msee.parse(content, mseeOptions) -} +module.exports = content => msee.parse(content, mseeOptions) diff --git a/lib/mseePipe.js b/lib/mseePipe.js index b96a212..79c0e0c 100644 --- a/lib/mseePipe.js +++ b/lib/mseePipe.js @@ -1,15 +1,15 @@ var msee = require('./msee') const through = require('through2') -module.exports = function () { - var buffer = [] - return through(function (contents, encoding, done) { +module.exports = () => { + const buffer = [] + return through((contents, _, done) => { buffer.push(contents.toString()) done() }, function (done) { - var contents = buffer.join('\n') + const contents = buffer.join('\n') - var str = msee(contents).replace(/^/gm, ' ').replace(/$/gm, ' ') + let str = msee(contents).replace(/^/gm, ' ').replace(/$/gm, ' ') str = str.substr(0, str.length - 3) this.push(str) done() diff --git a/lib/print.js b/lib/print.js index 33e74e9..4ac535a 100644 --- a/lib/print.js +++ b/lib/print.js @@ -3,30 +3,27 @@ const path = require('path') const colorsTmpl = require('colors-tmpl') const through2 = require('through2') const split = require('split') -const inherits = require('util').inherits const CombinedStream = require('combined-stream-wait-for-it') const StrToStream = require('string-to-stream') function getText (i18n, contents) { contents = colorsTmpl(contents) - .replace(/\{+([^{}]+)\}+/gi, function (match, k) { + .replace(/\{+([^{}]+)\}+/gi, (match, k) => { var numPart = /^([^#]*)#([^#]+)$/.exec(k) if (numPart) { k = numPart[1] return i18n.has(k) ? getText(i18n, i18n.__n(k, parseFloat(numPart[2]))) : match - } else { - return i18n.has(k) ? getText(i18n, i18n.__(k)) : match } + return i18n.has(k) ? getText(i18n, i18n.__(k)) : match }) - .replace(/\$([A-Z_]+)/g, function (match, k) { - return i18n.has(k) ? getText(i18n, i18n.__(k)) : ('$' + k) - }) + .replace(/\$([A-Z_]+)/g, (_, k) => i18n.has(k) ? getText(i18n, i18n.__(k)) : ('$' + k)) if (i18n.has('appDir')) { // proper path resolution - contents = contents.replace(/\{rootdir:([^}]+)\}/gi, function (match, subpath) { - return 'file://' + path.join(i18n.__('appDir'), subpath) - }) + contents = contents.replace( + /\{rootdir:([^}]+)\}/gi, + (_, subpath) => `file://${path.join(i18n.__('appDir'), subpath)}` + ) } return contents @@ -67,146 +64,149 @@ function localisedFirstFile (files, lang) { return file } -var PrintStream = function (i18n, lang) { - if (!(this instanceof PrintStream)) { - return new PrintStream(i18n, lang) +class PrintStream extends CombinedStream { + constructor (i18n, lang) { + super({}) + this.i18n = i18n + this.lang = lang + this.typeOpen = 'md' + this._append = CombinedStream.prototype.append + this._end = CombinedStream.prototype.end } - CombinedStream.call(this, {}) - this.i18n = i18n - this.lang = lang - this.typeOpen = 'md' -} + sub () { + var sub = new PrintStream(this.i18n, this.lang) + this._append(sub) + return sub + } -inherits(PrintStream, CombinedStream) + append (content, contentType) { + let stream = null -PrintStream.prototype._append = CombinedStream.prototype.append -PrintStream.prototype._end = CombinedStream.prototype.end -PrintStream.prototype.sub = function () { - var sub = new PrintStream(this.i18n, this.lang) - this._append(sub) - return sub -} -PrintStream.prototype.append = function (content, contentType) { - var stream = null + if (typeof content === 'function') { + content = content(this.i18n, this.lang) + } - if (typeof content === 'function') { - content = content(this.i18n, this.lang) - } + if (content === null || content === undefined) { + return false + } - if (content === null || content === undefined) { - return false - } + if (Array.isArray(content)) { + return content.reduce( + (found, child) => this.append(child) ? true : found, + false + ) + } + + if (Object.hasOwnProperty.call(content, 'first')) { + return content.first.reduce((found, child) => found || this.append(child), false) + } - if (Array.isArray(content)) { - return content.reduce(function (found, child) { - if (this.append(child)) { + if (Object.hasOwnProperty.call(content, 'files')) { + const files = content.files + .map(file => localisedFileName(this.lang, file)) + .filter(file => file !== null) + + if (files.length > 0) { + stream = this.sub() + files.forEach(file => { + stream.append('---', 'md') + if (files.length > 1) { + stream.append(`\`_${file}_\` :`) + } + stream.append({ file: file }) + }) + stream.append('---', 'md') return true } - return found - }.bind(this), false) - } + return false + } - if (Object.hasOwnProperty.call(content, 'first')) { - return content.first.reduce(function (found, child) { - return found || this.append(child) - }.bind(this), false) - } else if (Object.hasOwnProperty.call(content, 'files')) { - var files = content.files - .map(localisedFileName.bind(null, this.lang)) - .filter(function (file) { return file !== null }) - if (files.length > 0) { - stream = this.sub() - files.forEach(function (file) { - stream.append('---', 'md') - if (files.length > 1) { - stream.append('`_' + file + '_` :') + if (Object.hasOwnProperty.call(content, 'file')) { + const file = localisedFirstFile(content.file, this.lang) + if (file) { + // In order to properly support stream we need to rewrite workshopper-exercise + // to return an stream to the output instead of simply piping to stdout + // stream = fs.createReadStream(file, {encoding: 'utf8'}) + let str = fs.readFileSync(file, 'utf8') + contentType = content.type || contentType || path.extname(file).replace(/^\./, '').toLowerCase() + if (contentType !== 'md') { + str = `\`\`\`${contentType}\n${str}\n\`\`\`\n` + contentType = 'md' } - stream.append({ file: file }) - }) - stream.append('---', 'md') - return true + stream = new StrToStream(str) + } + } else if (content.pipe) { + stream = content + } else if (Object.hasOwnProperty.call(content, 'text')) { + contentType = content.type + stream = new StrToStream(`${content.text}${content.skipNewline ? '' : '\n'}`) } else { + stream = new StrToStream(`${content}\n`) + } + + if (!stream) { return false } - } - if (Object.hasOwnProperty.call(content, 'file')) { - var file = localisedFirstFile(content.file, this.lang) - if (file) { - // In order to properly support stream we need to rewrite workshopper-exercise - // to return an stream to the output instead of simply piping to stdout - // stream = fs.createReadStream(file, {encoding: 'utf8'}) - var str = fs.readFileSync(file, 'utf8') - contentType = content.type || contentType || path.extname(file).replace(/^\./, '').toLowerCase() - if (contentType !== 'md') { - str = '```' + contentType + '\n' + str + '\n```\n' - contentType = 'md' - } - stream = new StrToStream(str) + if (!contentType) { + contentType = 'md' } - } else if (content.pipe) { - stream = content - } else if (Object.hasOwnProperty.call(content, 'text')) { - contentType = content.type - stream = new StrToStream(content.text + (content.skipNewline ? '' : '\n')) - } else { - stream = new StrToStream(content + '\n') - } - if (!stream) { - return false + var i18n = this.i18n + var buffer = [] + + this._append( + stream + .pipe(split()) + .pipe( + through2( + { objectMode: true }, + (contents, _, done) => { + buffer.push(getText(i18n, contents.toString())) + done() + }, + function (done) { + this.push({ + text: buffer.join('\n'), + type: contentType + }) + done() + } + ) + ) + ) + return true } - if (!contentType) { - contentType = 'md' + write (data) { + if (typeof data === 'string') { + this.emit('data', data) + return + } + if (this.typeOpen !== data.type) { + if (data.type !== 'md') { + data.text = `\`\`\`${data.type}\n${data.text}` + } + if (this.typeOpen !== 'md') { + data.text = `\n\`\`\`\n${data.text}` + } + this.typeOpen = data.type + } + this.emit('data', data.text) } - var i18n = this.i18n - var buffer = [] - - this._append( - stream - .pipe(split()) - .pipe(through2({ objectMode: true }, function (contents, encoding, done) { - buffer.push(getText(i18n, contents.toString())) - done() - }, function (done) { - this.push({ - text: buffer.join('\n'), - type: contentType - }) - done() - })) - ) - return true -} -PrintStream.prototype.write = function (data) { - if (typeof data === 'string') { - this.emit('data', data) - return - } - if (this.typeOpen !== data.type) { - if (data.type !== 'md') { - data.text = '```' + data.type + '\n' + data.text - } + end () { if (this.typeOpen !== 'md') { - data.text = '\n```\n' + data.text + this.emit('data', '```') } - this.typeOpen = data.type + this._end() } - this.emit('data', data.text) -} -PrintStream.prototype.end = function () { - if (this.typeOpen !== 'md') { - this.emit('data', '```') - } - this._end() -} -PrintStream.prototype.appendChain = function (content, type) { - this.append(content, type) - return this + appendChain (content, type) { + this.append(content, type) + return this + } } module.exports = PrintStream diff --git a/util.js b/util.js index 1205af4..aac8ee2 100644 --- a/util.js +++ b/util.js @@ -1,10 +1,7 @@ const path = require('path') const fs = require('fs') -function idFromName (id, spaceChar = '_') { - if (id === null || id === undefined) { - id = '' - } +function idFromName (id = '', spaceChar = '_') { const regex = new RegExp(`[^\\w${spaceChar}]`, 'gi') return id.toString().toLowerCase() @@ -13,7 +10,8 @@ function idFromName (id, spaceChar = '_') { .replace(regex, '') } -function dirFromName (exerciseDir, name) { +exports.idFromName = idFromName +exports.dirFromName = (exerciseDir, name) => { if (typeof exerciseDir !== 'string') { return null } @@ -39,9 +37,5 @@ function getFsObject (type, file, base) { return file } -module.exports = { - idFromName: idFromName, - dirFromName: dirFromName, - getDir: getFsObject.bind(null, 'dir'), - getFile: getFsObject.bind(null, 'file') -} +exports.getDir = (file, base) => getFsObject('dir', file, base) +exports.getFile = (file, base) => getFsObject('file', file, base) diff --git a/workshopper.js b/workshopper.js index 6c7e839..78c3575 100644 --- a/workshopper.js +++ b/workshopper.js @@ -1,45 +1,40 @@ -var Adventure = require('./adventure') -var util = require('./util') -var inherits = require('util').inherits - -module.exports = LegacyWorkshopper - -function LegacyWorkshopper (options) { - if (!(this instanceof LegacyWorkshopper)) { - return new LegacyWorkshopper(options) - } - - if (!options.header) { - if (options.showHeader === undefined || options.showHeader) { - options.header = require('./default/header') +const Adventure = require('./adventure') +const util = require('./util') + +class LegacyWorkshopper extends Adventure { + constructor (options = {}) { + if (!options.header) { + if (options.showHeader === undefined || options.showHeader) { + options.header = require('./default/header') + } + } + if (options.hideRemaining === undefined) { + options.hideRemaining = false } - } - if (options.hideRemaining === undefined) { - options.hideRemaining = false - } - if (options.requireSubmission === undefined) { - options.requireSubmission = true - } + if (options.requireSubmission === undefined) { + options.requireSubmission = true + } - if (options.pass === undefined) { - options.pass = require('./default/pass') - } + if (options.pass === undefined) { + options.pass = require('./default/pass') + } - if (options.fail === undefined) { - options.fail = require('./default/fail') - } + if (options.fail === undefined) { + options.fail = require('./default/fail') + } - if (options.execute === undefined) { - options.execute = 'immediatly' - } + if (options.execute === undefined) { + options.execute = 'immediatly' + } - Adventure.apply(this, [options]) + super(options) - var menuJson = util.getFile(options.menuJson || 'menu.json', this.options.exerciseDir) - if (menuJson) { - this.addAll(require(menuJson)) + var menuJson = util.getFile(options.menuJson || 'menu.json', this.options.exerciseDir) + if (menuJson) { + this.addAll(require(menuJson)) + } } } -inherits(LegacyWorkshopper, Adventure) +module.exports = LegacyWorkshopper