diff --git a/src/core/event-codes.js b/src/core/event-codes.js new file mode 100644 index 0000000..f71e84b --- /dev/null +++ b/src/core/event-codes.js @@ -0,0 +1,43 @@ +/** +Event code definitions. +@module event-codes.js +@license MIT. See LICENSE.md for details. +*/ + + + +(function(){ + + var val = 0; + + module.exports = { + + unknown: 0, + unk: 0, + + begin: 1, + end: 2, + + beforeRead: 3, + afterRead: 4, + + beforeCreate: 5, + bc: 5, + + afterCreate: 6, + ac: 6, + + beforeTheme: 7, + afterTheme: 8, + + beforeMerge: 9, + afterMerge: 10, + + beforeGenerate: 11, + afterGenerate: 12 + + }; + + + +}()); diff --git a/src/core/verb.js b/src/core/verb.js index ed4e6a8..3bf4a13 100644 --- a/src/core/verb.js +++ b/src/core/verb.js @@ -23,12 +23,28 @@ Definition of the Verb class. */ var Verb = module.exports = Class.extend({ - init: function() { + init: function( moniker ) { + this.moniker = moniker; this.emitter = new EVENTS.EventEmitter(); }, on: function() { this.emitter.on.apply( this.emitter, arguments ); + }, + + fire: function(evtName, payload) { + payload = payload || { }; + payload.cmd = this.moniker; + this.emitter.emit( 'hmr:' + evtName, payload ); + return true; + }, + + stat: function( subEvent, payload ) { + payload = payload || { }; + payload.cmd = this.moniker; + payload.sub = subEvent; + this.emitter.emit( 'hmr:status', payload ); + return true; } }); diff --git a/src/generators/html-pdf-cli-generator.js b/src/generators/html-pdf-cli-generator.js index 4552c28..8a80e3f 100644 --- a/src/generators/html-pdf-cli-generator.js +++ b/src/generators/html-pdf-cli-generator.js @@ -40,19 +40,17 @@ Definition of the HtmlPdfCLIGenerator class. try { var safe_eng = info.opts.pdf || 'wkhtmltopdf'; - engines[ safe_eng ].call( this, info.mk, info.outputFile ); + if( safe_eng !== 'none' ) + engines[ safe_eng ].call( this, info.mk, info.outputFile ); return null; // halt further processing } catch(ex) { // { [Error: write EPIPE] code: 'EPIPE', errno: 'EPIPE', ... } // { [Error: ENOENT] } throw ( ex.inner && ex.inner.code === 'ENOENT' ) ? - { fluenterror: this.codes.notOnPath, inner: ex.inner, engine: ex.cmd, stack: ex.inner && ex.inner.stack } : - - { fluenterror: this.codes.pdfGeneration, inner: ex.inner, - stack: ex.inner && ex.inner.stack }; + { fluenterror: this.codes.pdfGeneration, inner: ex, stack: ex.stack }; } } diff --git a/src/generators/template-generator.js b/src/generators/template-generator.js index c5d5c4a..e38419f 100644 --- a/src/generators/template-generator.js +++ b/src/generators/template-generator.js @@ -169,6 +169,7 @@ Definition of the TemplateGenerator class. TODO: Refactor { outputFile: fileName, mk: file.data, opts: that.opts } ); } catch(ex) { + console.log(ex); require('../core/error-handler').err(ex, false); } } @@ -179,6 +180,7 @@ Definition of the TemplateGenerator class. TODO: Refactor FS.copySync( file.info.path, thisFilePath ); } catch(ex) { + console.log('B'); ex.showStack = true; require('../core/error-handler').err( ex ); } diff --git a/src/main.js b/src/main.js index 2c3819e..1d3405f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,15 +1,15 @@ +/** +Definition of the `main` function. +@module main.js +@license MIT. See LICENSE.md for details. +*/ + + + (function(){ - /** - Main function for HackMyResume - @license MIT. See LICENSE.md for details. - @module main.js - */ - - - var HMR = require( './hackmyapi') , PKG = require('../package.json') , FS = require('fs') @@ -17,17 +17,21 @@ , chalk = require('chalk') , PATH = require('path') , HACKMYSTATUS = require('./core/status-codes') + , HME = require('./core/event-codes') , safeLoadJSON = require('./utils/safe-json-loader') , _opts = { } , title = chalk.white.bold('\n*** HackMyResume v' + PKG.version + ' ***') , StringUtils = require('./utils/string.js') , _ = require('underscore') + , OUTPUT = require('./out') , Command = require('commander').Command; /** - Kick off the HackMyResume application. + Main function for HackMyResume + @license MIT. See LICENSE.md for details. + @module main.js */ var main = module.exports = function( args ) { @@ -117,8 +121,6 @@ - - /** Massage command-line args and setup Commander.js. */ @@ -163,11 +165,14 @@ Invoke a HackMyResume verb. */ function execVerb( src, dst, opts, log ) { - loadOptions.call( this, opts ); - require('./core/error-handler').init( _opts.debug ); + loadOptions.call( this, opts ); + require( './core/error-handler' ).init( _opts.debug ); + var out = new OUTPUT( _opts ); var v = new HMR.verbs[ this.name() ](); - v.invoke.call( null, src, dst, _opts, log ); + v.on( 'hmr:status', function() { out.do.apply( out, arguments ); }); + v.invoke.call( v, src, dst, _opts, log ); + } @@ -176,9 +181,7 @@ Initialize HackMyResume options. */ function loadOptions( o ) { - o.opts = this.parent.opts; - // Load the specified options file (if any) and apply options if( o.opts && String.is( o.opts )) { var json = safeLoadJSON( PATH.relative( process.cwd(), o.opts ) ); @@ -187,7 +190,6 @@ throw safeLoadJSON.error; } } - // Merge in command-line options o = EXTEND( true, o, this.opts() ); o.silent = this.parent.silent; @@ -237,4 +239,6 @@ _opts.silent || console.log( msg ); } + + }()); diff --git a/src/out.js b/src/out.js new file mode 100644 index 0000000..d23adb1 --- /dev/null +++ b/src/out.js @@ -0,0 +1,133 @@ +/** +Output routines for HackMyResume. +@license MIT. See LICENSE.md for details. +@module out.js +*/ + + + +(function() { + + + + var chalk = require('chalk') + , HME = require('./core/event-codes') + , _ = require('underscore') + , Class = require('./utils/class.js') + , PATH = require('path') + , pad = require('string-padding'); + + + + /** + A stateful output handler. + */ + var OutputHandler = module.exports = Class.extend({ + + + + init: function( opts ) { + this.opts = opts; + }, + + + + log: function( msg ) { + msg = msg || ''; + this.opts.silent || console.log( msg ); + }, + + + + do: function( evt ) { + + switch( evt.sub ) { + + case HME.beforeCreate: + this.log( chalk.green('Creating new ') + + chalk.green.bold(evt.cmd) + + chalk.green(' resume: ') + chalk.green.bold(evt.file)); + break; + + case HME.afterTheme: + this.theme = evt.theme; + break; + + case HME.beforeMerge: + var msg = ''; + evt.f.reverse().forEach( function( a, idx ) { + msg += ((idx === 0) ? chalk.cyan('Merging ') : chalk.cyan(' onto ')) + + chalk.cyan.bold(a.i().file); + }); + this.log( msg ); + break; + + case HME.afterMerge: + var numFormats = Object.keys(this.theme.formats).length; + this.log( chalk.yellow('Applying ') + + chalk.yellow.bold( this.theme.name.toUpperCase() ) + + chalk.yellow(' theme (' + numFormats + ' format' + + ( evt.numFormats === 1 ? ')' : 's)') )); + break; + + case HME.end: + if( evt.cmd === 'build' ) { + var themeName = this.theme.name.toUpperCase(); + if( this.opts.tips && (this.theme.message || this.theme.render) ) { + var WRAP = require('word-wrap'); + if( this.theme.message ) { + this.log( WRAP( chalk.gray('The ' + themeName + ' theme says: "') + + chalk.white(this.theme.message) + chalk.gray('"'), + { width: this.opts.wrap, indent: '' } )); + } + else if ( this.theme.render ) { + this.log( WRAP( chalk.gray('The ' + themeName + + ' theme says: "') + chalk.white('For best results view JSON ' + + 'Resume themes over a local or remote HTTP connection. For ' + + 'example:'), { width: this.opts.wrap, indent: '' } + )); + this.log( ''); + this.log( + ' npm install http-server -g\r' + + ' http-server ' ); + this.log(''); + this.log(chalk.white('For more information, see the README."'), + { width: this.opts.wrap, indent: '' } ); + } + } + } + break; + + case HME.beforeGenerate: + var suffix = ''; + if( evt.fmt === 'pdf' ) { + if( this.opts.pdf ) { + if( this.opts.pdf !== 'none' ) { + suffix = chalk.green(' (with ' + this.opts.pdf + ')'); + } + else { + this.log( chalk.gray('Skipping ') + + chalk.white.bold( pad(evt.fmt.toUpperCase(),4,null,pad.RIGHT)) + + chalk.gray(' resume') + suffix + chalk.green(': ') + + chalk.white( evt.file )); + return; + } + } + } + + this.log( chalk.green('Generating ') + + chalk.green.bold( + pad(evt.fmt.toUpperCase(),4,null,pad.RIGHT)) + + chalk.green(' resume') + suffix + chalk.green(': ') + + chalk.green.bold( PATH.relative(process.cwd(), evt.file )) ); + break; + } + } + + + + }); + + + +}()); diff --git a/src/verbs/build.js b/src/verbs/build.js index 834fb09..77f8910 100644 --- a/src/verbs/build.js +++ b/src/verbs/build.js @@ -16,6 +16,7 @@ Implementation of the 'generate' verb for HackMyResume. , MKDIRP = require('mkdirp') , EXTEND = require('../utils/extend') , HACKMYSTATUS = require('../core/status-codes') + , HME = require('../core/event-codes') , parsePath = require('parse-filepath') , _opts = require('../core/default-options') , FluentTheme = require('../core/fresh-theme') @@ -33,7 +34,7 @@ Implementation of the 'generate' verb for HackMyResume. var BuildVerb = module.exports = Verb.extend({ init: function() { - this._super(); + this._super('build'); }, invoke: function() { @@ -52,12 +53,15 @@ Implementation of the 'generate' verb for HackMyResume. */ function build( src, dst, opts, logger, errHandler ) { + this.stat( HME.begin ); + prep( src, dst, opts, logger, errHandler ); // Load the theme...we do this first because the theme choice (FRESH or // JSON Resume) determines what format we'll convert the resume to. var tFolder = verifyTheme( _opts.theme ); var theme = loadTheme( tFolder ); + this.stat( HME.afterTheme, { theme: theme }); // Check for invalid outputs var inv = verifyOutputs( dst, theme ); @@ -73,53 +77,26 @@ Implementation of the 'generate' verb for HackMyResume. }).map(function(sh){ return sh.rez; }); // Merge input resumes... - var msg = ''; + (sheets.length > 1) && this.stat( HME.beforeMerge, { f: _.clone(sheets) }); rez = _.reduceRight( sheets, function( a, b, idx ) { - msg += ((idx == sheets.length - 2) ? - chalk.cyan('Merging ') + chalk.cyan.bold(a.i().file) : '') + - chalk.cyan(' onto ') + chalk.cyan.bold(b.i().file); return extend( true, b, a ); }); - msg && _log(msg); + (sheets.length > 1) && this.stat( HME.afterMerge, { r: rez } ); // Output theme messages var numFormats = Object.keys(theme.formats).length; var themeName = theme.name.toUpperCase(); - _log( chalk.yellow('Applying ') + chalk.yellow.bold(themeName) + - chalk.yellow(' theme (' + numFormats + ' format' + - ( numFormats === 1 ? ')' : 's)') )); + // Expand output resumes... var targets = expand( dst, theme ); // Run the transformation! - targets.forEach( function(t) { - t.final = single( t, theme, targets ); - }); - - if( _opts.tips && (theme.message || theme.render) ) { - var WRAP = require('word-wrap'); - if( theme.message ) { - _log( WRAP( chalk.gray('The ' + themeName + - ' theme says: "') + chalk.white(theme.message) + chalk.gray('"'), - { width: _opts.wrap, indent: '' } )); - } - else { - _log( WRAP( chalk.gray('The ' + themeName + - ' theme says: "') + chalk.white('For best results view JSON Resume ' + - 'themes over a local or remote HTTP connection. For example:'), - { width: _opts.wrap, indent: '' } - )); - _log(''); - _log( - ' npm install http-server -g\r' + - ' http-server ' ); - _log(''); - _log(chalk.white('For more information, see the README."'), - { width: _opts.wrap, indent: '' } ); - } - } + _.each(targets, function(t) { + t.final = single.call( this, t, theme, targets ); + }, this); + this.stat( HME.end ); // Don't send the client back empty-handed return { sheet: rez, targets: targets, processed: targets }; @@ -172,28 +149,10 @@ Implementation of the 'generate' verb for HackMyResume. , fName = PATH.basename(f, '.' + fType) , theFormat; - var suffix = ''; - if( targInfo.fmt.outFormat === 'pdf' ) { - if( _opts.pdf ) { - if( _opts.pdf !== 'none' ) { - suffix = chalk.green(' (with ' + _opts.pdf + ')'); - } - else { - _log( chalk.gray('Skipping ') + - chalk.white.bold( - pad(targInfo.fmt.outFormat.toUpperCase(),4,null,pad.RIGHT)) + - chalk.gray(' resume') + suffix + chalk.green(': ') + - chalk.white( PATH.relative(process.cwd(), f )) ); - return; - } - } - } - - _log( chalk.green('Generating ') + - chalk.green.bold( - pad(targInfo.fmt.outFormat.toUpperCase(),4,null,pad.RIGHT)) + - chalk.green(' resume') + suffix + chalk.green(': ') + - chalk.green.bold( PATH.relative(process.cwd(), f )) ); + this.stat( HME.beforeGenerate, { + fmt: targInfo.fmt.outFormat, + file: PATH.relative(process.cwd(), f) + }); // If targInfo.fmt.files exists, this format is backed by a document. // Fluent/FRESH themes are handled here. diff --git a/src/verbs/create.js b/src/verbs/create.js index a34accc..59ab3db 100644 --- a/src/verbs/create.js +++ b/src/verbs/create.js @@ -4,17 +4,28 @@ Implementation of the 'create' verb for HackMyResume. @license MIT. See LICENSE.md for details. */ + + (function(){ + + var MKDIRP = require('mkdirp') , PATH = require('path') , chalk = require('chalk') , Verb = require('../core/verb') - , HACKMYSTATUS = require('../core/status-codes'); + , _ = require('underscore') + , HACKMYSTATUS = require('../core/status-codes') + , HME = require('../core/event-codes'); + var CreateVerb = module.exports = Verb.extend({ + init: function() { + this._super('new'); + }, + invoke: function() { create.apply( this, arguments ); } @@ -22,21 +33,27 @@ Implementation of the 'create' verb for HackMyResume. }); + /** Create a new empty resume in either FRESH or JRS format. */ - function create( src, dst, opts, logger ) { - var _log = logger || console.log; - if( !src || !src.length ) throw { fluenterror: HACKMYSTATUS.createNameMissing }; - src.forEach( function( t ) { - var safeFormat = opts.format.toUpperCase(); - _log(chalk.green('Creating new ') + chalk.green.bold(safeFormat) + - chalk.green(' resume: ') + chalk.green.bold(t)); + function create( src, dst, opts/*, logger*/ ) { + + if(!src || !src.length) throw {fluenterror: HACKMYSTATUS.createNameMissing}; + this.stat( HME.begin ); + + _.each( src, function( t ) { + var safeFmt = opts.format.toUpperCase(); + this.fireStat( HME.bc, { fmt: safeFmt, file: t } ); MKDIRP.sync( PATH.dirname( t ) ); // Ensure dest folder exists; - var RezClass = require('../core/' + safeFormat.toLowerCase() + '-resume' ); + var RezClass = require('../core/' + safeFmt.toLowerCase() + '-resume' ); RezClass.default().save(t); - //FLUENT[ safeFormat + 'Resume' ].default().save( t ); - }); + this.fireStat( HME.ac, { fmt: safeFmt, file: t } ); + }, this); + + this.stat( HME.end ); } + + }());