1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2025-05-10 07:47:07 +01:00

Rename src/gen --> src/generators.

This commit is contained in:
hacksalot
2016-01-08 10:02:47 -05:00
parent 95df8e5af4
commit 8d24087faa
15 changed files with 19 additions and 19 deletions

View File

@ -0,0 +1,39 @@
/**
Definition of the BaseGenerator class.
@module base-generator.js
@license MIT. See LICENSE.md for details.
*/
(function() {
// Use J. Resig's nifty class implementation
var Class = require( '../utils/class' );
/**
The BaseGenerator class is the root of the generator hierarchy. Functionality
common to ALL generators lives here.
*/
var BaseGenerator = module.exports = Class.extend({
/**
Base-class initialize.
*/
init: function( outputFormat ) {
this.format = outputFormat;
},
/**
Status codes.
*/
codes: require('../core/status-codes'),
/**
Generator options.
*/
opts: {
}
});
}());

View File

@ -0,0 +1,31 @@
/**
Definition of the HTMLGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module html-generator.js
*/
(function() {
var TemplateGenerator = require('./template-generator')
, FS = require('fs-extra')
, HTML = require( 'html' )
, PATH = require('path');
var HtmlGenerator = module.exports = TemplateGenerator.extend({
init: function() {
this._super( 'html' );
},
/**
Copy satellite CSS files to the destination and optionally pretty-print
the HTML resume prior to saving.
*/
onBeforeSave: function( info ) {
return this.opts.prettify ?
HTML.prettyPrint( info.mk, this.opts.prettify ) : info.mk;
}
});
}());

View File

@ -0,0 +1,139 @@
/**
Definition of the HtmlPdfCLIGenerator class.
@module html-pdf-generator.js
@license MIT. See LICENSE.md for details.
*/
(function() {
var TemplateGenerator = require('./template-generator')
, FS = require('fs-extra')
, HTML = require( 'html' )
, PATH = require('path')
, SLASH = require('slash');
/**
An HTML-driven PDF resume generator for HackMyResume. Talks to Phantom,
wkhtmltopdf, and other PDF engines over a CLI (command-line interface).
If an engine isn't installed for a particular platform, error out gracefully.
*/
var HtmlPdfCLIGenerator = module.exports = TemplateGenerator.extend({
init: function() {
this._super( 'pdf', 'html' );
},
/**
Generate the binary PDF.
*/
onBeforeSave: function( info ) {
try {
var safe_eng = info.opts.pdf || 'wkhtmltopdf';
engines[ safe_eng ].call( this, info.mk, info.outputFile );
return null; // halt further processing
}
catch(ex) {
// { [Error: write EPIPE] code: 'EPIPE', errno: 'EPIPE', ... }
// { [Error: ENOENT] }
throw ( ex.inner && ex.inner.code === 'ENOENT' ) ?
{ fluenterror: this.codes.notOnPath, engine: ex.cmd } :
{ fluenterror: this.codes.pdfGeneration, inner: ex.inner };
}
}
});
// TODO: Move each engine to a separate module
var engines = {
/**
Generate a PDF from HTML using wkhtmltopdf's CLI interface.
Spawns a child process with `wkhtmltopdf <source> <target>`. wkhtmltopdf
must be installed and path-accessible.
TODO: If HTML generation has run, reuse that output
TODO: Local web server to ease wkhtmltopdf rendering
*/
wkhtmltopdf: function(markup, fOut) {
// Save the markup to a temporary file
var tempFile = fOut.replace(/\.pdf$/i, '.pdf.html');
FS.writeFileSync( tempFile, markup, 'utf8' );
var spawn = require('child_process').spawnSync;
var info = spawn('wkhtmltopdf', [
tempFile, fOut
]);
if( info.error ) {
throw {
cmd: 'wkhtmltopdf',
inner: info.error
};
}
// child.stdout.on('data', function(chunk) {
// // output will be here in chunks
// });
// or if you want to send output elsewhere
//child.stdout.pipe(dest);
},
/**
Generate a PDF from HTML using Phantom's CLI interface.
Spawns a child process with `phantomjs <script> <source> <target>`. Phantom
must be installed and path-accessible.
TODO: If HTML generation has run, reuse that output
TODO: Local web server to ease Phantom rendering
*/
phantom: function( markup, fOut ) {
// Save the markup to a temporary file
var tempFile = fOut.replace(/\.pdf$/i, '.pdf.html');
FS.writeFileSync( tempFile, markup, 'utf8' );
var scriptPath = SLASH( PATH.relative( process.cwd(),
PATH.resolve( __dirname, '../utils/rasterize.js' ) ) );
var sourcePath = SLASH( PATH.relative( process.cwd(), tempFile) );
var destPath = SLASH( PATH.relative( process.cwd(), fOut) );
var spawn = require('child_process').spawnSync;
var info = spawn('phantomjs', [ scriptPath, sourcePath, destPath ]);
if( info.error ) {
throw {
cmd: 'phantomjs',
inner: info.error
};
}
// child.stdout.on('data', function(chunk) {
// // output will be here in chunks
// });
//
// // or if you want to send output elsewhere
// child.stdout.pipe(dest);
}
};
}());

