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

Add baseline support for local generation of JSON Resume themes.

This commit is contained in:
hacksalot 2015-12-28 04:37:42 -05:00
parent 547b87afc6
commit 3c1ae4cbd1
9 changed files with 122 additions and 67 deletions

View File

@ -62,6 +62,7 @@
"path-exists": "^2.1.0", "path-exists": "^2.1.0",
"recursive-readdir-sync": "^1.0.6", "recursive-readdir-sync": "^1.0.6",
"simple-html-tokenizer": "^0.2.0", "simple-html-tokenizer": "^0.2.0",
"string.prototype.startswith": "^0.2.0",
"underscore": "^1.8.3", "underscore": "^1.8.3",
"wkhtmltopdf": "^0.1.5", "wkhtmltopdf": "^0.1.5",
"xml-escape": "^1.0.0", "xml-escape": "^1.0.0",

View File

@ -143,15 +143,12 @@ Definition of the FRESHResume class.
}; };
/** /**
Open and parse the specified JSON resume sheet. Merge the JSON object model Initialize the FreshResume from JSON data.
onto this Sheet instance with extend() and convert sheet dates to a safe & Open and parse the specified FRESH resume. Merge the JSON object model onto
this Sheet instance with extend() and convert sheet dates to a safe &
consistent format. Then sort each section by startDate descending. consistent format. Then sort each section by startDate descending.
*/ */
FreshResume.prototype.parse = function( stringData, opts ) { FreshResume.prototype.parseJSON = function( rep, opts ) {
// Parse the incoming JSON representation
var rep = JSON.parse( stringData );
// Convert JSON Resume to FRESH if necessary // Convert JSON Resume to FRESH if necessary
if( rep.basics ) { if( rep.basics ) {
rep = CONVERTER.toFRESH( rep ); rep = CONVERTER.toFRESH( rep );
@ -178,6 +175,13 @@ Definition of the FRESHResume class.
return this; return this;
}; };
/**
Initialize the the FreshResume from string data.
*/
FreshResume.prototype.parse = function( stringData, opts ) {
return this.parseJSON( JSON.parse( stringData ), opts );
};
/** /**
Return a unique list of all keywords across all skills. Return a unique list of all keywords across all skills.
*/ */

View File

@ -70,6 +70,7 @@ Definition of the JRSResume class.
}; };
/** /**
Initialize the JRS Resume from string data.
Open and parse the specified JSON resume sheet. Merge the JSON object model Open and parse the specified JSON resume sheet. Merge the JSON object model
onto this Sheet instance with extend() and convert sheet dates to a safe & onto this Sheet instance with extend() and convert sheet dates to a safe &
consistent format. Then sort each section by startDate descending. consistent format. Then sort each section by startDate descending.
@ -77,7 +78,14 @@ Definition of the JRSResume class.
JRSResume.prototype.parse = function( stringData, opts ) { JRSResume.prototype.parse = function( stringData, opts ) {
opts = opts || { }; opts = opts || { };
var rep = JSON.parse( stringData ); var rep = JSON.parse( stringData );
return this.parseJSON( rep, opts );
};
/**
Initialize the JRSRume from JSON data.
*/
JRSResume.prototype.parseJSON = function( rep, opts ) {
opts = opts || { };
extend( true, this, rep ); extend( true, this, rep );
// Set up metadata // Set up metadata
if( opts.imp === undefined || opts.imp ) { if( opts.imp === undefined || opts.imp ) {

View File

@ -1,13 +0,0 @@
(function(){
var FRESHResume = require('../core/fresh-resume');
module.exports = function loadSourceResumes( src, log, fn ) {
return src.map( function( res ) {
log( 'Reading '.info + 'SOURCE'.infoBold + ' resume: '.info +
res.cyan.bold );
return (fn && fn(res)) || (new FRESHResume()).open( res );
});
};
}());

View File

@ -0,0 +1,43 @@
/**
Core resume-loading logic for HackMyResume.
@module resume-factory.js
*/
(function(){
require('string.prototype.startswith');
var FS = require('fs');
var ResumeConverter = require('./convert');
/**
A simple factory class for FRESH and JSON Resumes.
@class ResumeFactory
*/
module.exports = {
/**
Load one or more resumes in a specific source format.
*/
load: function ( src, log, fn, toFormat ) {
toFormat = toFormat && (toFormat.toLowerCase().trim()) || 'fresh';
var ResumeClass = require('../core/' + toFormat + '-resume');
return src.map( function( res ) {
var rezJson = JSON.parse( FS.readFileSync( res ) );
var orgFormat = ( rezJson.meta && rezJson.meta.format &&
rezJson.meta.format.startsWith('FRESH@') ) ?
'fresh' : 'jrs';
if(orgFormat !== toFormat) {
rezJson = ResumeConverter[ 'to' + toFormat.toUpperCase() ]( rezJson );
}
// TODO: Core should not log
log( 'Reading '.info + orgFormat.toUpperCase().infoBold + ' resume: '.info + res.cyan.bold );
return (fn && fn(res)) || (new ResumeClass()).parseJSON( rezJson );
});
}
};
}());

View File

@ -1,6 +1,6 @@
/** /**
Definition of the Theme class. Definition of the Theme class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk. @license MIT. Copyright (c) 2015 hacksalot / FluentDesk.
@module theme.js @module theme.js
*/ */
@ -12,6 +12,7 @@ Definition of the Theme class.
, _ = require('underscore') , _ = require('underscore')
, PATH = require('path') , PATH = require('path')
, parsePath = require('parse-filepath') , parsePath = require('parse-filepath')
, pathExists = require('path-exists').sync
, EXTEND = require('../utils/extend') , EXTEND = require('../utils/extend')
, moment = require('moment') , moment = require('moment')
, RECURSIVE_READ_DIR = require('recursive-readdir-sync'); , RECURSIVE_READ_DIR = require('recursive-readdir-sync');
@ -29,9 +30,26 @@ Definition of the Theme class.
*/ */
Theme.prototype.open = function( themeFolder ) { Theme.prototype.open = function( themeFolder ) {
// Open the [theme-name].json file; should have the same name as folder
this.folder = themeFolder; this.folder = themeFolder;
// Open the [theme-name].json file; should have the same name as folder
var pathInfo = parsePath( themeFolder ); var pathInfo = parsePath( themeFolder );
// Set up a formats hash for the theme
var formatsHash = { };
// See if the theme has a package.json. If so, load it.
var packageJsonPath = PATH.join(themeFolder, 'package.json');
if( pathExists( packageJsonPath ) ) {
var themePack = require( themeFolder );
var themePkgJson = require( packageJsonPath );
this.name = themePkgJson.name;
this.render = (themePack && themePack.render) || undefined;
this.formats = { html: { title: 'html', outFormat: 'html', ext: 'html', path: null, data: null } };
return this;
}
// Otherwise, do a full theme load
var themeFile = PATH.join( themeFolder, pathInfo.basename + '.json' ); var themeFile = PATH.join( themeFolder, pathInfo.basename + '.json' );
var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) ); var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) );
var that = this; var that = this;
@ -39,9 +57,6 @@ Definition of the Theme class.
// Move properties from the theme JSON file to the theme object // Move properties from the theme JSON file to the theme object
EXTEND( true, this, themeInfo ); EXTEND( true, this, themeInfo );
// Set up a formats has for the theme
var formatsHash = { };
// Check for an explicit "formats" entry in the theme JSON. If it has one, // Check for an explicit "formats" entry in the theme JSON. If it has one,
// then this theme declares its files explicitly. // then this theme declares its files explicitly.
if( !!this.formats ) { if( !!this.formats ) {

View File

@ -1,6 +1,6 @@
(function(){ (function(){
var loadSourceResumes = require('../core/load-source-resumes'); var ResumeFactory = require('../core/resume-factory');
/** /**
Convert between FRESH and JRS formats. Convert between FRESH and JRS formats.
@ -16,7 +16,7 @@
if( src && dst && src.length && dst.length && src.length !== dst.length ) { if( src && dst && src.length && dst.length && src.length !== dst.length ) {
throw { fluenterror: 7 }; throw { fluenterror: 7 };
} }
var sheets = loadSourceResumes( src, _log ); var sheets = ResumeFactory.load( src, _log );
sheets.forEach(function(sheet, idx){ 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';

View File

@ -1,11 +1,12 @@
(function() { (function() {
var PATH = require('path') var PATH = require('path')
, FS = require('fs')
, parsePath = require('parse-filepath') , parsePath = require('parse-filepath')
, MKDIRP = require('mkdirp') , MKDIRP = require('mkdirp')
, _opts = require('../core/default-options') , _opts = require('../core/default-options')
, FluentTheme = require('../core/theme') , FluentTheme = require('../core/theme')
, loadSourceResumes = require('../core/load-source-resumes') , ResumeFactory = require('../core/resume-factory')
, _ = require('underscore') , _ = require('underscore')
, _fmts = require('../core/default-formats') , _fmts = require('../core/default-formats')
, _err, _log, rez; , _err, _log, rez;
@ -36,9 +37,27 @@
_opts.theme = (opts.theme && opts.theme.toLowerCase().trim())|| 'modern'; _opts.theme = (opts.theme && opts.theme.toLowerCase().trim())|| 'modern';
_opts.prettify = opts.prettify === true ? _opts.prettify : false; _opts.prettify = opts.prettify === true ? _opts.prettify : false;
// Verify the specified theme name/path
var relativeThemeFolder = '../../node_modules/fluent-themes/themes';
var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme);
var exists = require('path-exists').sync;
if( !exists( tFolder ) ) {
tFolder = PATH.resolve( _opts.theme );
if (!exists( tFolder )) {
throw { fluenterror: 1, data: _opts.theme };
}
}
// Load the theme
var theTheme = (new FluentTheme()).open( tFolder );
_opts.themeObj = theTheme;
var numFormats = theTheme.formats ? Object.keys(theTheme.formats).length : 2;
_log( 'Applying '.info + theTheme.name.toUpperCase().infoBold +
(' theme (' + numFormats + ' formats)').info);
// Load input resumes... // Load input resumes...
if( !src || !src.length ) { throw { fluenterror: 3 }; } if( !src || !src.length ) { throw { fluenterror: 3 }; }
var sheets = loadSourceResumes( src, _log ); var sheets = ResumeFactory.load( src, _log, null, theTheme.render ? 'JRS' : 'FRESH' );
// Merge input resumes... // Merge input resumes...
var msg = ''; var msg = '';
@ -49,23 +68,6 @@
}); });
msg && _log(msg); msg && _log(msg);
// Verify the specified theme name/path
var relativeThemeFolder = '../../node_modules/fluent-themes/themes';
var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme);
var exists = require('path-exists').sync;
if (!exists( tFolder )) {
tFolder = PATH.resolve( _opts.theme );
if (!exists( tFolder )) {
throw { fluenterror: 1, data: _opts.theme };
}
}
// Load the theme
var theTheme = (new FluentTheme()).open( tFolder );
_opts.themeObj = theTheme;
_log( 'Applying '.info + theTheme.name.toUpperCase().infoBold +
(' theme (' + Object.keys(theTheme.formats).length + ' formats)').info);
// Expand output resumes... (can't use map() here) // Expand output resumes... (can't use map() here)
var targets = [], that = this; var targets = [], that = this;
( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) { ( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) {
@ -74,11 +76,15 @@
pa = parsePath(to), pa = parsePath(to),
fmat = pa.extname || '.all'; fmat = pa.extname || '.all';
targets.push.apply(targets, fmat === '.all' ? targets.push.apply(
targets, fmat === '.all' ?
Object.keys( theTheme.formats ).map(function(k){ Object.keys( theTheme.formats ).map(function(k){
var z = theTheme.formats[k]; var z = theTheme.formats[k];
return { file: to.replace(/all$/g,z.outFormat), fmt: z }; return { file: to.replace(/all$/g,z.outFormat), fmt: z };
}) : [{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]); }) :
[{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
}); });
@ -113,22 +119,6 @@
function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0]; function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0];
MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists; MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists;
theFormat.gen.generate( rez, f, _opts ); theFormat.gen.generate( rez, f, _opts );
// targInfo.fmt.files.forEach( function( form ) {
//
// if( form.action === 'transform' ) {
// var theFormat = _fmts.filter( function( fmt ) {
// return fmt.name === targInfo.fmt.outFormat;
// })[0];
// MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists;
// theFormat.gen.generate( rez, f, _opts );
// }
// else if( form.action === null ) {
// // Copy the file
// }
//
// });
} }
// Otherwise the theme has no files section // Otherwise the theme has no files section
else { else {
@ -139,9 +129,16 @@
theFormat = _fmts.filter( theFormat = _fmts.filter(
function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0]; function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0];
MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists; MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists;
if( theme.render ) {
var rezHtml = theme.render( rez );
FS.writeFileSync( f, rezHtml );
}
else {
theFormat.gen.generate( rez, f, _opts ); theFormat.gen.generate( rez, f, _opts );
} }
} }
}
catch( ex ) { catch( ex ) {
_err( ex ); _err( ex );
} }

View File

@ -1,7 +1,7 @@
(function() { (function() {
var FS = require('fs'); var FS = require('fs');
var loadSourceResumes = require('../core/load-source-resumes'); var ResumeFactory = require('../core/resume-factory');
module.exports = module.exports =
@ -20,7 +20,7 @@
}; };
// Load input resumes... // Load input resumes...
var sheets = loadSourceResumes(src, _log, function( res ) { var sheets = ResumeFactory.load(src, _log, function( res ) {
try { try {
return { return {
file: res, file: res,