mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2024-11-22 08:20:11 +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:
parent
992069b22d
commit
5735ddc495
@ -15,7 +15,7 @@ module.exports = function () {
|
|||||||
, FLUENT = require('./fluentlib')
|
, FLUENT = require('./fluentlib')
|
||||||
, PATH = require('path')
|
, PATH = require('path')
|
||||||
, MKDIRP = require('mkdirp')
|
, MKDIRP = require('mkdirp')
|
||||||
, COLORS = require('colors')
|
//, COLORS = require('colors')
|
||||||
, rez, _log, _err;
|
, rez, _log, _err;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,8 +62,8 @@ module.exports = function () {
|
|||||||
// Load the theme
|
// Load the theme
|
||||||
var theTheme = new FLUENT.Theme().open( tFolder );
|
var theTheme = new FLUENT.Theme().open( tFolder );
|
||||||
_opts.themeObj = theTheme;
|
_opts.themeObj = theTheme;
|
||||||
_log( 'Applying '.yellow + theTheme.name.toUpperCase().yellow.bold + (' theme (' +
|
_log( 'Applying '.status + theTheme.name.toUpperCase().infoBold + (' theme (' +
|
||||||
Object.keys(theTheme.formats).length + ' formats)').yellow );
|
Object.keys(theTheme.formats).length + ' formats)').status );
|
||||||
|
|
||||||
// Expand output resumes... (can't use map() here)
|
// Expand output resumes... (can't use map() here)
|
||||||
var targets = [], that = this;
|
var targets = [], that = this;
|
||||||
@ -99,8 +99,8 @@ module.exports = function () {
|
|||||||
var fObj = _.property( fi.fmt.pre )( theme.formats );
|
var fObj = _.property( fi.fmt.pre )( theme.formats );
|
||||||
var fOut = path.join( f.substring( 0, f.lastIndexOf('.')+1 ) + fObj.pre);
|
var fOut = path.join( f.substring( 0, f.lastIndexOf('.')+1 ) + fObj.pre);
|
||||||
|
|
||||||
_log( 'Generating '.green + fi.fmt.title.toUpperCase().green.bold + ' resume: '.green +
|
_log( 'Generating '.useful + fi.fmt.title.toUpperCase().useful.bold + ' resume: '.useful +
|
||||||
path.relative(process.cwd(), f ).green.bold );
|
path.relative(process.cwd(), f ).useful.bold );
|
||||||
|
|
||||||
var theFormat = _fmts.filter(
|
var theFormat = _fmts.filter(
|
||||||
function( fmt ) { return fmt.name === fi.fmt.pre; })[0];
|
function( fmt ) { return fmt.name === fi.fmt.pre; })[0];
|
||||||
@ -124,7 +124,7 @@ module.exports = function () {
|
|||||||
*/
|
*/
|
||||||
function validate( src, unused, opts, logger ) {
|
function validate( src, unused, opts, logger ) {
|
||||||
_log = logger || console.log;
|
_log = logger || console.log;
|
||||||
if( !src || !src.length ) { throw { fluenterror: 3 }; }
|
if( !src || !src.length ) { throw { fluenterror: 6 }; }
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
|
|
||||||
var validator = require('is-my-json-valid');
|
var validator = require('is-my-json-valid');
|
||||||
@ -152,7 +152,7 @@ module.exports = function () {
|
|||||||
var rez = JSON.parse( rep.raw );
|
var rez = JSON.parse( rep.raw );
|
||||||
}
|
}
|
||||||
catch( ex ) {
|
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) {
|
if (ex instanceof SyntaxError) {
|
||||||
// Invalid JSON
|
// Invalid JSON
|
||||||
@ -166,27 +166,39 @@ module.exports = function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fmt = rez.meta && rez.meta.format === 'FRESH@0.1.0' ? 'fresh':'jars';
|
var isValid = false;
|
||||||
process.stdout.write( 'Validating '.gray + rep.file + ' against '.gray +
|
var style = 'useful';
|
||||||
fmt.replace('jars','JSON Resume').toUpperCase() + ' schema: '.gray );
|
var errors = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fmt = rez.meta && rez.meta.format === 'FRESH@0.1.0' ? 'fresh':'jars';
|
||||||
var validate = validator( schemas[ fmt ], { // Note [1]
|
var validate = validator( schemas[ fmt ], { // Note [1]
|
||||||
formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ }
|
formats: { date: /^\d{4}(?:-(?:0[0-9]{1}|1[0-2]{1})(?:-[0-9]{2})?)?$/ }
|
||||||
});
|
});
|
||||||
|
|
||||||
var ret = validate( rez );
|
isValid = validate( rez );
|
||||||
if( !ret ) {
|
if( !isValid ) {
|
||||||
rez.imp = rez.imp || { };
|
style = 'warn';
|
||||||
rez.imp.validationErrors = validate.errors;
|
errors = validate.errors;
|
||||||
_log('INVALID'.bold.yellow);
|
}
|
||||||
rez.imp.validationErrors.forEach(function(err,idx){
|
|
||||||
|
}
|
||||||
|
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()
|
_log( '--> '.bold.yellow + ( err.field.replace('data.','resume.').toUpperCase()
|
||||||
+ ' ' + err.message).yellow );
|
+ ' ' + err.message).yellow );
|
||||||
});
|
});
|
||||||
}
|
|
||||||
else {
|
|
||||||
_log('VALID!'.bold.green);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -196,19 +208,35 @@ module.exports = function () {
|
|||||||
*/
|
*/
|
||||||
function convert( src, dst, opts, logger ) {
|
function convert( src, dst, opts, logger ) {
|
||||||
_log = logger || console.log;
|
_log = logger || console.log;
|
||||||
if( !src || !src.length ) { throw { fluenterror: 3 }; }
|
if( !src || !src.length ) { throw { fluenterror: 6 }; }
|
||||||
if( !dst || !dst.length ) { throw { fluenterror: 5 }; }
|
if( !dst || !dst.length ) {
|
||||||
var sheet = loadSourceResumes( src )[ 0 ];
|
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 sourceFormat = sheet.imp.orgFormat === 'JRS' ? 'JRS' : 'FRESH';
|
||||||
var targetFormat = sourceFormat === 'JRS' ? 'FRESH' : 'JRS';
|
var targetFormat = sourceFormat === 'JRS' ? 'FRESH' : 'JRS';
|
||||||
_log( 'Converting '.gray + src[0] + (' (' + sourceFormat + ') to ').gray + dst[0] +
|
_log( 'Converting '.useful + sheet.imp.fileName.useful.bold + (' (' + sourceFormat + ') to ').useful + dst[0].useful.bold +
|
||||||
(' (' + targetFormat + ').').gray );
|
(' (' + targetFormat + ').').useful );
|
||||||
sheet.saveAs( dst[0], targetFormat );
|
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 ) {
|
function loadSourceResumes( src, fn ) {
|
||||||
return src.map( function( res ) {
|
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 );
|
return (fn && fn(res)) || (new FLUENT.FRESHResume()).open( res );
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -246,7 +274,8 @@ module.exports = function () {
|
|||||||
verbs: {
|
verbs: {
|
||||||
build: generate,
|
build: generate,
|
||||||
validate: validate,
|
validate: validate,
|
||||||
convert: convert
|
convert: convert,
|
||||||
|
help: help
|
||||||
},
|
},
|
||||||
lib: require('./fluentlib'),
|
lib: require('./fluentlib'),
|
||||||
options: _opts,
|
options: _opts,
|
||||||
|
44
src/index.js
44
src/index.js
@ -8,8 +8,11 @@ Command-line interface (CLI) for FluentCV:CLI.
|
|||||||
var ARGS = require( 'minimist' )
|
var ARGS = require( 'minimist' )
|
||||||
, FCMD = require( './fluentcmd')
|
, FCMD = require( './fluentcmd')
|
||||||
, PKG = require('../package.json')
|
, PKG = require('../package.json')
|
||||||
|
, COLORS = require('colors')
|
||||||
|
, FS = require('fs')
|
||||||
|
, PATH = require('path')
|
||||||
, opts = { }
|
, opts = { }
|
||||||
, title = ('*** FluentCV v' + PKG.version + ' ***').white.bold
|
, title = ('*** FluentCV v' + PKG.version + ' ***').bold.white
|
||||||
, _ = require('underscore');
|
, _ = require('underscore');
|
||||||
|
|
||||||
|
|
||||||
@ -25,16 +28,30 @@ catch( ex ) {
|
|||||||
|
|
||||||
function main() {
|
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 }; }
|
if( process.argv.length <= 2 ) { throw { fluenterror: 4 }; }
|
||||||
var a = ARGS( process.argv.slice(2) );
|
var a = ARGS( process.argv.slice(2) );
|
||||||
opts = getOpts( a );
|
opts = getOpts( a );
|
||||||
logMsg( title );
|
logMsg( title );
|
||||||
|
|
||||||
|
|
||||||
// Get the action to be performed
|
// Get the action to be performed
|
||||||
var params = a._.map( function(p){ return p.toLowerCase().trim(); });
|
var params = a._.map( function(p){ return p.toLowerCase().trim(); });
|
||||||
var verb = params[0];
|
var verb = params[0];
|
||||||
if( !FCMD.verbs[ verb ] ) {
|
if( !FCMD.verbs[ verb ] ) {
|
||||||
logMsg('Invalid command: "'.yellow + verb.yellow.bold + '"'.yellow);
|
logMsg('Invalid command: "'.warn + verb.warn.bold + '"'.warn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +59,8 @@ function main() {
|
|||||||
var splitAt = _.indexOf( params, 'to' );
|
var splitAt = _.indexOf( params, 'to' );
|
||||||
if( splitAt === a._.length - 1 ) {
|
if( splitAt === a._.length - 1 ) {
|
||||||
// 'TO' cannot be the last argument
|
// '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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,20 +93,27 @@ function getOpts( args ) {
|
|||||||
|
|
||||||
function handleError( ex ) {
|
function handleError( ex ) {
|
||||||
var msg = '', exitCode;
|
var msg = '', exitCode;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if( ex.fluenterror ){
|
if( ex.fluenterror ){
|
||||||
switch( ex.fluenterror ) { // TODO: Remove magic numbers
|
switch( ex.fluenterror ) { // TODO: Remove magic numbers
|
||||||
case 1: msg = "The specified theme couldn't be found: " + ex.data; break;
|
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 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 3: msg = 'Please '.guide + 'specify a valid input resume'.guide.bold + ' in FRESH or JSON Resume format.'.guide; break;
|
||||||
case 4: msg = title + "\nPlease specify a command (".gray +
|
case 4: msg = title + "\nPlease ".guide + "specify a command".guide.bold + " (".guide +
|
||||||
Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
|
Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
|
||||||
return (idx === ar.length - 1 ? 'or '.gray : '')
|
return (idx === ar.length - 1 ? 'or '.guide : '')
|
||||||
+ v.toUpperCase();
|
+ v.toUpperCase().guide;
|
||||||
}).join(', ') + ")";
|
}).join(', '.guide) + ") to get started.\n\n".guide + FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' ).info.bold;
|
||||||
break;
|
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;
|
exitCode = ex.fluenterror;
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
msg = ex.toString();
|
msg = ex.toString();
|
||||||
@ -101,6 +126,7 @@ function handleError( ex ) {
|
|||||||
console.log( ('ERROR: ' + trimmed.toString()).red.bold );
|
console.log( ('ERROR: ' + trimmed.toString()).red.bold );
|
||||||
else
|
else
|
||||||
console.log( trimmed.toString() );
|
console.log( trimmed.toString() );
|
||||||
|
|
||||||
process.exit( exitCode );
|
process.exit( exitCode );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
22
src/use.txt
Normal file
22
src/use.txt
Normal 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.
|
Loading…
Reference in New Issue
Block a user