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

Finish HackMyCore reshaping.

Reintroduce HackMyCore, dropping the interim submodule, and reorganize
and improve tests.
This commit is contained in:
hacksalot
2016-01-29 15:23:57 -05:00
parent e9971eb882
commit 0f65e4c9f3
130 changed files with 5384 additions and 337 deletions

33
dist/generators/base-generator.js vendored Normal file
View File

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

42
dist/generators/html-generator.js vendored Normal file
View File

@ -0,0 +1,42 @@
/**
Definition of the HTMLGenerator class.
@license MIT. See LICENSE.md for details.
@module html-generator.js
*/
(function() {
var FS, HTML, HtmlGenerator, PATH, TemplateGenerator;
TemplateGenerator = require('./template-generator');
FS = require('fs-extra');
HTML = require('html');
PATH = require('path');
require('string.prototype.endswith');
HtmlGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('html');
},
/**
Copy satellite CSS files to the destination and optionally pretty-print
the HTML resume prior to saving.
*/
onBeforeSave: function(info) {
if (info.outputFile.endsWith('.css')) {
return info.mk;
}
if (this.opts.prettify) {
return HTML.prettyPrint(info.mk, this.opts.prettify);
} else {
return info.mk;
}
}
});
}).call(this);

View File

@ -0,0 +1,98 @@
/**
Definition of the HtmlPdfCLIGenerator class.
@module html-pdf-generator.js
@license MIT. See LICENSE.md for details.
*/
(function() {
var FS, HTML, HtmlPdfCLIGenerator, PATH, SLASH, SPAWN, TemplateGenerator, engines;
TemplateGenerator = require('./template-generator');
FS = require('fs-extra');
HTML = require('html');
PATH = require('path');
SPAWN = require('../utils/safe-spawn');
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.
*/
HtmlPdfCLIGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('pdf', 'html');
},
/** Generate the binary PDF. */
onBeforeSave: function(info) {
var ex, safe_eng;
try {
safe_eng = info.opts.pdf || 'wkhtmltopdf';
if (safe_eng !== 'none') {
engines[safe_eng].call(this, info.mk, info.outputFile);
return null;
}
} catch (_error) {
ex = _error;
if (ex.inner && ex.inner.code === 'ENOENT') {
throw {
fluenterror: this.codes.notOnPath,
inner: ex.inner,
engine: ex.cmd,
stack: ex.inner && ex.inner.stack
};
} else {
throw {
fluenterror: this.codes.pdfGeneration,
inner: ex,
stack: ex.stack
};
}
}
}
});
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) {
var info, tempFile;
tempFile = fOut.replace(/\.pdf$/i, '.pdf.html');
FS.writeFileSync(tempFile, markup, 'utf8');
return info = SPAWN('wkhtmltopdf', [tempFile, fOut]);
},
/**
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) {
var destPath, info, scriptPath, sourcePath, tempFile;
tempFile = fOut.replace(/\.pdf$/i, '.pdf.html');
FS.writeFileSync(tempFile, markup, 'utf8');
scriptPath = SLASH(PATH.relative(process.cwd(), PATH.resolve(__dirname, '../utils/rasterize.js')));
sourcePath = SLASH(PATH.relative(process.cwd(), tempFile));
destPath = SLASH(PATH.relative(process.cwd(), fOut));
return info = SPAWN('phantomjs', [scriptPath, sourcePath, destPath]);
}
};
}).call(this);

64
dist/generators/html-png-generator.js vendored Normal file
View File

@ -0,0 +1,64 @@
/**
Definition of the HtmlPngGenerator class.
@license MIT. See LICENSE.MD for details.
@module html-png-generator.js
*/
(function() {
var FS, HTML, HtmlPngGenerator, PATH, SLASH, SPAWN, TemplateGenerator, phantom;
TemplateGenerator = require('./template-generator');
FS = require('fs-extra');
HTML = require('html');
SLASH = require('slash');
SPAWN = require('../utils/safe-spawn');
PATH = require('path');
/**
An HTML-based PNG resume generator for HackMyResume.
*/
HtmlPngGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('png', 'html');
},
invoke: function(rez, themeMarkup, cssInfo, opts) {},
generate: function(rez, f, opts) {
var htmlFile, htmlResults;
htmlResults = opts.targets.filter(function(t) {
return t.fmt.outFormat === 'html';
});
htmlFile = htmlResults[0].final.files.filter(function(fl) {
return fl.info.ext === 'html';
});
phantom(htmlFile[0].data, f);
}
});
/**
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) {
var destPath, info, scriptPath, sourcePath, tempFile;
tempFile = fOut.replace(/\.png$/i, '.png.html');
FS.writeFileSync(tempFile, markup, 'utf8');
scriptPath = SLASH(PATH.relative(process.cwd(), PATH.resolve(__dirname, '../utils/rasterize.js')));
sourcePath = SLASH(PATH.relative(process.cwd(), tempFile));
destPath = SLASH(PATH.relative(process.cwd(), fOut));
info = SPAWN('phantomjs', [scriptPath, sourcePath, destPath]);
};
}).call(this);

45
dist/generators/json-generator.js vendored Normal file
View File

@ -0,0 +1,45 @@
/**
Definition of the JsonGenerator class.
@license MIT. See LICENSE.md for details.
@module generators/json-generator
*/
(function() {
var BaseGenerator, FS, JsonGenerator, _;
BaseGenerator = require('./base-generator');
FS = require('fs');
_ = require('underscore');
/**
The JsonGenerator generates a JSON resume directly.
*/
JsonGenerator = module.exports = BaseGenerator.extend({
init: function() {
return this._super('json');
},
keys: ['imp', 'warnings', 'computed', 'filt', 'ctrl', 'index', 'safeStartDate', 'safeEndDate', 'safeDate', 'safeReleaseDate', 'result', 'isModified', 'htmlPreview', 'safe'],
invoke: function(rez) {
var replacer;
replacer = function(key, value) {
if (_.some(this.keys, function(val) {
return key.trim() === val;
})) {
return void 0;
} else {
return value;
}
};
return JSON.stringify(rez, replacer, 2);
},
generate: function(rez, f) {
FS.writeFileSync(f, this.invoke(rez), 'utf8');
}
});
}).call(this);

