From 13fc903b2b50791444e668ab5a42b92dbc0bf175 Mon Sep 17 00:00:00 2001 From: hacksalot Date: Fri, 1 Jan 2016 17:20:42 -0500 Subject: [PATCH] Catch JSON syntax errors for all commands. ...and emit line/column info. --- src/core/error-handler.js | 6 ++++++ src/core/resume-factory.js | 36 ++++++++++++++++++++++++------------ src/verbs/convert.js | 2 +- src/verbs/generate.js | 4 +++- src/verbs/validate.js | 21 +++++++++++++++------ 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/core/error-handler.js b/src/core/error-handler.js index b1a6b19..358e67e 100644 --- a/src/core/error-handler.js +++ b/src/core/error-handler.js @@ -32,6 +32,12 @@ Error-handling routines for HackMyResume. err: function( ex, shouldExit ) { var msg = '', exitCode; + if( ex.handled ) { + if( shouldExit ) + process.exit( exitCode ); + return; + } + if( ex.fluenterror ){ switch( ex.fluenterror ) { // TODO: Remove magic numbers diff --git a/src/core/resume-factory.js b/src/core/resume-factory.js index 8bf0f83..dda26cc 100644 --- a/src/core/resume-factory.js +++ b/src/core/resume-factory.js @@ -14,6 +14,7 @@ Definition of the ResumeFactory class. var FS = require('fs'); var ResumeConverter = require('./convert'); var chalk = require('chalk'); + var SyntaxErrorEx = require('../utils/syntax-error-ex'); @@ -28,12 +29,13 @@ Definition of the ResumeFactory class. /** Load one or more resumes from disk. */ - load: function ( sources, log, toFormat, objectify ) { + load: function ( sources, opts ) { + // Loop over all inputs, parsing each to JSON and then to a FRESHResume // or JRSResume object. var that = this; return sources.map( function( src ) { - return that.loadOne( src, log, toFormat, objectify ); + return that.loadOne( src, opts ); }); }, @@ -43,17 +45,21 @@ Definition of the ResumeFactory class. /** Load a single resume from disk. */ - loadOne: function( src, log, toFormat, objectify ) { + loadOne: function( src, opts ) { + + var log = opts.log; + var toFormat = opts.format; + var objectify = opts.objectify; // Get the destination format. Can be 'fresh', 'jrs', or null/undefined. toFormat && (toFormat = toFormat.toLowerCase().trim()); // Load and parse the resume JSON - var info = _parse( src, log, toFormat ); + var info = _parse( src, opts ); if( info.error ) return info; - var json = info.json; // Determine the resume format: FRESH or JRS + var json = info.json; var orgFormat = ( json.meta && json.meta.format && json.meta.format.startsWith('FRESH@') ) ? 'fresh' : 'jrs'; @@ -81,12 +87,12 @@ Definition of the ResumeFactory class. - function _parse( fileName, log, toFormat ) { + function _parse( fileName, opts ) { var rawData; try { // TODO: Core should not log - log( chalk.gray('Reading resume: ') + chalk.cyan.bold(fileName) ); + opts.log( chalk.gray('Reading resume: ') + chalk.cyan.bold(fileName) ); // Read the file rawData = FS.readFileSync( fileName, 'utf8' ); @@ -99,12 +105,18 @@ Definition of the ResumeFactory class. } catch( ex ) { - // If FS.readFileSync threw, pass the exception along. - if (!rawData) - throw ex; + // JSON.parse failed due to invalid JSON + if ( ex instanceof SyntaxError) { + var info = new SyntaxErrorEx( ex, rawData ); + opts.log( chalk.red.bold(fileName.toUpperCase() + ' contains invalid JSON on line ' + + info.line + ' column ' + info.col + '.' + + chalk.red(' Unable to validate.'))); + opts.log( chalk.red.bold('INTERNAL: ' + ex) ); + ex.handled = true; + } - // Otherwise if JSON.parse failed: probably a SyntaxError. - return { + if( opts.throw ) throw ex; + else return { error: ex, raw: rawData }; diff --git a/src/verbs/convert.js b/src/verbs/convert.js index 1d4a139..c70196d 100644 --- a/src/verbs/convert.js +++ b/src/verbs/convert.js @@ -23,7 +23,7 @@ Implementation of the 'convert' verb for HackMyResume. if( sources && dst && sources.length && dst.length && sources.length !== dst.length ) { throw { fluenterror: 7 }; } - var sourceResumes = ResumeFactory.load( sources, _log, null, true ); + var sourceResumes = ResumeFactory.load( sources, { log: _log, format: null, objectify: true, throw: true } ); sourceResumes.forEach(function( src, idx ) { var sheet = src.rez; var sourceFormat = ((sheet.basics && sheet.basics.imp) || sheet.imp).orgFormat === 'JRS' ? 'JRS' : 'FRESH'; diff --git a/src/verbs/generate.js b/src/verbs/generate.js index 5b7de95..50db299 100644 --- a/src/verbs/generate.js +++ b/src/verbs/generate.js @@ -59,7 +59,9 @@ Implementation of the 'generate' verb for HackMyResume. // Load input resumes... if( !src || !src.length ) { throw { fluenterror: 3 }; } - var sheets = ResumeFactory.load(src, _log, theme.render ? 'JRS' : 'FRESH', true); + var sheets = ResumeFactory.load(src, { + log: _log, format: theme.render ? 'JRS' : 'FRESH', objectify: true, throw: true + }); // Merge input resumes... var msg = ''; diff --git a/src/verbs/validate.js b/src/verbs/validate.js index 0d12ce4..2d7f744 100644 --- a/src/verbs/validate.js +++ b/src/verbs/validate.js @@ -30,17 +30,26 @@ Implementation of the 'validate' verb for HackMyResume. // Load input resumes... sources.forEach(function( src ) { - var result = ResumeFactory.loadOne( src, function(){}, null, false ); + var result = ResumeFactory.loadOne( src, { + log: function(){}, + format: null, + objectify: false, + throw: false + }); + if( result.error ) { - _log( chalk.white('Validating ') + chalk.gray.bold(src) + chalk.white(' against ') + chalk.gray.bold('AUTO') + chalk.white(' schema:') + chalk.red.bold(' BROKEN') ); + // TODO: Core should not log + _log( chalk.white('Validating ') + chalk.gray.bold(src) + + chalk.white(' against ') + chalk.gray.bold('AUTO') + + chalk.white(' schema:') + chalk.red.bold(' BROKEN') ); var ex = result.error; // alias if ( ex instanceof SyntaxError) { var info = new SyntaxErrorEx( ex, result.raw ); - _log( chalk.red.bold('--> ') + chalk.red(src.toUpperCase() + ' contains invalid JSON on line ' + - info.line + ' column ' + info.col + '.') + - chalk.red(' Unable to validate.') ); - _log( chalk.red(' INTERNAL: ' + ex) ); + _log( chalk.red.bold('--> ' + src.toUpperCase() + ' contains invalid JSON on line ' + + info.line + ' column ' + info.col + '.' + + chalk.red(' Unable to validate.') ) ); + _log( chalk.red.bold(' INTERNAL: ' + ex) ); } else { _log(chalk.red.bold('ERROR: ' + ex.toString()));