View File

@ -0,0 +1,66 @@
/**
Definition of the HtmlPngGenerator class.
@license MIT. See LICENSE.MD for details.
@module html-png-generator.js
*/
(function() {
var TemplateGenerator = require('./template-generator')
, FS = require('fs-extra')
, HTML = require( 'html' );
/**
An HTML-based PNG resume generator for HackMyResume.
*/
var HtmlPngGenerator = module.exports = TemplateGenerator.extend({
init: function() {
this._super( 'png', 'html' );
},
invoke: function( rez, themeMarkup, cssInfo, opts ) {
// TODO: Not currently called or callable.
},
generate: function( rez, f, opts ) {
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 PNG from HTML.
*/
function png( markup, fOut ) {
// TODO: Which Webshot syntax?
// require('webshot')( markup , { encoding: 'binary', siteType: 'html' } )
// .pipe( FS.createWriteStream( fOut ) );
require('webshot')( markup , fOut, { siteType: 'html' }, function(err) { } );
}
}());

View File

@ -0,0 +1,36 @@
/**
Definition of the JsonGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module json-generator.js
*/
var BaseGenerator = require('./base-generator');
var FS = require('fs');
var _ = require('underscore');
/**
The JsonGenerator generates a JSON resume directly.
*/
var JsonGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'json' );
},
invoke: function( rez ) {
// TODO: merge with FCVD
function replacer( key,value ) { // Exclude these keys from stringification
return _.some(['imp', 'warnings', 'computed', 'filt', 'ctrl', 'index',
'safeStartDate', 'safeEndDate', 'safeDate', 'safeReleaseDate', 'result',
'isModified', 'htmlPreview', 'safe' ],
function( val ) { return key.trim() === val; }
) ? undefined : value;
}
return JSON.stringify( rez, replacer, 2 );
},
generate: function( rez, f ) {
FS.writeFileSync( f, this.invoke(rez), 'utf8' );
}
});

View File

@ -0,0 +1,37 @@
/**
Definition of the JsonYamlGenerator class.
@module json-yaml-generator.js
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
*/
(function() {
var BaseGenerator = require('./base-generator');
var FS = require('fs');
var YAML = require('yamljs');
/**
JsonYamlGenerator takes a JSON resume object and translates it directly to
JSON without a template, producing an equivalent YAML-formatted resume. See
also YamlGenerator (yaml-generator.js).
*/
var JsonYamlGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'yml' );
},
invoke: function( rez, themeMarkup, cssInfo, opts ) {
return YAML.stringify( JSON.parse( rez.stringify() ), Infinity, 2 );
},
generate: function( rez, f, opts ) {
var data = YAML.stringify( JSON.parse( rez.stringify() ), Infinity, 2 );
FS.writeFileSync( f, data, 'utf8' );
}
});
}());

View File

@ -0,0 +1,18 @@
/**
Definition of the LaTeXGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module latex-generator.js
*/
var TemplateGenerator = require('./template-generator');
/**
LaTeXGenerator generates a LaTeX resume via TemplateGenerator.
*/
var LaTeXGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'latex', 'tex' );
}
});

View File

@ -0,0 +1,18 @@
/**
Definition of the MarkdownGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module markdown-generator.js
*/
var TemplateGenerator = require('./template-generator');
/**
MarkdownGenerator generates a Markdown-formatted resume via TemplateGenerator.
*/
var MarkdownGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'md', 'txt' );
}
});

View File