38
dist/generators/json-yaml-generator.js vendored Normal file
View File

@ -0,0 +1,38 @@
/**
Definition of the JsonYamlGenerator class.
@module json-yaml-generator.js
@license MIT. See LICENSE.md for details.
*/
(function() {
var BaseGenerator, FS, JsonYamlGenerator, YAML;
BaseGenerator = require('./base-generator');
FS = require('fs');
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).
*/
JsonYamlGenerator = module.exports = BaseGenerator.extend({
init: function() {
return 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;
data = YAML.stringify(JSON.parse(rez.stringify()), Infinity, 2);
return FS.writeFileSync(f, data, 'utf8');
}
});
}).call(this);

24
dist/generators/latex-generator.js vendored Normal file
View File

@ -0,0 +1,24 @@
/**
Definition of the LaTeXGenerator class.
@license MIT. See LICENSE.md for details.
@module generators/latex-generator
*/
(function() {
var LaTeXGenerator, TemplateGenerator;
TemplateGenerator = require('./template-generator');
/**
LaTeXGenerator generates a LaTeX resume via TemplateGenerator.
*/
LaTeXGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('latex', 'tex');
}
});
}).call(this);

24
dist/generators/markdown-generator.js vendored Normal file
View File

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

243
dist/generators/template-generator.js vendored Normal file
View File

