mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2024-11-05 09:56:22 +00:00
Add baseline support for local generation of JSON Resume themes.
This commit is contained in:
parent
547b87afc6
commit
3c1ae4cbd1
@ -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",
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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 ) {
|
||||||
|
@ -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 );
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
}());
|
|
43
src/core/resume-factory.js
Normal file
43
src/core/resume-factory.js
Normal 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 );
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
@ -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 ) {
|
||||||
|
@ -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';
|
||||||
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user