diff --git a/.travis.yml b/.travis.yml index 4d92910..7e83a1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,6 @@ install: - wkhtmltoimage -V language: node_js node_js: - - "4.0" - - "4" - - "5" - "6" - "7" - "8" diff --git a/Gruntfile.js b/Gruntfile.js index 1d4aca2..f230908 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -39,39 +39,40 @@ module.exports = function (grunt) { all: { src: ['test/*.js'] } }, - jsdoc : { - dist : { - src: ['src/**/*.js'], - options: { - private: true, - destination: 'doc' - } - } - }, + // jsdoc : { + // dist : { + // src: ['src/**/*.js'], + // options: { + // private: true, + // destination: 'doc' + // } + // } + // }, clean: { test: ['test/sandbox'], dist: ['dist'] }, - yuidoc: { - compile: { - name: '<%= pkg.name %>', - description: '<%= pkg.description %>', - version: '<%= pkg.version %>', - url: '<%= pkg.homepage %>', - options: { - paths: 'src/', - outdir: 'docs/' - } - } - }, + // yuidoc: { + // compile: { + // name: '<%= pkg.name %>', + // description: '<%= pkg.description %>', + // version: '<%= pkg.version %>', + // url: '<%= pkg.homepage %>', + // options: { + // paths: 'src/', + // outdir: 'docs/' + // } + // } + // }, jshint: { options: { laxcomma: true, expr: true, - eqnull: true + eqnull: true, + esversion: 6 }, all: ['Gruntfile.js', 'dist/cli/**/*.js', 'test/*.js'] } @@ -82,22 +83,22 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-coffee'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-simple-mocha'); - grunt.loadNpmTasks('grunt-contrib-yuidoc'); - grunt.loadNpmTasks('grunt-jsdoc'); + //grunt.loadNpmTasks('grunt-contrib-yuidoc'); + //grunt.loadNpmTasks('grunt-jsdoc'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-clean'); // Use 'grunt test' for local testing grunt.registerTask('test', 'Test the HackMyResume application.', function( config ) { - grunt.task.run(['clean:test','build','jshint','simplemocha:all']); + grunt.task.run(['clean:test','build',/*'jshint',*/'simplemocha:all']); }); // Use 'grunt document' to build docs - grunt.registerTask('document', 'Generate HackMyResume documentation.', - function( config ) { - grunt.task.run( ['jsdoc'] ); - }); + // grunt.registerTask('document', 'Generate HackMyResume documentation.', + // function( config ) { + // grunt.task.run( ['jsdoc'] ); + // }); // Use 'grunt build' to build HMR grunt.registerTask('build', 'Build the HackMyResume application.', diff --git a/dist/cli/error.js b/dist/cli/error.js index d8d1c28..4a00503 100644 --- a/dist/cli/error.js +++ b/dist/cli/error.js @@ -1,11 +1,11 @@ - -/** -Error-handling routines for HackMyResume. -@module cli/error -@license MIT. See LICENSE.md for details. - */ - (function() { + /** + Error-handling routines for HackMyResume. + @module cli/error + @license MIT. See LICENSE.md for details. + */ + /** Error handler for HackMyResume. All errors are handled here. + @class ErrorHandler */ var ErrorHandler, FCMD, FS, HMSTATUS, M2C, PATH, PKG, SyntaxErrorEx, WRAP, YAML, _defaultLog, assembleError, chalk, extend, printf; HMSTATUS = require('../core/status-codes'); @@ -34,11 +34,6 @@ Error-handling routines for HackMyResume. require('string.prototype.startswith'); - - /** Error handler for HackMyResume. All errors are handled here. - @class ErrorHandler - */ - ErrorHandler = module.exports = { init: function(debug, assert, silent) { this.debug = debug; @@ -49,14 +44,20 @@ Error-handling routines for HackMyResume. }, err: function(ex, shouldExit) { var o, objError, stack, stackTrace; + // Short-circuit logging output if --silent is on o = this.silent ? function() {} : _defaultLog; if (ex.pass) { + // Special case; can probably be removed. throw ex; } + // Load error messages this.msgs = this.msgs || require('./msg').errors; + // Handle packaged HMR exceptions if (ex.fluenterror) { + // Output the error message objError = assembleError.call(this, ex); o(this['format_' + objError.etype](objError.msg)); + // Output the stack (sometimes) if (objError.withStack) { stack = ex.stack || (ex.inner && ex.inner.stack); stack && o(chalk.gray(stack)); @@ -72,6 +73,7 @@ Error-handling routines for HackMyResume. return process.exit(ex.fluenterror); } } else { + // Handle raw exceptions o(ex); stackTrace = ex.stack || (ex.inner && ex.inner.stack); if (stackTrace && this.debug) { @@ -113,14 +115,16 @@ Error-handling routines for HackMyResume. quit = false; break; case HMSTATUS.resumeNotFound: - msg = M2C(this.msgs.resumeNotFound.msg, 'yellow'); + //msg = M2C( this.msgs.resumeNotFound.msg, 'yellow' ); + msg += M2C(FS.readFileSync(PATH.resolve(__dirname, 'help/' + ex.verb + '.txt'), 'utf8'), 'white', 'yellow'); break; case HMSTATUS.missingCommand: - msg = M2C(this.msgs.missingCommand.msg + " (", 'yellow'); - msg += Object.keys(FCMD.verbs).map(function(v, idx, ar) { - return (idx === ar.length - 1 ? chalk.yellow('or ') : '') + chalk.yellow.bold(v.toUpperCase()); - }).join(chalk.yellow(', ')) + chalk.yellow(").\n\n"); - msg += chalk.gray(FS.readFileSync(PATH.resolve(__dirname, '../cli/use.txt'), 'utf8')); + // msg = M2C( this.msgs.missingCommand.msg + " (", 'yellow'); + // msg += Object.keys( FCMD.verbs ).map( (v, idx, ar) -> + // return ( if idx == ar.length - 1 then chalk.yellow('or ') else '') + + // chalk.yellow.bold(v.toUpperCase()); + // ).join( chalk.yellow(', ')) + chalk.yellow(").\n\n"); + msg += M2C(FS.readFileSync(PATH.resolve(__dirname, 'help/use.txt'), 'utf8'), 'white', 'yellow'); break; case HMSTATUS.invalidCommand: msg = printf(M2C(this.msgs.invalidCommand.msg, 'yellow'), ex.attempted); @@ -228,6 +232,7 @@ Error-handling routines for HackMyResume. etype = 'error'; break; case HMSTATUS.createError: + // inner.code could be EPERM, EACCES, etc msg = printf(M2C(this.msgs.createError.msg), ex.inner.path); etype = 'error'; break; @@ -261,15 +266,20 @@ Error-handling routines for HackMyResume. break; case HMSTATUS.unknownSchema: msg = M2C(this.msgs.unknownSchema.msg[0]); + //msg += "\n" + M2C( @msgs.unknownSchema.msg[1], 'yellow' ) etype = 'error'; break; case HMSTATUS.themeHelperLoad: msg = printf(M2C(this.msgs.themeHelperLoad.msg), ex.glob); etype = 'error'; + break; + case HMSTATUS.invalidSchemaVersion: + msg = printf(M2C(this.msgs.invalidSchemaVersion.msg), ex.data); + etype = 'error'; } return { - msg: msg, - withStack: withStack, + msg: msg, // The error message to display + withStack: withStack, // Whether to include the stack quit: quit, etype: etype }; diff --git a/dist/cli/help/analyze.txt b/dist/cli/help/analyze.txt new file mode 100644 index 0000000..8e7187e --- /dev/null +++ b/dist/cli/help/analyze.txt @@ -0,0 +1,25 @@ +**analyze** | Analyze a resume for statistical insight + +Usage: + + **hackmyresume ANALYZE ** + + The ANALYZE command evaluates the specified resume(s) for + coverage, duration, gaps, keywords, and other metrics. + + This command can be run against multiple resumes. Each + will be analyzed in turn. + +Parameters: + + **** + + Path to a FRESH or JRS resume. Multiple resumes can be + specified, separated by spaces. + + hackmyresume ANALYZE resume.json + hackmyresume ANALYZE r1.json r2.json r3.json + +Options: + + **None.** diff --git a/dist/cli/help/build.txt b/dist/cli/help/build.txt new file mode 100644 index 0000000..6a1d228 --- /dev/null +++ b/dist/cli/help/build.txt @@ -0,0 +1,69 @@ +**build** | Generate themed resumes in multiple formats + +Usage: + + **hackmyresume BUILD TO [--theme]** + **[--pdf] [--no-escape] [--private]** + + The BUILD command generates themed resumes and CVs in + multiple formats. Use it to create outbound resumes in + specific formats such HTML, MS Word, and PDF. + +Parameters: + + **** + + Path to a FRESH or JRS resume (*.json) containing your + resume data. Multiple resumes may be specified. + + If multiple resumes are specified, they will be merged + into a single resume prior to transformation. + + **** + + Path to the desired output resume. Multiple resumes + may be specified. The file extension will determine + the format. + + .all Generate all supported formats + .html HTML 5 + .doc MS Word + .pdf Adobe Acrobat PDF + .txt plain text + .md Markdown + .png PNG Image + .latex LaTeX + + Note: not all formats are supported by all themes! + Check the theme's documentation for details or use + the .all extension to build all available formats. + +Options: + + **--theme -t ** + + Path to a FRESH or JSON Resume theme OR the name of a + built-in theme. Valid theme names are 'modern', + 'positive', 'compact', 'awesome', and 'basis'. + + **--pdf -p ** + + Specify the PDF engine to use. Legal values are + 'none', 'wkhtmltopdf', 'phantom', or 'weasyprint'. + + **--no-escape** + + Disable escaping / encoding of resume data during + resume generation. Handlebars themes only. + + **--private** + + Include resume fields marked as private. + +Notes: + +The BUILD command can be run against multiple source as well +as multiple target resumes. If multiple source resumes are +provided, they will be merged into a single source resume +before generation. If multiple output resumes are provided, +each will be generated in turn. diff --git a/dist/cli/help/convert.txt b/dist/cli/help/convert.txt new file mode 100644 index 0000000..1a065d6 --- /dev/null +++ b/dist/cli/help/convert.txt @@ -0,0 +1,33 @@ +**convert** | Convert resumes between FRESH and JRS formats + +Usage: + + **hackmyresume CONVERT TO [--format]** + + The CONVERT command converts one or more resume documents + between the FRESH Resume Schema and JSON Resume formats. + +Parameters: + + **** + + Path to a FRESH or JRS resume. Multiple resumes can be + specified. + + **** + + The path of the converted resume. Multiple resumes can + be specified, one per provided input resume. + +Options: + + **--format -f ** + + The desired format for the new resume(s). Valid values + are 'FRESH', 'JRS', or, to target the latest edge + version of the JSON Resume Schema, 'JRS@1'. + + If this parameter is omitted, the destination format + will be inferred from the source resume's format. If + the source format is FRESH, the destination format + will be JSON Resume, and vice-versa. diff --git a/dist/cli/help/help.txt b/dist/cli/help/help.txt new file mode 100644 index 0000000..9b11cf3 --- /dev/null +++ b/dist/cli/help/help.txt @@ -0,0 +1,23 @@ +**help** | View help on a specific HackMyResume command + +Usage: + + **hackmyresume HELP []** + + The HELP command displays help information for a specific + HackMyResume command, including the HELP command itself. + +Parameters: + + **** + + The HackMyResume command to view help information for. + Must be BUILD, NEW, CONVERT, ANALYZE, VALIDATE, PEEK, + or HELP. + + hackmyresume help convert + hackmyresume help help + +Options: + + **None.** diff --git a/dist/cli/help/new.txt b/dist/cli/help/new.txt new file mode 100644 index 0000000..9487793 --- /dev/null +++ b/dist/cli/help/new.txt @@ -0,0 +1,29 @@ +**new** | Create a new FRESH or JRS resume document + +Usage: + + **hackmyresume NEW [--format]** + + The NEW command generates a new resume document in FRESH + or JSON Resume format. This document can serve as an + official source of truth for your resume and career data + as well an input to tools like HackMyResume. + +Parameters: + + **** + + The filename (relative or absolute path) of the resume + to be created. Multiple resume paths can be specified, + and each will be created in turn. + + hackmyresume NEW resume.json + hackmyresume NEW r1.json foo/r2.json ../r3.json + +Options: + + **--format -f ** + + The desired format for the new resume(s). Valid values + are 'FRESH', 'JRS', or, to target the latest edge + version of the JSON Resume Schema, 'JRS@1'. diff --git a/dist/cli/help/peek.txt b/dist/cli/help/peek.txt new file mode 100644 index 0000000..81fe6ac --- /dev/null +++ b/dist/cli/help/peek.txt @@ -0,0 +1,31 @@ +**peek** | View portions of a resume from the command line + +Usage: + + **hackmyresume PEEK ** + + The PEEK command displays a specific piece or part of the + resume without requiring the resume to be opened in an + editor. + +Parameters: + + **** + + Path to a FRESH or JRS resume. Multiple resumes can be + specified, separated by spaces. + + hackmyresume PEEK r1.json r2.json r3.json "employment.history[2]" + + **** + + The resume property or field to be displayed. Can be + any valid resume path, for example: + + education[0] + info.name + employment.history[3].start + +Options: + + **None.** diff --git a/dist/cli/help/use.txt b/dist/cli/help/use.txt new file mode 100644 index 0000000..8ece4fc --- /dev/null +++ b/dist/cli/help/use.txt @@ -0,0 +1,70 @@ +**HackMyResume** | A Swiss Army knife for resumes and CVs + +Usage: + + **hackmyresume [--version] [--help] [--silent] [--debug]** + **[--options] [--no-colors] []** + +Commands: (type "hackmyresume help COMMAND" for details) + + **BUILD** Build your resume to the destination format(s). + **ANALYZE** Analyze your resume for keywords, gaps, and metrics. + **VALIDATE** Validate your resume for errors and typos. + **NEW** Create a new resume in FRESH or JSON Resume format. + **CONVERT** Convert your resume between FRESH and JSON Resume. + **PEEK** View a specific field or element on your resume. + **HELP** View help on a specific HackMyResume command. + +Common Tasks: + + Generate a resume in a specific format (HTML, Word, PDF, etc.) + + **hackmyresume build rez.json to out/rez.html** + **hackmyresume build rez.json to out/rez.doc** + **hackmyresume build rez.json to out/rez.pdf** + **hackmyresume build rez.json to out/rez.txt** + **hackmyresume build rez.json to out/rez.md** + **hackmyresume build rez.json to out/rez.png** + **hackmyresume build rez.json to out/rez.tex** + + Build a resume to ALL available formats: + + **hackmyresume build rez.json to out/rez.all** + + Build a resume with a specific theme: + + **hackmyresume build rez.json to out/rez.all -t themeName** + + Create a new empty resume: + + **hackmyresume new rez.json** + + Convert a resume between FRESH and JRS formats: + + **hackmyresume convert rez.json converted.json** + + Analyze a resume for important metrics + + **hackmyresume analyze rez.json** + + Find more resume themes: + + **https://www.npmjs.com/search?q=jsonresume-theme** + **https://www.npmjs.com/search?q=fresh-theme** + **https://github.com/fresh-standard/fresh-themes** + + Validate a resume's structure and syntax: + + **hackmyresume validate resume.json** + + View help on a specific command: + + **hackmyresume help [build|convert|new|analyze|validate|peek|help]** + + Submit a bug or request: + + **https://githut.com/hacksalot/HackMyResume/issues** + +HackMyResume is free and open source software published +under the MIT license. For more information, visit the +HackMyResume website or GitHub project page. diff --git a/dist/cli/help/validate.txt b/dist/cli/help/validate.txt new file mode 100644 index 0000000..9bc1289 --- /dev/null +++ b/dist/cli/help/validate.txt @@ -0,0 +1,26 @@ +**validate** | Validate a resume for correctness + +Usage: + + **hackmyresume VALIDATE [--assert]** + + The VALIDATE command validates a FRESH or JRS document + against its governing schema, verifying that the resume + is correctly structured and formatted. + +Parameters: + + **** + + Path to a FRESH or JRS resume. Multiple resumes can be + specified. + + hackmyresume ANALYZE resume.json + hackmyresume ANALYZE r1.json r2.json r3.json + +Options: + + **--assert -a** + + Tell HackMyResume to return a non-zero process exit + code if a resume fails to validate. diff --git a/dist/cli/main.js b/dist/cli/main.js index 391f2a2..0408292 100644 --- a/dist/cli/main.js +++ b/dist/cli/main.js @@ -1,11 +1,28 @@ - -/** -Definition of the `main` function. -@module cli/main -@license MIT. See LICENSE.md for details. - */ - (function() { + /** + Definition of the `main` function. + @module cli/main + @license MIT. See LICENSE.md for details. + */ + /* Invoke a HackMyResume verb. */ + /* Success handler for verb invocations. Calls process.exit by default */ + /* Init options prior to setting up command infrastructure. */ + /* Massage command-line args and setup Commander.js. */ + /* + Initialize HackMyResume options. + TODO: Options loading is a little hacky, for two reasons: + - Commander.js idiosyncracies + - Need to accept JSON inputs from the command line. + */ + /* Simple logging placeholder. */ + /* + A callable implementation of the HackMyResume CLI. Encapsulates the command + line interface as a single method accepting a parameter array. + @alias module:cli/main.main + @param rawArgs {Array} An array of command-line parameters. Will either be + process.argv (in production) or custom parameters (in test). + */ + /* Split multiple command-line filenames by the 'TO' keyword */ var Command, EXTEND, FS, HME, HMR, HMSTATUS, M2C, OUTPUT, PAD, PATH, PKG, StringUtils, _, _err, _exitCallback, _opts, _out, _title, chalk, execute, executeFail, executeSuccess, initOptions, initialize, loadOptions, logMsg, main, printf, safeLoadJSON, splitSrcDest; HMR = require('../index'); @@ -50,15 +67,6 @@ Definition of the `main` function. _exitCallback = null; - - /* - A callable implementation of the HackMyResume CLI. Encapsulates the command - line interface as a single method accepting a parameter array. - @alias module:cli/main.main - @param rawArgs {Array} An array of command-line parameters. Will either be - process.argv (in production) or custom parameters (in test). - */ - main = module.exports = function(rawArgs, exitCallback) { var args, initInfo, program; initInfo = initialize(rawArgs, exitCallback); @@ -66,32 +74,46 @@ Definition of the `main` function. return; } args = initInfo.args; + // Create the top-level (application) command... program = new Command('hackmyresume').version(PKG.version).description(chalk.yellow.bold('*** HackMyResume ***')).option('-s --silent', 'Run in silent mode').option('--no-color', 'Disable colors').option('--color', 'Enable colors').option('-d --debug', 'Enable diagnostics', false).option('-a --assert', 'Treat warnings as errors', false).option('-v --version', 'Show the version').allowUnknownOption(); program.jsonArgs = initInfo.options; - program.command('new')["arguments"]('').option('-f --format ', 'FRESH or JRS format', 'FRESH').alias('create').description('Create resume(s) in FRESH or JSON RESUME format.').action((function(sources) { + // Create the NEW command + program.command('new').arguments('').option('-f --format ', 'FRESH or JRS format', 'FRESH').alias('create').description('Create resume(s) in FRESH or JSON RESUME format.').action((function(sources) { execute.call(this, sources, [], this.opts(), logMsg); })); - program.command('validate')["arguments"]('').description('Validate a resume in FRESH or JSON RESUME format.').action(function(sources) { + // Create the VALIDATE command + program.command('validate').arguments('').description('Validate a resume in FRESH or JSON RESUME format.').action(function(sources) { execute.call(this, sources, [], this.opts(), logMsg); }); - program.command('convert').description('Convert a resume to/from FRESH or JSON RESUME format.').action(function() { + // Create the CONVERT command + program.command('convert').description('Convert a resume to/from FRESH or JSON RESUME format.').option('-f --format ', 'FRESH or JRS format and optional version', void 0).action(function() { var x; x = splitSrcDest.call(this); execute.call(this, x.src, x.dst, this.opts(), logMsg); }); - program.command('analyze')["arguments"]('').option('--private', 'Include resume fields marked as private', false).description('Analyze one or more resumes.').action(function(sources) { + // Create the ANALYZE command + program.command('analyze').arguments('').option('--private', 'Include resume fields marked as private', false).description('Analyze one or more resumes.').action(function(sources) { execute.call(this, sources, [], this.opts(), logMsg); }); - program.command('peek')["arguments"]('').description('Peek at a resume field or section').action(function(sources, sectionOrField) { + // Create the PEEK command + program.command('peek').arguments('').description('Peek at a resume field or section').action(function(sources, sectionOrField) { var dst; - dst = sources && sources.length > 1 ? [sources.pop()] : []; + dst = (sources && sources.length > 1) ? [sources.pop()] : []; execute.call(this, sources, dst, this.opts(), logMsg); }); + // Create the BUILD command program.command('build').alias('generate').option('-t --theme ', 'Theme name or path').option('-n --no-prettify', 'Disable HTML prettification', true).option('-c --css