1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2025-04-19 14:20:25 +01:00
HackMyResume/src/generators/template-generator.coffee
2016-02-02 13:38:12 -05:00

200 lines
6.2 KiB
CoffeeScript

###*
Definition of the TemplateGenerator class. TODO: Refactor
@module generators/template-generator
@license MIT. See LICENSE.md for details.
###
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
###
module.exports = class TemplateGenerator extends BaseGenerator
###* Constructor. Set the output format and template format for this
generator. Will usually be called by a derived generator such as
HTMLGenerator or MarkdownGenerator. ###
constructor: ( outputFormat, templateFormat, cssFile ) ->
super outputFormat
@tplFormat = templateFormat || outputFormat
return
###* 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: ( rez, opts ) ->
opts =
if opts
then (this.opts = EXTEND( true, { }, _defaultOpts, opts ))
else this.opts
# Sort such that CSS files are processed before others
curFmt = opts.themeObj.getFormat( this.format )
curFmt.files = _.sortBy curFmt.files, (fi) -> fi.ext != 'css'
# Run the transformation!
results = curFmt.files.map( ( tplInfo, idx ) ->
trx = @.single rez, tplInfo.data, this.format, opts, opts.themeObj, curFmt
if tplInfo.ext == 'css'
curFmt.files[idx].data = trx
else tplInfo.ext == 'html'
#tplInfo.css contains the CSS data loaded by theme
#tplInfo.cssPath contains the absolute path to the source CSS File
return info: tplInfo, data: trx
, @)
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: ( rez, f, opts ) ->
# Prepare
this.opts = EXTEND( true, { }, _defaultOpts, opts );
# Call the string-based generation method to perform the generation.
genInfo = this.invoke( rez, null )
outFolder = parsePath( f ).dirname
curFmt = opts.themeObj.getFormat( this.format )
# Process individual files within this format. For example, the HTML
# output format for a theme may have multiple HTML files, CSS files,
# etc. Process them here.
genInfo.files.forEach(( file ) ->
# Pre-processing
file.info.orgPath = file.info.orgPath || '' # <-- For JRS themes
thisFilePath = PATH.join( outFolder, file.info.orgPath )
if this.onBeforeSave
file.data = this.onBeforeSave
theme: opts.themeObj
outputFile: if file.info.major then f else thisFilePath
mk: file.data
opts: this.opts
if !file.data
return # PDF etc
# Write the file
fileName = if file.info.major then f else thisFilePath
MKDIRP.sync PATH.dirname( fileName )
FS.writeFileSync fileName, file.data, { encoding: 'utf8', flags: 'w' }
# Post-processing
if @onAfterSave
@onAfterSave( outputFile: fileName, mk: file.data, opts: this.opts )
, @)
# Some themes require a symlink structure. If so, create it.
if curFmt.symLinks
Object.keys( curFmt.symLinks ).forEach (loc) ->
absLoc = PATH.join outFolder, loc
absTarg = PATH.join PATH.dirname(absLoc), curFmt.symLinks[loc]
# 'file', 'dir', or 'junction' (Windows only)
type = parsePath( absLoc ).extname ? 'file' : 'junction'
FS.symlinkSync absTarg, absLoc, type
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: ( json, jst, format, opts, theme, curFmt ) ->
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
result
###* Freeze newlines for protection against errant JST parsers. ###
freeze = ( markup ) ->
markup.replace( _reg.regN, _defaultOpts.nSym )
markup.replace( _reg.regR, _defaultOpts.rSym )
###* Unfreeze newlines when the coast is clear. ###
unfreeze = ( markup ) ->
markup.replace _reg.regSymR, '\r'
markup.replace _reg.regSymN, '\n'
###* Default template generator options. ###
_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: ( txt ) -> txt
raw: ( txt ) -> txt
xml: ( txt ) -> XML(txt)
md: ( txt ) -> MD( txt || '' )
mdin: ( txt ) -> MD(txt || '' ).replace(/^\s*<p>|<\/p>\s*$/gi, '')
lower: ( txt ) -> txt.toLowerCase()
link: ( name, url ) ->
return if url then '<a href="' + url + '">' + name + '</a>' else 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
###* 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' )