1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2024-11-05 09:56:22 +00:00

Multiple enhancements.

A set of rough enhancements supporting FRESH:

- Added ability to process multiple sources for all commands (BUILD,
VALIDATE, CONVERT).

- Added new HELP command to show usage.

- Improved error-handling and color-coding.
This commit is contained in:
devlinjd 2015-11-21 16:12:22 -05:00
parent 992069b22d
commit 5735ddc495
3 changed files with 121 additions and 44 deletions

View File

@ -15,7 +15,7 @@ module.exports = function () {
, FLUENT = require('./fluentlib')
, PATH = require('path')
, MKDIRP = require('mkdirp')
, COLORS = require('colors')
//, COLORS = require('colors')
, rez, _log, _err;
/**
@ -62,8 +62,8 @@ module.exports = function () {
// Load the theme
var theTheme = new FLUENT.Theme().open( tFolder );
_opts.themeObj = theTheme;
_log( 'Applying '.yellow + theTheme.name.toUpperCase().yellow.bold + (' theme (' +
Object.keys(theTheme.formats).length + ' formats)').yellow );
_log( 'Applying '.status + theTheme.name.toUpperCase().infoBold + (' theme (' +
Object.keys(theTheme.formats).length + ' formats)').status );
// Expand output resumes... (can't use map() here)
var targets = [], that = this;
@ -99,8 +99,8 @@ module.exports = function () {
var fObj = _.property( fi.fmt.pre )( theme.formats );
var fOut = path.join( f.substring( 0, f.lastIndexOf('.')+1 ) + fObj.pre);
_log( 'Generating '.green + fi.fmt.title.toUpperCase().green.bold + ' resume: '.green +
path.relative(process.cwd(), f ).green.bold );
_log( 'Generating '.useful + fi.fmt.title.toUpperCase().useful.bold + ' resume: '.useful +
path.relative(process.cwd(), f ).useful.bold );
var theFormat = _fmts.filter(
function( fmt ) { return fmt.name === fi.fmt.pre; })[0];
@ -124,7 +124,7 @@ module.exports = function () {
*/
function validate( src, unused, opts, logger ) {
_log = logger || console.log;
if( !src || !src.length ) { throw { fluenterror: 3 }; }
if( !src || !src.length ) { throw { fluenterror: 6 }; }
var isValid = true;
var validator = require('is-my-json-valid');
@ -152,7 +152,7 @@ module.exports = function () {
var rez = JSON.parse( rep.raw );
}
catch( ex ) {
_log('Validating '.gray + rep.file.cyan.bold + ' against FRESH/JRS schema: '.gray + 'ERROR!'.red.bold);
_log('Validating '.info + rep.file.infoBold + ' against FRESH/JRS schema: '.info + 'ERROR!'.error.bold);
if (ex instanceof SyntaxError) {
// Invalid JSON
@ -166,27 +166,39 @@ module.exports = function () {
return;
}
var fmt = rez.meta && rez.meta.format === 'FRESH@0.1.0' ? 'fresh':'jars';
process.stdout.write( 'Validating '.gray + rep.file + ' against '.gray +
fmt.replace('jars','JSON Resume').toUpperCase() + ' schema: '.gray );
var isValid = false;
var style = 'useful';
var errors = [];
var validate = validator( schemas[ fmt ], { // Note [1]
formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ }
try {
var fmt = rez.meta && rez.meta.format === 'FRESH@0.1.0' ? 'fresh':'jars';
var validate = validator( schemas[ fmt ], { // Note [1]
formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ }
});
isValid = validate( rez );
if( !isValid ) {
style = 'warn';
errors = validate.errors;
}
}
catch(ex) {
}
_log( 'Validating '.info + rep.file.infoBold + ' against '.info +
fmt.replace('jars','JSON Resume').toUpperCase().infoBold + ' schema: '.info + (isValid ? 'VALID!' : 'INVALID')[style].bold );
errors.forEach(function(err,idx){
_log( '--> '.bold.yellow + ( err.field.replace('data.','resume.').toUpperCase()
+ ' ' + err.message).yellow );
});
var ret = validate( rez );
if( !ret ) {
rez.imp = rez.imp || { };
rez.imp.validationErrors = validate.errors;
_log('INVALID'.bold.yellow);
rez.imp.validationErrors.forEach(function(err,idx){
_log( '--> '.bold.yellow + ( err.field.replace('data.','resume.').toUpperCase()
+ ' ' + err.message).yellow );
});
}
else {
_log('VALID!'.bold.green);
}
});
}
@ -196,19 +208,35 @@ module.exports = function () {
*/
function convert( src, dst, opts, logger ) {
_log = logger || console.log;
if( !src || !src.length ) { throw { fluenterror: 3 }; }
if( !dst || !dst.length ) { throw { fluenterror: 5 }; }
var sheet = loadSourceResumes( src )[ 0 ];
var sourceFormat = sheet.imp.orgFormat === 'JRS' ? 'JRS' : 'FRESH';
var targetFormat = sourceFormat === 'JRS' ? 'FRESH' : 'JRS';
_log( 'Converting '.gray + src[0] + (' (' + sourceFormat + ') to ').gray + dst[0] +
(' (' + targetFormat + ').').gray );
sheet.saveAs( dst[0], targetFormat );
if( !src || !src.length ) { throw { fluenterror: 6 }; }
if( !dst || !dst.length ) {
if( src.length === 1 ) { throw { fluenterror: 5 }; }
else if( src.length === 2 ) { dst = [ src[1] ]; src = [ src[0] ]; }
else { throw { fluenterror: 5 }; }
}
if( src && dst && src.length && dst.length && src.length !== dst.length ) {
throw { fluenterror: 7 };
}
var sheets = loadSourceResumes( src );
sheets.forEach(function(sheet, idx){
var sourceFormat = sheet.imp.orgFormat === 'JRS' ? 'JRS' : 'FRESH';
var targetFormat = sourceFormat === 'JRS' ? 'FRESH' : 'JRS';
_log( 'Converting '.useful + sheet.imp.fileName.useful.bold + (' (' + sourceFormat + ') to ').useful + dst[0].useful.bold +
(' (' + targetFormat + ').').useful );
sheet.saveAs( dst[idx], targetFormat );
});
}
/**
Display help documentation.
*/
function help() {
console.log( FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' ).useful.bold );
}
function loadSourceResumes( src, fn ) {
return src.map( function( res ) {
_log( 'Reading '.gray + 'SOURCE' + ' resume: '.gray + res.cyan.bold );
_log( 'Reading '.info + 'SOURCE'.infoBold + ' resume: '.status + res.cyan.bold );
return (fn && fn(res)) || (new FLUENT.FRESHResume()).open( res );
});
}
@ -246,7 +274,8 @@ module.exports = function () {
verbs: {
build: generate,
validate: validate,
convert: convert
convert: convert,
help: help
},
lib: require('./fluentlib'),
options: _opts,

View File

@ -8,8 +8,11 @@ Command-line interface (CLI) for FluentCV:CLI.
var ARGS = require( 'minimist' )
, FCMD = require( './fluentcmd')
, PKG = require('../package.json')
, COLORS = require('colors')
, FS = require('fs')
, PATH = require('path')
, opts = { }
, title = ('*** FluentCV v' + PKG.version + ' ***').white.bold
, title = ('*** FluentCV v' + PKG.version + ' ***').bold.white
, _ = require('underscore');
@ -25,16 +28,30 @@ catch( ex ) {
function main() {
// Colorize
COLORS.setTheme({
title: ['white','bold'],
info: process.platform === 'win32' ? 'gray' : ['white','dim'],
infoBold: ['white','dim'],
warn: 'yellow',
error: 'red',
guide: 'yellow',
status: 'gray',//['white','dim'],
useful: 'green',
});
// Setup
if( process.argv.length <= 2 ) { throw { fluenterror: 4 }; }
var a = ARGS( process.argv.slice(2) );
opts = getOpts( a );
logMsg( title );
// Get the action to be performed
var params = a._.map( function(p){ return p.toLowerCase().trim(); });
var verb = params[0];
if( !FCMD.verbs[ verb ] ) {
logMsg('Invalid command: "'.yellow + verb.yellow.bold + '"'.yellow);
logMsg('Invalid command: "'.warn + verb.warn.bold + '"'.warn);
return;
}
@ -42,7 +59,8 @@ function main() {
var splitAt = _.indexOf( params, 'to' );
if( splitAt === a._.length - 1 ) {
// 'TO' cannot be the last argument
logMsg('Please '.gray + 'specify an output file' + ' for this operation or '.gray + 'omit the TO keyword' + '.'.gray);
logMsg('Please '.warn + 'specify an output file'.warnBold +
' for this operation or '.warn + 'omit the TO keyword'.warnBold + '.'.warn );
return;
}
@ -75,20 +93,27 @@ function getOpts( args ) {
function handleError( ex ) {
var msg = '', exitCode;
if( ex.fluenterror ){
switch( ex.fluenterror ) { // TODO: Remove magic numbers
case 1: msg = "The specified theme couldn't be found: " + ex.data; break;
case 2: msg = "Couldn't copy CSS file to destination folder"; break;
case 3: msg = 'Please '.gray + 'specify a valid input resume' + ' in '.gray + 'FRESH' + ' or '.gray + 'JSON Resume' + ' format.'.gray; break;
case 4: msg = title + "\nPlease specify a command (".gray +
case 3: msg = 'Please '.guide + 'specify a valid input resume'.guide.bold + ' in FRESH or JSON Resume format.'.guide; break;
case 4: msg = title + "\nPlease ".guide + "specify a command".guide.bold + " (".guide +
Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
return (idx === ar.length - 1 ? 'or '.gray : '')
+ v.toUpperCase();
}).join(', ') + ")";
return (idx === ar.length - 1 ? 'or '.guide : '')
+ v.toUpperCase().guide;
}).join(', '.guide) + ") to get started.\n\n".guide + FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' ).info.bold;
break;
case 5: msg = "Please specify the name of the TARGET file to convert to.".gray;
//case 4: msg = title + '\n' + ; break;
case 5: msg = 'Please '.guide + 'specify the output resume file'.guide.bold + ' that should be created in the new format.'.guide; break;
case 6: msg = 'Please '.guide + 'specify a valid input resume'.guide.bold + ' in either FRESH or JSON Resume format.'.guide; break;
case 7: msg = 'Please '.guide + 'specify an output file name'.guide.bold + ' for every input file you wish to convert.'.guide; break;
};
exitCode = ex.fluenterror;
}
else {
msg = ex.toString();
@ -101,6 +126,7 @@ function handleError( ex ) {
console.log( ('ERROR: ' + trimmed.toString()).red.bold );
else
console.log( trimmed.toString() );
process.exit( exitCode );
}

22
src/use.txt Normal file
View File

@ -0,0 +1,22 @@
Usage:
fluentcv <COMMAND> <SOURCES> [TO <TARGETS>] [-t <THEME>]
<COMMAND> should be BUILD, CONVERT, VALIDATE, or HELP. <SOURCES> should
be the path to one or more FRESH or JSON Resume format resumes. <TARGETS>
should be the name of the destination resume to be created, if any. The
<THEME> parameter should be the name of a predefined theme (for example:
COMPACT, MINIMIST, MODERN, or HELLO-WORLD) or the relative path to a
custom theme.
fluentcv BUILD resume.json TO out/resume.all
fluentcv CONVERT resume.json TO resume-jrs.json
fluentcv VALIDATE resume.json
Both SOURCES and TARGETS can accept multiple files:
fluentCV BUILD r1.json r2.json TO out/resume.all out2/resume.html
fluentCV VALIDATE resume.json resume2.json resume3.json
See https://github.com/fluentdesk/fluentCV/blob/master/README.md
for more information.