@ -0,0 +1,323 @@
/**
Definition of the TemplateGenerator class. TODO: Refactor
@license MIT. See LICENSE.md for details.
@module template-generator.js
*/
(function() {
var FS = require( 'fs-extra' )
, _ = require( 'underscore' )
, MD = require( 'marked' )
, XML = require( 'xml-escape' )
, PATH = require('path')
, parsePath = require('parse-filepath')
, MKDIRP = require('mkdirp')
, BaseGenerator = require( './base-generator' )
, EXTEND = require('../utils/extend')
, FRESHTheme = require('../core/fresh-theme')
, JRSTheme = require('../core/jrs-theme');
// Default options.
var _defaultOpts = {
engine: 'underscore',
keepBreaks: true,
freezeBreaks: false,
nSym: '&newl;', // newline entity
rSym: '&retn;', // return entity
template: {
interpolate: /\{\{(.+?)\}\}/g,
escape: /\{\{\=(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g,
comment: /\{\#(.+?)\#\}/g
},
filters: {
out: function( txt ) { return txt; },
raw: function( txt ) { return txt; },
xml: function( txt ) { return XML(txt); },
md: function( txt ) { return MD( txt || '' ); },
mdin: function( txt ) {
return MD(txt || '' ).replace(/^\s*<p>|<\/p>\s*$/gi, '');
},
lower: function( txt ) { return txt.toLowerCase(); },
link: function( name, url ) { return url ?
'<a href="' + url + '">' + name + '</a>' : name; }
},
prettify: { // ← See https://github.com/beautify-web/js-beautify#options
indent_size: 2,
unformatted: ['em','strong','a'],
max_char: 80, // ← See lib/html.js in above-linked repo
//wrap_line_length: 120, <-- Don't use this
}
};
/**
TemplateGenerator performs resume generation via local Handlebar or Underscore
style template expansion and is appropriate for text-based formats like HTML,
plain text, and XML versions of Microsoft Word, Excel, and OpenOffice.
@class TemplateGenerator
*/
var TemplateGenerator = module.exports = BaseGenerator.extend({
init: function( outputFormat, templateFormat, cssFile ){
this._super( outputFormat );
this.tplFormat = templateFormat || outputFormat;
},
/**
String-based template generation method.
@method invoke
@param rez A FreshResume object.
@param opts Generator options.
@returns An array of objects representing the generated output files. Each
object has this format:
{
files: [ { info: { }, data: [ ] }, { ... } ],
themeInfo: { }
}
*/
invoke: function( rez, opts ) {
// Carry over options
this.opts = EXTEND( true, { }, _defaultOpts, opts );
// Load the theme
var themeInfo = themeFromMoniker.call( this );
var theme = themeInfo.theme;
var tFolder = themeInfo.folder;
var tplFolder = PATH.join( tFolder, 'src' );
var curFmt = theme.getFormat( this.format );
var that = this;
// "Generate": process individual files within the theme
return {
files: curFmt.files.map( function( tplInfo ) {
return {
info: tplInfo,
data: tplInfo.action === 'transform' ?
transform.call( that, rez, tplInfo, theme ) : undefined
};
}).filter(function(item){ return item !== null; }),
themeInfo: themeInfo
};
},
/**
File-based template generation method.
@method generate
@param rez A FreshResume object.
@param f Full path to the output resume file to generate.
@param opts Generator options.
*/
generate: function( rez, f, opts ) {
// Call the generation method
var genInfo = this.invoke( rez, opts );
// Carry over options
this.opts = EXTEND( true, { }, _defaultOpts, opts );
// Load the theme
var themeInfo = genInfo.themeInfo;
var theme = themeInfo.theme;
var tFolder = themeInfo.folder;
var tplFolder = PATH.join( tFolder, 'src' );
var outFolder = parsePath(f).dirname;
var curFmt = theme.getFormat( this.format );
var that = this;
// "Generate": process individual files within the theme
genInfo.files.forEach(function( file ){
var thisFilePath;
if( theme.engine === 'jrs' ) {
file.info.orgPath = '';
}
if( file.info.action === 'transform' ) {
thisFilePath = PATH.join( outFolder, file.info.orgPath );
try {
if( that.onBeforeSave ) {
file.data = that.onBeforeSave({
theme: theme,
outputFile: (file.info.major ? f : thisFilePath),
mk: file.data,
opts: that.opts
});
if( !file.data ) return; // PDF etc
}
var fileName = file.info.major ? f : thisFilePath;
MKDIRP.sync( PATH.dirname( fileName ) );
FS.writeFileSync( fileName, file.data,
{ encoding: 'utf8', flags: 'w' } );
that.onAfterSave && that.onAfterSave(
{ outputFile: fileName, mk: file.data, opts: that.opts } );
}
catch(ex) {
require('../core/error-handler').err(ex, false);
}
}
else if( file.info.action === null/* && theme.explicit*/ ) {
thisFilePath = PATH.join( outFolder, file.info.orgPath );
try {
MKDIRP.sync( PATH.dirname(thisFilePath) );
FS.copySync( file.info.path, thisFilePath );
}
catch(ex) {
ex.showStack = true;
require('../core/error-handler').err( ex );
}
}
});
// Some themes require a symlink structure. If so, create it.
if( curFmt.symLinks ) {
Object.keys( curFmt.symLinks ).forEach( function(loc) {
var absLoc = PATH.join(outFolder, loc);
var absTarg = PATH.join(PATH.dirname(absLoc), curFmt.symLinks[loc]);
// 'file', 'dir', or 'junction' (Windows only)
var type = parsePath( absLoc ).extname ? 'file' : 'junction';
FS.symlinkSync( absTarg, absLoc, type);
});
}
return genInfo;
},
/**
Perform a single resume JSON-to-DEST resume transformation.
@param json A FRESH or JRS resume object.
@param jst The stringified template data
@param format The format name, such as "html" or "latex"
@param cssInfo Needs to be refactored.
@param opts Options and passthrough data.
*/
single: function( json, jst, format, cssInfo, opts, theme ) {
this.opts.freezeBreaks && ( jst = freeze(jst) );
var eng = require( '../renderers/' + theme.engine + '-generator' );
var result = eng.generate( json, jst, format, cssInfo, opts, theme );
this.opts.freezeBreaks && ( result = unfreeze(result) );
return result;
}
});
/**
Export the TemplateGenerator function/ctor.
*/
module.exports = TemplateGenerator;
/**
Given a theme title, load the corresponding theme.
*/
function themeFromMoniker() {
// Verify the specified theme name/path
var tFolder = PATH.join(
parsePath( require.resolve('fresh-themes') ).dirname,
'/themes/',
this.opts.theme
);
var t;
if( this.opts.theme.startsWith('jsonresume-theme-') ) {
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
return {
theme: t,
folder: tFolder
};
}
function transform( rez, tplInfo, theme ) {
try {
var cssInfo = {
file: tplInfo.css ? tplInfo.cssPath : null,
data: tplInfo.css || null
};
return this.single( rez, tplInfo.data, this.format, cssInfo, this.opts,
theme );
}
catch(ex) {
ex.showStack = true;
require('../core/error-handler').err( ex );
}
}
/**
Freeze newlines for protection against errant JST parsers.
*/
function freeze( markup ) {
return markup
.replace( _reg.regN, _defaultOpts.nSym )
.replace( _reg.regR, _defaultOpts.rSym );
}
/**
Unfreeze newlines when the coast is clear.
*/
function unfreeze( markup ) {
return markup
.replace( _reg.regSymR, '\r' )
.replace( _reg.regSymN, '\n' );
}
/**
Regexes for linebreak preservation.
*/
var _reg = {
regN: new RegExp( '\n', 'g' ),
regR: new RegExp( '\r', 'g' ),
regSymN: new RegExp( _defaultOpts.nSym, 'g' ),
regSymR: new RegExp( _defaultOpts.rSym, 'g' )
};
}());

View File

@ -0,0 +1,20 @@
/**
Definition of the TextGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module text-generator.js
*/
var TemplateGenerator = require('./template-generator');
/**
The TextGenerator generates a plain-text resume via the TemplateGenerator.
*/
var TextGenerator = TemplateGenerator.extend({
init: function(){
this._super( 'txt' );
},
});
module.exports = TextGenerator;

View File

@ -0,0 +1,19 @@
/**
Definition of the WordGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module word-generator.js
*/
(function() {
var TemplateGenerator = require('./template-generator');
var WordGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'doc', 'xml' );
}
});
}());

View File

@ -0,0 +1,18 @@
/**
Definition of the XMLGenerator class.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
@module xml-generator.js
*/
var BaseGenerator = require('./base-generator');
/**
The XmlGenerator generates an XML resume via the TemplateGenerator.
*/
var XMLGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'xml' );
},
});

View File

@ -0,0 +1,24 @@
/**
Definition of the YAMLGenerator class.
@module yaml-generator.js
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
*/
(function() {
var TemplateGenerator = require('./template-generator');
/**
YamlGenerator generates a YAML-formatted resume via TemplateGenerator.
*/
var YAMLGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'yml', 'yml' );
}
});
}());