@ -0,0 +1,243 @@
/**
Definition of the TemplateGenerator class. TODO: Refactor
@license MIT. See LICENSE.md for details.
@module template-generator.js
*/
(function() {
var BaseGenerator, EXTEND, FRESHTheme, FS, JRSTheme, MD, MKDIRP, PATH, TemplateGenerator, XML, _, _defaultOpts, _reg, freeze, parsePath, unfreeze;
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('extend');
FRESHTheme = require('../core/fresh-theme');
JRSTheme = require('../core/jrs-theme');
/**
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
*/
TemplateGenerator = module.exports = BaseGenerator.extend({
/** Constructor. Set the output format and template format for this
generator. Will usually be called by a derived generator such as
HTMLGenerator or MarkdownGenerator.
*/
init: function(outputFormat, templateFormat, cssFile) {
this._super(outputFormat);
this.tplFormat = templateFormat || outputFormat;
},
/** Generate a resume using string-based inputs and outputs without touching
the filesystem.
@method invoke
@param rez A FreshResume object.
@param opts Generator options.
@returns {Array} An array of objects representing the generated output
files.
*/
invoke: function(rez, opts) {
var curFmt, results;
opts = opts ? (this.opts = EXTEND(true, {}, _defaultOpts, opts)) : this.opts;
curFmt = opts.themeObj.getFormat(this.format);
curFmt.files = _.sortBy(curFmt.files, function(fi) {
return fi.ext !== 'css';
});
results = curFmt.files.map(function(tplInfo, idx) {
var trx;
trx = this.single(rez, tplInfo.data, this.format, opts, opts.themeObj, curFmt);
if (tplInfo.ext === 'css') {
curFmt.files[idx].data = trx;
} else {
tplInfo.ext === 'html';
}
return {
info: tplInfo,
data: trx
};
}, this);
return {
files: results
};
},
/** Generate a resume using file-based inputs and outputs. Requires access
to the local filesystem.
@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) {
var curFmt, genInfo, outFolder;
this.opts = EXTEND(true, {}, _defaultOpts, opts);
genInfo = this.invoke(rez, null);
outFolder = parsePath(f).dirname;
curFmt = opts.themeObj.getFormat(this.format);
genInfo.files.forEach(function(file) {
var fileName, thisFilePath;
file.info.orgPath = file.info.orgPath || '';
thisFilePath = PATH.join(outFolder, file.info.orgPath);
if (this.onBeforeSave) {
file.data = this.onBeforeSave({
theme: opts.themeObj,
outputFile: file.info.major ? f : thisFilePath,
mk: file.data,
opts: this.opts
});
if (!file.data) {
return;
}
}
fileName = file.info.major ? f : thisFilePath;
MKDIRP.sync(PATH.dirname(fileName));
FS.writeFileSync(fileName, file.data, {
encoding: 'utf8',
flags: 'w'
});
if (this.onAfterSave) {
return this.onAfterSave({
outputFile: fileName,
mk: file.data,
opts: this.opts
});
}
}, this);
if (curFmt.symLinks) {
Object.keys(curFmt.symLinks).forEach(function(loc) {
var absLoc, absTarg, ref, type;
absLoc = PATH.join(outFolder, loc);
absTarg = PATH.join(PATH.dirname(absLoc), curFmt.symLinks[loc]);
type = (ref = parsePath(absLoc).extname) != null ? ref : {
'file': 'junction'
};
return FS.symlinkSync(absTarg, absLoc, type);
});
}
return genInfo;
},
/** Perform a single resume resume transformation using string-based inputs
and outputs without touching the local file system.
@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, opts, theme, curFmt) {
var eng, result;
if (this.opts.freezeBreaks) {
jst = freeze(jst);
}
eng = require('../renderers/' + theme.engine + '-generator');
result = eng.generate(json, jst, format, curFmt, opts, theme);
if (this.opts.freezeBreaks) {
result = unfreeze(result);
}
return result;
}
});
/** Export the TemplateGenerator function/ctor. */
module.exports = TemplateGenerator;
/** Freeze newlines for protection against errant JST parsers. */
freeze = function(markup) {
markup.replace(_reg.regN, _defaultOpts.nSym);
return markup.replace(_reg.regR, _defaultOpts.rSym);
};
/** Unfreeze newlines when the coast is clear. */
unfreeze = function(markup) {
markup.replace(_reg.regSymR, '\r');
return markup.replace(_reg.regSymN, '\n');
};
/** Default template generator options. */
_defaultOpts = {
engine: 'underscore',
keepBreaks: true,
freezeBreaks: false,
nSym: '&newl;',
rSym: '&retn;',
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) {
if (url) {
return '<a href="' + url + '">' + name + '</a>';
} else {
return name;
}
}
},
prettify: {
indent_size: 2,
unformatted: ['em', 'strong', 'a'],
max_char: 80
}
};
/** Regexes for linebreak preservation. */
_reg = {
regN: new RegExp('\n', 'g'),
regR: new RegExp('\r', 'g'),
regSymN: new RegExp(_defaultOpts.nSym, 'g'),
regSymR: new RegExp(_defaultOpts.rSym, 'g')
};
}).call(this);

24
dist/generators/text-generator.js vendored Normal file
View File

@ -0,0 +1,24 @@
/**
Definition of the TextGenerator class.
@license MIT. See LICENSE.md for details.
@module text-generator.js
*/
(function() {
var TemplateGenerator, TextGenerator;
TemplateGenerator = require('./template-generator');
/**
The TextGenerator generates a plain-text resume via the TemplateGenerator.
*/
TextGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('txt');
}
});
}).call(this);

19
dist/generators/word-generator.js vendored Normal file
View File

@ -0,0 +1,19 @@
/*
Definition of the WordGenerator class.
@license MIT. See LICENSE.md for details.
@module generators/word-generator
*/
(function() {
var TemplateGenerator, WordGenerator;
TemplateGenerator = require('./template-generator');
WordGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('doc', 'xml');
}
});
}).call(this);

24
dist/generators/xml-generator.js vendored Normal file
View File

@ -0,0 +1,24 @@
/**
Definition of the XMLGenerator class.
@license MIT. See LICENSE.md for details.
@module generatprs/xml-generator
*/
(function() {
var BaseGenerator, XMLGenerator;
BaseGenerator = require('./base-generator');
/**
The XmlGenerator generates an XML resume via the TemplateGenerator.
*/
XMLGenerator = module.exports = BaseGenerator.extend({
init: function() {
return this._super('xml');
}
});
}).call(this);

24
dist/generators/yaml-generator.js vendored Normal file
View File

@ -0,0 +1,24 @@
/**
Definition of the YAMLGenerator class.
@module yaml-generator.js
@license MIT. See LICENSE.md for details.
*/
(function() {
var TemplateGenerator, YAMLGenerator;
TemplateGenerator = require('./template-generator');
/**
YamlGenerator generates a YAML-formatted resume via TemplateGenerator.
*/
YAMLGenerator = module.exports = TemplateGenerator.extend({
init: function() {
return this._super('yml', 'yml');
}
});
}).call(this);