mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2024-11-22 16:30:11 +00:00
Merge branch 'master' of https://github.com/hacksalot/HackMyResume
This commit is contained in:
commit
2b3c83c57e
@ -327,5 +327,5 @@ MIT. Go crazy. See [LICENSE.md][1] for details.
|
|||||||
[travis-image]: https://img.shields.io/travis/palomajs/paloma.svg?style=flat-square
|
[travis-image]: https://img.shields.io/travis/palomajs/paloma.svg?style=flat-square
|
||||||
[travis-url]: https://travis-ci.org/hacksalot/HackMyResume
|
[travis-url]: https://travis-ci.org/hacksalot/HackMyResume
|
||||||
[contribute]: CONTRIBUTING.md
|
[contribute]: CONTRIBUTING.md
|
||||||
[fresh-themes]: https://github.com/fluentdesk/fluent-themes
|
[fresh-themes]: https://github.com/fluentdesk/fresh-themes
|
||||||
[jrst]: https://www.npmjs.com/search?q=jsonresume-theme
|
[jrst]: https://www.npmjs.com/search?q=jsonresume-theme
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"copy": "^0.1.3",
|
"copy": "^0.1.3",
|
||||||
"fluent-themes": "~0.8.0-beta",
|
"fresh-themes": "~0.9.3-beta",
|
||||||
"fresca": "~0.2.2",
|
"fresca": "~0.2.2",
|
||||||
"fs-extra": "^0.24.0",
|
"fs-extra": "^0.24.0",
|
||||||
"handlebars": "^4.0.5",
|
"handlebars": "^4.0.5",
|
||||||
@ -79,6 +79,7 @@
|
|||||||
"grunt-contrib-yuidoc": "^0.10.0",
|
"grunt-contrib-yuidoc": "^0.10.0",
|
||||||
"grunt-simple-mocha": "*",
|
"grunt-simple-mocha": "*",
|
||||||
"jane-q-fullstacker": "fluentdesk/jane-q-fullstacker",
|
"jane-q-fullstacker": "fluentdesk/jane-q-fullstacker",
|
||||||
|
"johnny-trouble-resume": "fluentdesk/johnny-trouble-resume",
|
||||||
"jsonresume-theme-boilerplate": "^0.1.2",
|
"jsonresume-theme-boilerplate": "^0.1.2",
|
||||||
"jsonresume-theme-classy": "^1.0.9",
|
"jsonresume-theme-classy": "^1.0.9",
|
||||||
"jsonresume-theme-modern": "0.0.18",
|
"jsonresume-theme-modern": "0.0.18",
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
var HACKMYSTATUS = require('./status-codes')
|
var HACKMYSTATUS = require('./status-codes')
|
||||||
, PKG = require('../../package.json')
|
, PKG = require('../../package.json')
|
||||||
|
, FS = require('fs')
|
||||||
|
, FCMD = require('../hackmycmd')
|
||||||
|
, PATH = require('path')
|
||||||
, title = ('\n*** HackMyResume v' + PKG.version + ' ***').bold.white;
|
, title = ('\n*** HackMyResume v' + PKG.version + ' ***').bold.white;
|
||||||
|
|
||||||
var ErrorHandler = module.exports = {
|
var ErrorHandler = module.exports = {
|
||||||
@ -26,17 +29,21 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HACKMYSTATUS.resumeNotFound:
|
case HACKMYSTATUS.resumeNotFound:
|
||||||
msg = 'Please '.guide + 'specify a valid input resume'.guide.bold +
|
msg = 'Please '.guide + 'feed me a resume'.guide.bold +
|
||||||
' in FRESH or JSON Resume format.'.guide;
|
' in FRESH or JSON Resume format.'.guide;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HACKMYSTATUS.missingCommand:
|
case HACKMYSTATUS.missingCommand:
|
||||||
msg = title + "\nPlease ".guide + "specify a command".guide.bold + " (".guide +
|
msg = title + "\nPlease ".guide + "give me a command".guide.bold +
|
||||||
Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
|
" (".guide;
|
||||||
|
|
||||||
|
msg += Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
|
||||||
return (idx === ar.length - 1 ? 'or '.guide : '') +
|
return (idx === ar.length - 1 ? 'or '.guide : '') +
|
||||||
v.toUpperCase().guide;
|
v.toUpperCase().guide;
|
||||||
}).join(', '.guide) + ").\n\n".guide +
|
}).join(', '.guide) + ").\n\n".guide;
|
||||||
FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' ).info.bold;
|
|
||||||
|
msg += FS.readFileSync(
|
||||||
|
PATH.resolve(__dirname, '../use.txt'), 'utf8' ).info.bold;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HACKMYSTATUS.invalidCommand:
|
case HACKMYSTATUS.invalidCommand:
|
||||||
@ -45,7 +52,7 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HACKMYSTATUS.resumeNotFoundAlt:
|
case HACKMYSTATUS.resumeNotFoundAlt:
|
||||||
msg = 'Please '.guide + 'specify a valid input resume'.guide.bold +
|
msg = 'Please '.guide + 'feed me a resume'.guide.bold +
|
||||||
' in either FRESH or JSON Resume format.'.guide;
|
' in either FRESH or JSON Resume format.'.guide;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
Definition of the Theme class.
|
Definition of the FRESHTheme class.
|
||||||
@license MIT. Copyright (c) 2015 hacksalot / FluentDesk.
|
@module fresh-theme.js
|
||||||
@module theme.js
|
@license MIT. See LICENSE.md for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var FS = require('fs')
|
var FS = require('fs')
|
||||||
, extend = require('../utils/extend')
|
, extend = require('../utils/extend')
|
||||||
, validator = require('is-my-json-valid')
|
, validator = require('is-my-json-valid')
|
||||||
@ -15,20 +17,25 @@ Definition of the Theme class.
|
|||||||
, pathExists = require('path-exists').sync
|
, 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');
|
, READFILES = require('recursive-readdir-sync');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The Theme class is a representation of a HackMyResume theme asset.
|
The FRESHTheme class is a representation of a FRESH theme
|
||||||
@class Theme
|
asset. See also: JRSTheme.
|
||||||
|
@class FRESHTheme
|
||||||
*/
|
*/
|
||||||
function Theme() {
|
function FRESHTheme() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Open and parse the specified theme.
|
Open and parse the specified theme.
|
||||||
*/
|
*/
|
||||||
Theme.prototype.open = function( themeFolder ) {
|
FRESHTheme.prototype.open = function( themeFolder ) {
|
||||||
|
|
||||||
this.folder = themeFolder;
|
this.folder = themeFolder;
|
||||||
|
|
||||||
@ -38,18 +45,7 @@ Definition of the Theme class.
|
|||||||
// Set up a formats hash for the theme
|
// Set up a formats hash for the theme
|
||||||
var formatsHash = { };
|
var formatsHash = { };
|
||||||
|
|
||||||
// See if the theme has a package.json. If so, load it.
|
// Load the theme
|
||||||
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;
|
||||||
@ -67,10 +63,6 @@ Definition of the Theme class.
|
|||||||
formatsHash = loadImplicit.call( this );
|
formatsHash = loadImplicit.call( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add freebie formats every theme gets
|
|
||||||
formatsHash.json = { title: 'json', outFormat: 'json', pre: 'json', ext: 'json', path: null, data: null };
|
|
||||||
formatsHash.yml = { title: 'yaml', outFormat: 'yml', pre: 'yml', ext: 'yml', path: null, data: null };
|
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
this.formats = formatsHash;
|
this.formats = formatsHash;
|
||||||
|
|
||||||
@ -80,20 +72,29 @@ Definition of the Theme class.
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Determine if the theme supports the specified output format.
|
Determine if the theme supports the specified output format.
|
||||||
*/
|
*/
|
||||||
Theme.prototype.hasFormat = function( fmt ) {
|
FRESHTheme.prototype.hasFormat = function( fmt ) {
|
||||||
return _.has( this.formats, fmt );
|
return _.has( this.formats, fmt );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Determine if the theme supports the specified output format.
|
Determine if the theme supports the specified output format.
|
||||||
*/
|
*/
|
||||||
Theme.prototype.getFormat = function( fmt ) {
|
FRESHTheme.prototype.getFormat = function( fmt ) {
|
||||||
return this.formats[ fmt ];
|
return this.formats[ fmt ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Load the theme implicitly, by scanning the theme folder for
|
||||||
|
files. TODO: Refactor duplicated code with loadExplicit.
|
||||||
|
*/
|
||||||
function loadImplicit() {
|
function loadImplicit() {
|
||||||
|
|
||||||
// Set up a hash of formats supported by this theme.
|
// Set up a hash of formats supported by this theme.
|
||||||
@ -107,7 +108,7 @@ Definition of the Theme class.
|
|||||||
// Iterate over all files in the theme folder, producing an array, fmts,
|
// Iterate over all files in the theme folder, producing an array, fmts,
|
||||||
// containing info for each file. While we're doing that, also build up
|
// containing info for each file. While we're doing that, also build up
|
||||||
// the formatsHash object.
|
// the formatsHash object.
|
||||||
var fmts = RECURSIVE_READ_DIR( tplFolder ).map( function( absPath ) {
|
var fmts = READFILES(tplFolder).map( function(absPath) {
|
||||||
|
|
||||||
// If this file lives in a specific format folder within the theme,
|
// If this file lives in a specific format folder within the theme,
|
||||||
// such as "/latex" or "/html", then that format is the output format
|
// such as "/latex" or "/html", then that format is the output format
|
||||||
@ -135,7 +136,7 @@ Definition of the Theme class.
|
|||||||
// compact-[outputformat].[extension], for ex, compact-pdf.html.
|
// compact-[outputformat].[extension], for ex, compact-pdf.html.
|
||||||
if( !outFmt ) {
|
if( !outFmt ) {
|
||||||
var idx = pathInfo.name.lastIndexOf('-');
|
var idx = pathInfo.name.lastIndexOf('-');
|
||||||
outFmt = ( idx === -1 ) ? pathInfo.name : pathInfo.name.substr( idx + 1 );
|
outFmt = (idx === -1) ? pathInfo.name : pathInfo.name.substr(idx + 1);
|
||||||
isMajor = true;
|
isMajor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,9 +166,13 @@ Definition of the Theme class.
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Now, get all the CSS files...
|
// Now, get all the CSS files...
|
||||||
(this.cssFiles = fmts.filter(function( fmt ){ return fmt && (fmt.ext === 'css'); }))
|
(this.cssFiles = fmts.filter(function( fmt ){
|
||||||
|
return fmt && (fmt.ext === 'css');
|
||||||
|
}))
|
||||||
|
|
||||||
|
// For each CSS file, get its corresponding HTML file
|
||||||
.forEach(function( cssf ) {
|
.forEach(function( cssf ) {
|
||||||
// For each CSS file, get its corresponding HTML file
|
|
||||||
var idx = _.findIndex(fmts, function( fmt ) {
|
var idx = _.findIndex(fmts, function( fmt ) {
|
||||||
return fmt && fmt.pre === cssf.pre && fmt.ext === 'html';
|
return fmt && fmt.pre === cssf.pre && fmt.ext === 'html';
|
||||||
});
|
});
|
||||||
@ -184,33 +189,39 @@ Definition of the Theme class.
|
|||||||
return formatsHash;
|
return formatsHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Load the theme explicitly, by following the 'formats' hash
|
||||||
|
in the theme's JSON settings file.
|
||||||
|
*/
|
||||||
function loadExplicit() {
|
function loadExplicit() {
|
||||||
|
|
||||||
var that = this;
|
// Housekeeping
|
||||||
// Set up a hash of formats supported by this theme.
|
|
||||||
var formatsHash = { };
|
var formatsHash = { };
|
||||||
|
|
||||||
// Establish the base theme folder
|
|
||||||
var tplFolder = PATH.join( this.folder, 'src' );
|
var tplFolder = PATH.join( this.folder, 'src' );
|
||||||
|
|
||||||
var act = null;
|
var act = null;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
// Iterate over all files in the theme folder, producing an array, fmts,
|
// Iterate over all files in the theme folder, producing an array, fmts,
|
||||||
// containing info for each file. While we're doing that, also build up
|
// containing info for each file. While we're doing that, also build up
|
||||||
// the formatsHash object.
|
// the formatsHash object.
|
||||||
var fmts = RECURSIVE_READ_DIR( tplFolder ).map( function( absPath ) {
|
var fmts = READFILES( tplFolder ).map( function( absPath ) {
|
||||||
|
|
||||||
act = null;
|
act = null;
|
||||||
// If this file is mentioned in the theme's JSON file under "transforms"
|
// If this file is mentioned in the theme's JSON file under "transforms"
|
||||||
var pathInfo = parsePath(absPath);
|
var pathInfo = parsePath(absPath);
|
||||||
var absPathSafe = absPath.trim().toLowerCase();
|
var absPathSafe = absPath.trim().toLowerCase();
|
||||||
var outFmt = _.find( Object.keys( that.formats ), function( fmtKey ) {
|
var outFmt = _.find(
|
||||||
var fmtVal = that.formats[ fmtKey ];
|
Object.keys( that.formats ),
|
||||||
return _.some( fmtVal.transform, function( fpath ) {
|
function( fmtKey ) {
|
||||||
var absPathB = PATH.join( that.folder, fpath ).trim().toLowerCase();
|
var fmtVal = that.formats[ fmtKey ];
|
||||||
return absPathB === absPathSafe;
|
return _.some( fmtVal.transform, function(fpath) {
|
||||||
|
var absPathB = PATH.join( that.folder, fpath )
|
||||||
|
.trim().toLowerCase();
|
||||||
|
return absPathB === absPathSafe;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
if( outFmt ) {
|
if( outFmt ) {
|
||||||
act = 'transform';
|
act = 'transform';
|
||||||
}
|
}
|
||||||
@ -231,7 +242,7 @@ Definition of the Theme class.
|
|||||||
// compact-[outputformat].[extension], for ex, compact-pdf.html.
|
// compact-[outputformat].[extension], for ex, compact-pdf.html.
|
||||||
if( !outFmt ) {
|
if( !outFmt ) {
|
||||||
var idx = pathInfo.name.lastIndexOf('-');
|
var idx = pathInfo.name.lastIndexOf('-');
|
||||||
outFmt = ( idx === -1 ) ? pathInfo.name : pathInfo.name.substr( idx + 1 );
|
outFmt = (idx === -1) ? pathInfo.name : pathInfo.name.substr(idx + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have a valid output format now.
|
// We should have a valid output format now.
|
||||||
@ -261,7 +272,11 @@ Definition of the Theme class.
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Now, get all the CSS files...
|
// Now, get all the CSS files...
|
||||||
(this.cssFiles = fmts.filter(function( fmt ){ return fmt.ext === 'css'; }))
|
(this.cssFiles = fmts.filter(function( fmt ){
|
||||||
|
return fmt.ext === 'css';
|
||||||
|
}))
|
||||||
|
|
||||||
|
// For each CSS file, get its corresponding HTML file
|
||||||
.forEach(function( cssf ) {
|
.forEach(function( cssf ) {
|
||||||
// For each CSS file, get its corresponding HTML file
|
// For each CSS file, get its corresponding HTML file
|
||||||
var idx = _.findIndex(fmts, function( fmt ) {
|
var idx = _.findIndex(fmts, function( fmt ) {
|
||||||
@ -279,12 +294,22 @@ Definition of the Theme class.
|
|||||||
return formatsHash;
|
return formatsHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return a more friendly name for certain formats.
|
||||||
|
TODO: Refactor
|
||||||
|
*/
|
||||||
function friendlyName( val ) {
|
function friendlyName( val ) {
|
||||||
val = val.trim().toLowerCase();
|
val = val.trim().toLowerCase();
|
||||||
var friendly = { yml: 'yaml', md: 'markdown', txt: 'text' };
|
var friendly = { yml: 'yaml', md: 'markdown', txt: 'text' };
|
||||||
return friendly[val] || val;
|
return friendly[val] || val;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Theme;
|
|
||||||
|
|
||||||
|
module.exports = FRESHTheme;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}());
|
}());
|
85
src/core/jrs-theme.js
Normal file
85
src/core/jrs-theme.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
Definition of the JRSTheme class.
|
||||||
|
@module jrs-theme.js
|
||||||
|
@license MIT. See LICENSE.MD for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var _ = require('underscore')
|
||||||
|
, PATH = require('path')
|
||||||
|
, parsePath = require('parse-filepath')
|
||||||
|
, pathExists = require('path-exists').sync;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
The JRSTheme class is a representation of a JSON Resume
|
||||||
|
theme asset. See also: FRESHTheme.
|
||||||
|
@class JRSTheme
|
||||||
|
*/
|
||||||
|
function JRSTheme() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Open and parse the specified theme.
|
||||||
|
@method open
|
||||||
|
*/
|
||||||
|
JRSTheme.prototype.open = function( thFolder ) {
|
||||||
|
|
||||||
|
this.folder = thFolder;
|
||||||
|
|
||||||
|
// Open the [theme-name].json file; should have the same
|
||||||
|
// name as folder
|
||||||
|
var pathInfo = parsePath( thFolder );
|
||||||
|
|
||||||
|
// Open and parse the theme's package.json file.
|
||||||
|
var pkgJsonPath = PATH.join( thFolder, 'package.json' );
|
||||||
|
if( pathExists( pkgJsonPath )) {
|
||||||
|
var thApi = require( thFolder )
|
||||||
|
, thPkg = require( pkgJsonPath );
|
||||||
|
this.name = thPkg.name;
|
||||||
|
this.render = (thApi && thApi.render) || undefined;
|
||||||
|
this.formats = {
|
||||||
|
html: { title:'html', outFormat:'html', ext:'html' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw { fluenterror: 10 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determine if the theme supports the output format.
|
||||||
|
@method hasFormat
|
||||||
|
*/
|
||||||
|
JRSTheme.prototype.hasFormat = function( fmt ) {
|
||||||
|
return _.has( this.formats, fmt );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the requested output format.
|
||||||
|
@method getFormat
|
||||||
|
*/
|
||||||
|
JRSTheme.prototype.getFormat = function( fmt ) {
|
||||||
|
return this.formats[ fmt ];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = JRSTheme;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}());
|
@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
Status codes for HackMyResume.
|
Status codes for HackMyResume.
|
||||||
@module status-codes.js
|
@module status-codes.js
|
||||||
|
@license MIT. See LICENSE.MD for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function(){
|
(function(){
|
||||||
@ -15,7 +16,8 @@ Status codes for HackMyResume.
|
|||||||
resumeNotFoundAlt: 6,
|
resumeNotFoundAlt: 6,
|
||||||
inputOutputParity: 7,
|
inputOutputParity: 7,
|
||||||
createNameMissing: 8,
|
createNameMissing: 8,
|
||||||
wkhtmltopdf: 9
|
wkhtmltopdf: 9,
|
||||||
|
missingPackageJSON: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
Generic template helper definitions for FluentCV.
|
Generic template helper definitions for HackMyResume / FluentCV.
|
||||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
@license MIT. See LICENSE.md for details.
|
||||||
@module generic-helpers.js
|
@module generic-helpers.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -114,6 +114,16 @@ Generic template helper definitions for FluentCV.
|
|||||||
if (lhs || rhs) return options.fn(this);
|
if (lhs || rhs) return options.fn(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Conditional stylesheet link. Either display the link or embed the stylesheet
|
||||||
|
via <style></style> tag.
|
||||||
|
*/
|
||||||
|
styleSheet: function( file, options ) {
|
||||||
|
return ( this.opts.css === 'link') ?
|
||||||
|
'<link href="' + file + '" rel="stylesheet" type="text/css">' :
|
||||||
|
'<style>' + this.cssInfo.data + '</style>';
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Perform a generic comparison.
|
Perform a generic comparison.
|
||||||
See: http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates
|
See: http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates
|
||||||
|
@ -40,6 +40,7 @@ Definition of the HandlebarsGenerator class.
|
|||||||
RAW: json,
|
RAW: json,
|
||||||
filt: opts.filters,
|
filt: opts.filters,
|
||||||
cssInfo: cssInfo,
|
cssInfo: cssInfo,
|
||||||
|
opts: opts,
|
||||||
headFragment: opts.headFragment || ''
|
headFragment: opts.headFragment || ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ Definition of the UnderscoreGenerator class.
|
|||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Perform template-based resume generation using Underscore.js.
|
Perform template-based resume generation using Underscore.js.
|
||||||
@class UnderscoreGenerator
|
@class UnderscoreGenerator
|
||||||
@ -32,6 +31,10 @@ Definition of the UnderscoreGenerator class.
|
|||||||
// Strip {# comments #}
|
// Strip {# comments #}
|
||||||
jst = jst.replace( delims.comment, '');
|
jst = jst.replace( delims.comment, '');
|
||||||
|
|
||||||
|
var helpers = require('./generic-helpers');
|
||||||
|
helpers.opts = opts;
|
||||||
|
helpers.cssInfo = cssInfo;
|
||||||
|
|
||||||
// Compile and run the template. TODO: avoid unnecessary recompiles.
|
// Compile and run the template. TODO: avoid unnecessary recompiles.
|
||||||
var compiled = _.template(jst);
|
var compiled = _.template(jst);
|
||||||
var ret = compiled({
|
var ret = compiled({
|
||||||
@ -40,13 +43,15 @@ Definition of the UnderscoreGenerator class.
|
|||||||
XML: require('xml-escape'),
|
XML: require('xml-escape'),
|
||||||
RAW: json,
|
RAW: json,
|
||||||
cssInfo: cssInfo,
|
cssInfo: cssInfo,
|
||||||
headFragment: opts.headFragment || ''
|
headFragment: opts.headFragment || '',
|
||||||
|
opts: opts,
|
||||||
|
h: helpers
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
Definition of the BaseGenerator class.
|
Definition of the BaseGenerator class.
|
||||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
|
||||||
@module base-generator.js
|
@module base-generator.js
|
||||||
|
@license MIT. See LICENSE.md for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
Definition of the HtmlPngGenerator class.
|
Definition of the HtmlPngGenerator class.
|
||||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
@license MIT. See LICENSE.MD for details.
|
||||||
@module html-png-generator.js
|
@module html-png-generator.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ Definition of the HtmlPngGenerator class.
|
|||||||
, HTML = require( 'html' );
|
, HTML = require( 'html' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
An HTML-based PDF resume generator for HackMyResume.
|
An HTML-based PNG resume generator for HackMyResume.
|
||||||
*/
|
*/
|
||||||
var HtmlPngGenerator = module.exports = TemplateGenerator.extend({
|
var HtmlPngGenerator = module.exports = TemplateGenerator.extend({
|
||||||
|
|
||||||
@ -19,24 +19,29 @@ Definition of the HtmlPngGenerator class.
|
|||||||
this._super( 'png', 'html' );
|
this._super( 'png', 'html' );
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
invoke: function( rez, themeMarkup, cssInfo, opts ) {
|
||||||
Generate the binary PDF.
|
//return YAML.stringify( JSON.parse( rez.stringify() ), Infinity, 2 );
|
||||||
*/
|
},
|
||||||
onBeforeSave: function( info ) {
|
|
||||||
png( info.mk, info.outputFile );
|
generate: function( rez, f, opts ) {
|
||||||
return null; // halt further processing
|
var htmlResults = opts.targets.filter(function(t){
|
||||||
|
return t.fmt.outFormat === 'html';
|
||||||
|
});
|
||||||
|
var htmlFile = htmlResults[0].final.files.filter(function(fl){
|
||||||
|
return fl.info.ext === 'html';
|
||||||
|
});
|
||||||
|
png(htmlFile[0].data, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generate a PDF from HTML.
|
Generate a PNG from HTML.
|
||||||
*/
|
*/
|
||||||
function png( markup, fOut ) {
|
function png( markup, fOut ) {
|
||||||
|
// require('webshot')( markup , { encoding: 'binary', siteType: 'html' } )
|
||||||
require('webshot')( markup , { encoding: 'binary', siteType: 'html' } )
|
// .pipe( FS.createWriteStream( fOut ) );
|
||||||
.pipe( FS.createWriteStream( fOut ) );
|
require('webshot')( markup , fOut, { siteType: 'html' }, function(err) { } );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
Definition of the TemplateGenerator class.
|
Definition of the TemplateGenerator class.
|
||||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
@license MIT. See LICENSE.md for details.
|
||||||
@module template-generator.js
|
@module template-generator.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -17,7 +17,8 @@ Definition of the TemplateGenerator class.
|
|||||||
, MKDIRP = require('mkdirp')
|
, MKDIRP = require('mkdirp')
|
||||||
, BaseGenerator = require( './base-generator' )
|
, BaseGenerator = require( './base-generator' )
|
||||||
, EXTEND = require('../utils/extend')
|
, EXTEND = require('../utils/extend')
|
||||||
, Theme = require('../core/theme');
|
, FRESHTheme = require('../core/fresh-theme')
|
||||||
|
, JRSTheme = require('../core/jrs-theme');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +78,14 @@ Definition of the TemplateGenerator class.
|
|||||||
@method invoke
|
@method invoke
|
||||||
@param rez A FreshResume object.
|
@param rez A FreshResume object.
|
||||||
@param opts Generator options.
|
@param opts Generator options.
|
||||||
@returns An array of strings representing generated output files.
|
@returns An array of objects representing the generated output files. Each
|
||||||
|
object has this format:
|
||||||
|
|
||||||
|
{
|
||||||
|
files: [ { info: { }, data: [ ] }, { ... } ],
|
||||||
|
themeInfo: { }
|
||||||
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
invoke: function( rez, opts ) {
|
invoke: function( rez, opts ) {
|
||||||
|
|
||||||
@ -182,6 +190,8 @@ Definition of the TemplateGenerator class.
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return genInfo;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -220,20 +230,28 @@ Definition of the TemplateGenerator class.
|
|||||||
Given a theme title, load the corresponding theme.
|
Given a theme title, load the corresponding theme.
|
||||||
*/
|
*/
|
||||||
function themeFromMoniker() {
|
function themeFromMoniker() {
|
||||||
|
|
||||||
// Verify the specified theme name/path
|
// Verify the specified theme name/path
|
||||||
var tFolder = PATH.join(
|
var tFolder = PATH.join(
|
||||||
parsePath( require.resolve('fluent-themes') ).dirname,
|
parsePath( require.resolve('fresh-themes') ).dirname,
|
||||||
this.opts.theme
|
this.opts.theme
|
||||||
);
|
);
|
||||||
var exists = require('path-exists').sync;
|
|
||||||
if( !exists( tFolder ) ) {
|
|
||||||
tFolder = PATH.resolve( this.opts.theme );
|
|
||||||
if( !exists( tFolder ) ) {
|
|
||||||
throw { fluenterror: this.codes.themeNotFound, data: this.opts.theme};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var t = this.opts.themeObj || new Theme().open( tFolder );
|
var t;
|
||||||
|
if( this.opts.theme.startsWith('jsonresume-theme-') ) {
|
||||||
|
console.log('LOADING JSON RESUME');
|
||||||
|
t = new JRSTheme().open( tFolder );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var exists = require('path-exists').sync;
|
||||||
|
if( !exists( tFolder ) ) {
|
||||||
|
tFolder = PATH.resolve( this.opts.theme );
|
||||||
|
if( !exists( tFolder ) ) {
|
||||||
|
throw { fluenterror: this.codes.themeNotFound, data: this.opts.theme};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t = this.opts.themeObj || new FRESHTheme().open( tFolder );
|
||||||
|
}
|
||||||
|
|
||||||
// Load the theme and format
|
// Load the theme and format
|
||||||
return {
|
return {
|
||||||
|
@ -8,7 +8,8 @@ module.exports = {
|
|||||||
Sheet: require('./core/fresh-resume'),
|
Sheet: require('./core/fresh-resume'),
|
||||||
FRESHResume: require('./core/fresh-resume'),
|
FRESHResume: require('./core/fresh-resume'),
|
||||||
JRSResume: require('./core/jrs-resume'),
|
JRSResume: require('./core/jrs-resume'),
|
||||||
Theme: require('./core/theme'),
|
FRESHTheme: require('./core/fresh-theme'),
|
||||||
|
JRSTheme: require('./core/jrs-theme'),
|
||||||
FluentDate: require('./core/fluent-date'),
|
FluentDate: require('./core/fluent-date'),
|
||||||
HtmlGenerator: require('./gen/html-generator'),
|
HtmlGenerator: require('./gen/html-generator'),
|
||||||
TextGenerator: require('./gen/text-generator'),
|
TextGenerator: require('./gen/text-generator'),
|
||||||
|
@ -8,29 +8,34 @@ Internal resume generation logic for HackMyResume.
|
|||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
|
|
||||||
var unused = require('./utils/string')
|
var unused = require('./utils/string')
|
||||||
, PATH = require('path');
|
, PATH = require('path')
|
||||||
|
, FS = require('fs');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Display help documentation.
|
Display help documentation.
|
||||||
*/
|
*/
|
||||||
function help() {
|
function help() {
|
||||||
console.log( FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' )
|
var manPage = FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' );
|
||||||
.useful.bold );
|
console.log( manPage.useful.bold );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Internal module interface. Used by FCV Desktop and HMR.
|
Internal module interface. Used by FCV Desktop and HMR.
|
||||||
*/
|
*/
|
||||||
|
var v = {
|
||||||
|
build: require('./verbs/generate'),
|
||||||
|
validate: require('./verbs/validate'),
|
||||||
|
convert: require('./verbs/convert'),
|
||||||
|
new: require('./verbs/create'),
|
||||||
|
help: help
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
verbs: {
|
verbs: v,
|
||||||
generate: require('./verbs/generate'),
|
alias: {
|
||||||
build: require('./verbs/generate'),
|
generate: v.build,
|
||||||
validate: require('./verbs/validate'),
|
create: v.build
|
||||||
convert: require('./verbs/convert'),
|
|
||||||
create: require('./verbs/create'),
|
|
||||||
new: require('./verbs/create'),
|
|
||||||
help: help
|
|
||||||
},
|
},
|
||||||
lib: require('./hackmyapi'),
|
lib: require('./hackmyapi'),
|
||||||
options: require('./core/default-options'),
|
options: require('./core/default-options'),
|
||||||
|
@ -55,7 +55,7 @@ function main() {
|
|||||||
// 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 ] && !FCMD.alias[ verb ] ) {
|
||||||
logMsg('Invalid command: "'.warn + verb.warn.bold + '"'.warn);
|
logMsg('Invalid command: "'.warn + verb.warn.bold + '"'.warn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -74,10 +74,9 @@ function main() {
|
|||||||
var src = a._.slice(1, splitAt === -1 ? undefined : splitAt );
|
var src = a._.slice(1, splitAt === -1 ? undefined : splitAt );
|
||||||
var dst = splitAt === -1 ? [] : a._.slice( splitAt + 1 );
|
var dst = splitAt === -1 ? [] : a._.slice( splitAt + 1 );
|
||||||
( splitAt === -1 ) && src.length > 1 && dst.push( src.pop() ); // Allow omitting TO keyword
|
( splitAt === -1 ) && src.length > 1 && dst.push( src.pop() ); // Allow omitting TO keyword
|
||||||
var parms = [ src, dst, opts, logMsg ];
|
|
||||||
|
|
||||||
// Invoke the action
|
// Invoke the action
|
||||||
FCMD.verbs[ verb ].apply( null, parms );
|
(FCMD.verbs[verb] || FCMD.alias[verb]).apply(null, [src, dst, opts, logMsg]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +91,7 @@ function getOpts( args ) {
|
|||||||
theme: args.t || 'modern',
|
theme: args.t || 'modern',
|
||||||
format: args.f || 'FRESH',
|
format: args.f || 'FRESH',
|
||||||
prettify: !noPretty,
|
prettify: !noPretty,
|
||||||
silent: args.s || args.silent
|
silent: args.s || args.silent,
|
||||||
|
css: args.css || 'embed'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,29 @@
|
|||||||
|
/**
|
||||||
|
Implementation of the 'generate' verb for HackMyResume.
|
||||||
|
@module generate.js
|
||||||
|
@license MIT. See LICENSE.md for details.
|
||||||
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var PATH = require('path')
|
var PATH = require('path')
|
||||||
, FS = require('fs')
|
, FS = require('fs')
|
||||||
, parsePath = require('parse-filepath')
|
|
||||||
, MD = require('marked')
|
, MD = require('marked')
|
||||||
, MKDIRP = require('mkdirp')
|
, MKDIRP = require('mkdirp')
|
||||||
|
, EXTEND = require('../utils/extend')
|
||||||
|
, parsePath = require('parse-filepath')
|
||||||
, _opts = require('../core/default-options')
|
, _opts = require('../core/default-options')
|
||||||
, FluentTheme = require('../core/theme')
|
, FluentTheme = require('../core/fresh-theme')
|
||||||
|
, JRSTheme = require('../core/jrs-theme')
|
||||||
, ResumeFactory = require('../core/resume-factory')
|
, 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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Handle an exception.
|
Handle an exception.
|
||||||
*/
|
*/
|
||||||
@ -19,42 +31,29 @@
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports =
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Given a source JSON resume, a destination resume path, and a theme file,
|
Given a source resume in FRESH or JRS format, a destination resume path, and a
|
||||||
generate 0..N resumes in the desired formats.
|
theme file, generate 0..N resumes in the desired formats.
|
||||||
@param src Path to the source JSON resume file: "rez/resume.json".
|
@param src Path to the source JSON resume file: "rez/resume.json".
|
||||||
@param dst An array of paths to the target resume file(s).
|
@param dst An array of paths to the target resume file(s).
|
||||||
@param theme Friendly name of the resume theme. Defaults to "modern".
|
@param theme Friendly name of the resume theme. Defaults to "modern".
|
||||||
@param logger Optional logging override.
|
@param logger Optional logging override.
|
||||||
*/
|
*/
|
||||||
function generate( src, dst, opts, logger, errHandler ) {
|
function build( src, dst, opts, logger, errHandler ) {
|
||||||
|
|
||||||
|
// Housekeeping...
|
||||||
_log = logger || console.log;
|
_log = logger || console.log;
|
||||||
_err = errHandler || error;
|
_err = errHandler || error;
|
||||||
|
|
||||||
//_opts = extend( true, _opts, opts );
|
//_opts = extend( true, _opts, opts );
|
||||||
_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;
|
||||||
|
_opts.css = opts.css;
|
||||||
|
|
||||||
// Verify the specified theme name/path
|
// Load the theme...
|
||||||
var relativeThemeFolder = '../../node_modules/fluent-themes/themes';
|
var tFolder = verify_theme( _opts.theme );
|
||||||
var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme);
|
var theTheme = load_theme( tFolder );
|
||||||
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 }; }
|
||||||
@ -70,41 +69,29 @@
|
|||||||
});
|
});
|
||||||
msg && _log(msg);
|
msg && _log(msg);
|
||||||
|
|
||||||
// Expand output resumes... (can't use map() here)
|
// Expand output resumes...
|
||||||
var targets = [], that = this;
|
var targets = expand( dst, theTheme );
|
||||||
( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) {
|
|
||||||
|
|
||||||
var to = PATH.resolve(t),
|
|
||||||
pa = parsePath(to),
|
|
||||||
fmat = pa.extname || '.all';
|
|
||||||
|
|
||||||
targets.push.apply(
|
|
||||||
targets, fmat === '.all' ?
|
|
||||||
|
|
||||||
Object.keys( theTheme.formats ).map(function(k){
|
|
||||||
var z = theTheme.formats[k];
|
|
||||||
return { file: to.replace(/all$/g,z.outFormat), fmt: z };
|
|
||||||
}) :
|
|
||||||
|
|
||||||
[{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run the transformation!
|
// Run the transformation!
|
||||||
var finished = targets.map( function(t) { return single(t, theTheme); });
|
targets.forEach( function(t) {
|
||||||
|
t.final = single( t, theTheme, targets );
|
||||||
|
});
|
||||||
|
|
||||||
// Don't send the client back empty-handed
|
// Don't send the client back empty-handed
|
||||||
return { sheet: rez, targets: targets, processed: finished };
|
return { sheet: rez, targets: targets, processed: targets };
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generate a single resume of a specific format.
|
Generate a single target resume such as "out/rez.html" or "out/rez.doc".
|
||||||
@param f Full path to the destination resume to generate, for example,
|
@param targInfo Information for the target resume.
|
||||||
"/foo/bar/resume.pdf" or "c:\foo\bar\resume.txt".
|
@param theme A FRESHTheme or JRSTheme object.
|
||||||
|
@returns
|
||||||
*/
|
*/
|
||||||
function single( targInfo, theme ) {
|
function single( targInfo, theme, finished ) {
|
||||||
|
|
||||||
function MDIN(txt) {
|
function MDIN(txt) { // TODO: Move this
|
||||||
return MD(txt || '' ).replace(/^\s*<p>|<\/p>\s*$/gi, '');
|
return MD(txt || '' ).replace(/^\s*<p>|<\/p>\s*$/gi, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,32 +101,32 @@
|
|||||||
, fName = PATH.basename(f, '.' + fType)
|
, fName = PATH.basename(f, '.' + fType)
|
||||||
, theFormat;
|
, theFormat;
|
||||||
|
|
||||||
// If targInfo.fmt.files exists, this theme has an explicit "files"
|
|
||||||
// section in its theme.json file.
|
|
||||||
if( targInfo.fmt.files && targInfo.fmt.files.length ) {
|
|
||||||
|
|
||||||
_log( 'Generating '.useful +
|
_log( 'Generating '.useful +
|
||||||
targInfo.fmt.outFormat.toUpperCase().useful.bold +
|
targInfo.fmt.outFormat.toUpperCase().useful.bold +
|
||||||
' resume: '.useful + PATH.relative(process.cwd(), f ).replace(/\\/g,'/').useful.bold);
|
' resume: '.useful + PATH.relative(process.cwd(), f ).useful.bold );
|
||||||
|
|
||||||
|
// If targInfo.fmt.files exists, this format is backed by a document.
|
||||||
|
// Fluent/FRESH themes are handled here.
|
||||||
|
if( targInfo.fmt.files && targInfo.fmt.files.length ) {
|
||||||
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;
|
||||||
theFormat.gen.generate( rez, f, _opts );
|
_opts.targets = finished;
|
||||||
|
return theFormat.gen.generate( rez, f, _opts );
|
||||||
}
|
}
|
||||||
// Otherwise the theme has no files section
|
|
||||||
else {
|
|
||||||
_log( 'Generating '.useful +
|
|
||||||
targInfo.fmt.outFormat.toUpperCase().useful.bold +
|
|
||||||
' resume: '.useful + PATH.relative(process.cwd(), f ).replace(/\\/g,'/').useful.bold);
|
|
||||||
|
|
||||||
theFormat = _fmts.filter(
|
// Otherwise this is either a) a JSON Resume theme or b) an ad-hoc format
|
||||||
function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0];
|
// (JSON, YML, or PNG) that every theme gets "for free".
|
||||||
|
else {
|
||||||
|
|
||||||
|
theFormat = _fmts.filter( function(fmt) {
|
||||||
|
return fmt.name === targInfo.fmt.outFormat;
|
||||||
|
})[0];
|
||||||
|
|
||||||
var outFolder = PATH.dirname( f );
|
var outFolder = PATH.dirname( f );
|
||||||
MKDIRP.sync( outFolder ); // Ensure dest folder exists;
|
MKDIRP.sync( outFolder ); // Ensure dest folder exists;
|
||||||
|
|
||||||
// TODO: refactor
|
// JSON Resume themes have a 'render' method that needs to be called
|
||||||
if( theme.render ) {
|
if( theme.render ) {
|
||||||
var COPY = require('copy');
|
var COPY = require('copy');
|
||||||
var globs = [ /*'**',*/ '*.css', '*.js', '*.png', '*.jpg', '*.gif', '*.bmp' ];
|
var globs = [ /*'**',*/ '*.css', '*.js', '*.png', '*.jpg', '*.gif', '*.bmp' ];
|
||||||
@ -151,7 +138,7 @@
|
|||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prevent JSON Resume theme .js from chattering
|
// Prevent JSON Resume theme .js from chattering (TODO: redirect IO)
|
||||||
var consoleLog = console.log;
|
var consoleLog = console.log;
|
||||||
console.log = function() { };
|
console.log = function() { };
|
||||||
|
|
||||||
@ -169,9 +156,12 @@
|
|||||||
|
|
||||||
// Save the file
|
// Save the file
|
||||||
FS.writeFileSync( f, rezHtml );
|
FS.writeFileSync( f, rezHtml );
|
||||||
|
|
||||||
|
// Return markup to the client
|
||||||
|
return rezHtml;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
theFormat.gen.generate( rez, f, _opts );
|
return theFormat.gen.generate( rez, f, _opts );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,4 +170,117 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Expand output files. For example, "foo.all" should be expanded to
|
||||||
|
["foo.html", "foo.doc", "foo.pdf", "etc"].
|
||||||
|
@param dst An array of output files as specified by the user.
|
||||||
|
@param theTheme A FRESHTheme or JRSTheme object.
|
||||||
|
*/
|
||||||
|
function expand( dst, theTheme ) {
|
||||||
|
|
||||||
|
// Add freebie formats (JSON, YAML, PNG) every theme gets...
|
||||||
|
// Add HTML-driven PNG only if the theme has an HTML format.
|
||||||
|
theTheme.formats.json = theTheme.formats.json || {
|
||||||
|
freebie: true, title: 'json', outFormat: 'json', pre: 'json',
|
||||||
|
ext: 'json', path: null, data: null
|
||||||
|
};
|
||||||
|
theTheme.formats.yml = theTheme.formats.yml || {
|
||||||
|
freebie: true, title: 'yaml', outFormat: 'yml', pre: 'yml',
|
||||||
|
ext: 'yml', path: null, data: null
|
||||||
|
};
|
||||||
|
if( theTheme.formats.html && !theTheme.formats.png ) {
|
||||||
|
theTheme.formats.png = {
|
||||||
|
freebie: true, title: 'png', outFormat: 'png',
|
||||||
|
ext: 'yml', path: null, data: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the destination collection. It's either the array of files passed
|
||||||
|
// by the user or 'out/resume.all' if no targets were specified.
|
||||||
|
var destColl = (dst && dst.length && dst) ||
|
||||||
|
[PATH.normalize('out/resume.all')];
|
||||||
|
|
||||||
|
// Assemble an array of expanded target files... (can't use map() here)
|
||||||
|
var targets = [];
|
||||||
|
destColl.forEach( function(t) {
|
||||||
|
|
||||||
|
var to = PATH.resolve(t), pa = parsePath(to),fmat = pa.extname || '.all';
|
||||||
|
|
||||||
|
var explicitFormats = _.omit( theTheme.formats, function(val, key) {
|
||||||
|
return !val.freebie;
|
||||||
|
});
|
||||||
|
var implicitFormats = _.omit( theTheme.formats, function(val) {
|
||||||
|
return val.freebie;
|
||||||
|
});
|
||||||
|
|
||||||
|
targets.push.apply(
|
||||||
|
targets, fmat === '.all' ?
|
||||||
|
Object.keys( implicitFormats ).map( function( k ) {
|
||||||
|
var z = theTheme.formats[k];
|
||||||
|
return { file: to.replace( /all$/g, z.outFormat ), fmt: z };
|
||||||
|
}) :
|
||||||
|
[{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
|
||||||
|
|
||||||
|
targets.push.apply(
|
||||||
|
targets, fmat === '.all' ?
|
||||||
|
Object.keys( explicitFormats ).map( function( k ) {
|
||||||
|
var z = theTheme.formats[k];
|
||||||
|
return { file: to.replace( /all$/g, z.outFormat ), fmt: z };
|
||||||
|
}) :
|
||||||
|
[{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Verify the specified theme name/path.
|
||||||
|
*/
|
||||||
|
function verify_theme( themeNameOrPath ) {
|
||||||
|
var tFolder = PATH.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../node_modules/fresh-themes/themes',
|
||||||
|
themeNameOrPath
|
||||||
|
);
|
||||||
|
var exists = require('path-exists').sync;
|
||||||
|
if( !exists( tFolder ) ) {
|
||||||
|
tFolder = PATH.resolve( themeNameOrPath );
|
||||||
|
if( !exists( tFolder ) ) {
|
||||||
|
throw { fluenterror: 1, data: _opts.theme };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Load the specified theme.
|
||||||
|
*/
|
||||||
|
function load_theme( tFolder ) {
|
||||||
|
|
||||||
|
// Create a FRESH or JRS theme object
|
||||||
|
var theTheme = _opts.theme.indexOf('jsonresume-theme-') > -1 ?
|
||||||
|
new JRSTheme().open(tFolder) : new FluentTheme().open( tFolder );
|
||||||
|
|
||||||
|
// Cache the theme object
|
||||||
|
_opts.themeObj = theTheme;
|
||||||
|
|
||||||
|
// Output a message TODO: core should not log
|
||||||
|
var numFormats = Object.keys(theTheme.formats).length;
|
||||||
|
_log( 'Applying '.info + theTheme.name.toUpperCase().infoBold +
|
||||||
|
(' theme (' + numFormats + ' formats)').info);
|
||||||
|
return theTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = build;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -9,59 +9,54 @@ var chai = require('chai')
|
|||||||
|
|
||||||
chai.config.includeStack = false;
|
chai.config.includeStack = false;
|
||||||
|
|
||||||
describe('jane-doe.json (FRESH)', function () {
|
function testResume(opts) {
|
||||||
|
|
||||||
var _sheet;
|
describe( opts.title + ' (FRESH)', function () {
|
||||||
|
|
||||||
it('should open without throwing an exception', function () {
|
var _sheet;
|
||||||
function tryOpen() {
|
|
||||||
_sheet = new FRESHResume().open(
|
|
||||||
'node_modules/jane-q-fullstacker/resume/jane-resume.json' );
|
|
||||||
}
|
|
||||||
tryOpen.should.not.Throw();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have one or more of each section', function() {
|
it('should open without throwing an exception', function () {
|
||||||
expect(
|
function tryOpen() {
|
||||||
//(_sheet.basics) &&
|
_sheet = new FRESHResume().open( opts.path );
|
||||||
_sheet.name && _sheet.info && _sheet.location && _sheet.contact &&
|
}
|
||||||
(_sheet.employment.history && _sheet.employment.history.length > 0) &&
|
tryOpen.should.not.Throw();
|
||||||
(_sheet.skills && _sheet.skills.list.length > 0) &&
|
});
|
||||||
(_sheet.education.history && _sheet.education.history.length > 0) &&
|
|
||||||
(_sheet.service.history && _sheet.service.history.length > 0) &&
|
|
||||||
(_sheet.writing && _sheet.writing.length > 0) &&
|
|
||||||
(_sheet.recognition && _sheet.recognition.length > 0) &&
|
|
||||||
(_sheet.samples && _sheet.samples.length > 0) &&
|
|
||||||
(_sheet.references && _sheet.references.length > 0) &&
|
|
||||||
(_sheet.interests && _sheet.interests.length > 0)
|
|
||||||
).to.equal( true );
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have a work duration of 7 years', function() {
|
it('should have one or more of each section', function() {
|
||||||
_sheet.computed.numYears.should.equal( 7 );
|
var newObj = _.pick( _sheet, opts.sections );
|
||||||
});
|
expect( Object.keys(newObj).length ).to.equal( opts.sections.length );
|
||||||
|
});
|
||||||
|
|
||||||
it('should save without throwing an exception', function(){
|
it('should have a work duration of ' + opts.duration + ' years', function() {
|
||||||
function trySave() {
|
_sheet.computed.numYears.should.equal( opts.duration );
|
||||||
_sheet.save( 'test/sandbox/jane-q-fullstacker.json' );
|
});
|
||||||
}
|
|
||||||
trySave.should.not.Throw();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be modified after saving', function() {
|
it('should save without throwing an exception', function(){
|
||||||
var savedSheet = new FRESHResume().open('test/sandbox/jane-q-fullstacker.json');
|
function trySave() {
|
||||||
_sheet.stringify().should.equal( savedSheet.stringify() );
|
_sheet.save( 'test/sandbox/' + opts.title + '.json' );
|
||||||
});
|
}
|
||||||
|
trySave.should.not.Throw();
|
||||||
|
});
|
||||||
|
|
||||||
it('should validate against the FRESH resume schema', function() {
|
it('should not be modified after saving', function() {
|
||||||
var result = _sheet.isValid();
|
var savedSheet = new FRESHResume().open('test/sandbox/' + opts.title + '.json');
|
||||||
// var schemaJson = require('fresca');
|
_sheet.stringify().should.equal( savedSheet.stringify() );
|
||||||
// var validate = validator( schemaJson, { verbose: true } );
|
});
|
||||||
// var result = validate( JSON.parse( _sheet.imp.raw ) );
|
|
||||||
result || console.log("\n\nOops, resume didn't validate. " +
|
it('should validate against the FRESH resume schema', function() {
|
||||||
"Validation errors:\n\n", _sheet.imp.validationErrors, "\n\n");
|
var result = _sheet.isValid();
|
||||||
result.should.equal( true );
|
// var schemaJson = require('fresca');
|
||||||
});
|
// var validate = validator( schemaJson, { verbose: true } );
|
||||||
|
// var result = validate( JSON.parse( _sheet.imp.raw ) );
|
||||||
|
result || console.log("\n\nOops, resume didn't validate. " +
|
||||||
|
"Validation errors:\n\n", _sheet.imp.validationErrors, "\n\n");
|
||||||
|
result.should.equal( true );
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var sects = [ 'info', 'employment', 'service', 'skills', 'education', 'writing', 'recognition', 'references' ];
|
||||||
|
testResume({ title: 'jane-q-fullstacker', path: 'node_modules/jane-q-fullstacker/resume/jane-resume.json', duration: 7, sections: sects });
|
||||||
|
testResume({ title: 'johnny-trouble-resume', path: 'node_modules/johnny-trouble-resume/src/johnny-trouble.fresh.json', duration: 3, sections: sects });
|
||||||
|
@ -12,7 +12,9 @@ var SPAWNWATCHER = require('../src/core/spawn-watch')
|
|||||||
|
|
||||||
chai.config.includeStack = false;
|
chai.config.includeStack = false;
|
||||||
|
|
||||||
describe('Testing themes', function () {
|
function genThemes( title, src, fmt ) {
|
||||||
|
|
||||||
|
describe('Testing themes against ' + title.toUpperCase() + ' resume ' + '(' + fmt + ')' , function () {
|
||||||
|
|
||||||
var _sheet;
|
var _sheet;
|
||||||
|
|
||||||
@ -29,11 +31,11 @@ describe('Testing themes', function () {
|
|||||||
|
|
||||||
function genTheme( fmt, src, themeName, themeLoc, testTitle ) {
|
function genTheme( fmt, src, themeName, themeLoc, testTitle ) {
|
||||||
themeLoc = themeLoc || themeName;
|
themeLoc = themeLoc || themeName;
|
||||||
testTitle = themeName.toUpperCase() + ' theme should generate without throwing an exception';
|
testTitle = themeName.toUpperCase() + ' theme (' + fmt + ') should generate without throwing an exception';
|
||||||
it( testTitle, function () {
|
it( testTitle, function () {
|
||||||
function tryOpen() {
|
function tryOpen() {
|
||||||
//var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
|
//var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
|
||||||
var dst = ['test/sandbox/' + fmt + '/' + themeName + '/resume.all'];
|
var dst = ['test/sandbox/' + fmt + '/' + title + '/' + themeName + '/resume.all'];
|
||||||
var opts = {
|
var opts = {
|
||||||
theme: themeLoc,
|
theme: themeLoc,
|
||||||
format: fmt,
|
format: fmt,
|
||||||
@ -46,28 +48,21 @@ describe('Testing themes', function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
|
genTheme(fmt, src, 'hello-world');
|
||||||
genTheme('FRESH', src, 'hello-world');
|
genTheme(fmt, src, 'compact');
|
||||||
genTheme('FRESH', src, 'compact');
|
genTheme(fmt, src, 'modern');
|
||||||
genTheme('FRESH', src, 'modern');
|
genTheme(fmt, src, 'minimist');
|
||||||
genTheme('FRESH', src, 'minimist');
|
genTheme(fmt, src, 'awesome');
|
||||||
genTheme('FRESH', src, 'awesome');
|
genTheme(fmt, src, 'positive');
|
||||||
genTheme('FRESH', src, 'positive');
|
genTheme(fmt, src, 'jsonresume-theme-boilerplate', 'node_modules/jsonresume-theme-boilerplate' );
|
||||||
genTheme('FRESH', src, 'jsonresume-theme-boilerplate', 'node_modules/jsonresume-theme-boilerplate' );
|
genTheme(fmt, src, 'jsonresume-theme-sceptile', 'node_modules/jsonresume-theme-sceptile' );
|
||||||
genTheme('FRESH', src, 'jsonresume-theme-sceptile', 'node_modules/jsonresume-theme-sceptile' );
|
genTheme(fmt, src, 'jsonresume-theme-modern', 'node_modules/jsonresume-theme-modern' );
|
||||||
genTheme('FRESH', src, 'jsonresume-theme-modern', 'node_modules/jsonresume-theme-modern' );
|
genTheme(fmt, src, 'jsonresume-theme-classy', 'node_modules/jsonresume-theme-classy' );
|
||||||
genTheme('FRESH', src, 'jsonresume-theme-classy', 'node_modules/jsonresume-theme-classy' );
|
|
||||||
|
|
||||||
src = ['test/resumes/jrs-0.0.0/richard-hendriks.json'];
|
});
|
||||||
genTheme('JRS', src, 'hello-world');
|
|
||||||
genTheme('JRS', src, 'compact');
|
|
||||||
genTheme('JRS', src, 'modern');
|
|
||||||
genTheme('JRS', src, 'minimist');
|
|
||||||
genTheme('JRS', src, 'awesome');
|
|
||||||
genTheme('JRS', src, 'positive');
|
|
||||||
genTheme('JRS', src, 'jsonresume-theme-boilerplate', 'node_modules/jsonresume-theme-boilerplate' );
|
|
||||||
genTheme('JRS', src, 'jsonresume-theme-sceptile', 'node_modules/jsonresume-theme-sceptile' );
|
|
||||||
genTheme('JRS', src, 'jsonresume-theme-modern', 'node_modules/jsonresume-theme-modern' );
|
|
||||||
genTheme('JRS', src, 'jsonresume-theme-classy', 'node_modules/jsonresume-theme-classy' );
|
|
||||||
|
|
||||||
});
|
}
|
||||||
|
|
||||||
|
genThemes( 'jane-q-fullstacker', ['node_modules/jane-q-fullstacker/resume/jane-resume.json'], 'FRESH' );
|
||||||
|
genThemes( 'johnny-trouble', ['node_modules/johnny-trouble-resume/src/johnny-trouble.fresh.json'], 'FRESH' );
|
||||||
|
genThemes( 'richard-hendriks', ['test/resumes/jrs-0.0.0/richard-hendriks.json'], 'JRS' );
|
||||||
|
Loading…
Reference in New Issue
Block a user