1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2025-07-06 10:21:03 +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

277
src/core/fresh-theme.coffee Normal file
View File

@ -0,0 +1,277 @@
###*
Definition of the FRESHTheme class.
@module core/fresh-theme
@license MIT. See LICENSE.md for details.
###
FS = require 'fs'
validator = require 'is-my-json-valid'
_ = require 'underscore'
PATH = require 'path'
parsePath = require 'parse-filepath'
pathExists = require('path-exists').sync
EXTEND = require 'extend'
HMSTATUS = require './status-codes'
moment = require 'moment'
loadSafeJson = require '../utils/safe-json-loader'
READFILES = require 'recursive-readdir-sync'
###
The FRESHTheme class is a representation of a FRESH theme
asset. See also: JRSTheme.
@class FRESHTheme
###
class FRESHTheme
###
Open and parse the specified theme.
###
open: ( themeFolder ) ->
this.folder = themeFolder;
# Open the [theme-name].json file; should have the same name as folder
pathInfo = parsePath( themeFolder )
# Set up a formats hash for the theme
formatsHash = { }
# Load the theme
themeFile = PATH.join( themeFolder, 'theme.json' )
themeInfo = loadSafeJson( themeFile )
if themeInfo.ex
throw
fluenterror:
if themeInfo.ex.operation == 'parse'
then HMSTATUS.parseError
else HMSTATUS.readError
inner: themeInfo.ex.inner
that = this
# Move properties from the theme JSON file to the theme object
EXTEND true, @, themeInfo.json
# Check for an "inherits" entry in the theme JSON.
if @inherits
cached = { }
_.each @inherits, (th, key) ->
themesFolder = require.resolve 'fresh-themes'
d = parsePath( themeFolder ).dirname
themePath = PATH.join d, th
cached[ th ] = cached[th] || new FRESHTheme().open( themePath )
formatsHash[ key ] = cached[ th ].getFormat( key )
# Check for an explicit "formats" entry in the theme JSON. If it has one,
# then this theme declares its files explicitly.
if !!@formats
formatsHash = loadExplicit.call this, formatsHash
@explicit = true;
else
formatsHash = loadImplicit.call this, formatsHash
# Cache
@formats = formatsHash
# Set the official theme name
@name = parsePath( @folder ).name
@
### Determine if the theme supports the specified output format. ###
hasFormat: ( fmt ) -> _.has @formats, fmt
### Determine if the theme supports the specified output format. ###
getFormat: ( fmt ) -> @formats[ fmt ]
### Load the theme implicitly, by scanning the theme folder for files. TODO:
Refactor duplicated code with loadExplicit. ###
loadImplicit = (formatsHash) ->
# Set up a hash of formats supported by this theme.
that = @
major = false
# Establish the base theme folder
tplFolder = PATH.join @folder, 'src'
# 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
# the formatsHash object.
fmts = READFILES(tplFolder).map (absPath) ->
# If this file lives in a specific format folder within the theme,
# such as "/latex" or "/html", then that format is the output format
# for all files within the folder.
pathInfo = parsePath absPath
outFmt = ''
isMajor = false
portion = pathInfo.dirname.replace tplFolder,''
if portion && portion.trim()
return if portion[1] == '_'
reg = /^(?:\/|\\)(html|latex|doc|pdf|png|partials)(?:\/|\\)?/ig
res = reg.exec( portion )
if res
if res[1] != 'partials'
outFmt = res[1]
else
that.partials = that.partials || []
that.partials.push( { name: pathInfo.name, path: absPath } )
return null
# Otherwise, the output format is inferred from the filename, as in
# compact-[outputformat].[extension], for ex, compact-pdf.html.
if !outFmt
idx = pathInfo.name.lastIndexOf '-'
outFmt = if idx == -1 then pathInfo.name else pathInfo.name.substr( idx + 1 )
isMajor = true
# We should have a valid output format now.
formatsHash[ outFmt ] = formatsHash[outFmt] || {
outFormat: outFmt,
files: []
}
# Create the file representation object.
obj =
action: 'transform'
path: absPath
major: isMajor
orgPath: PATH.relative(tplFolder, absPath)
ext: pathInfo.extname.slice(1)
title: friendlyName( outFmt )
pre: outFmt
# outFormat: outFmt || pathInfo.name,
data: FS.readFileSync( absPath, 'utf8' )
css: null
# Add this file to the list of files for this format type.
formatsHash[ outFmt ].files.push( obj )
obj
# Now, get all the CSS files...
@cssFiles = fmts.filter (fmt) -> fmt and (fmt.ext == 'css')
# For each CSS file, get its corresponding HTML file. It's possible that
# a theme can have a CSS file but *no* HTML file, as when a theme author
# creates a pure CSS override of an existing theme.
@cssFiles.forEach (cssf) ->
idx = _.findIndex fmts, ( fmt ) ->
fmt && fmt.pre == cssf.pre && fmt.ext == 'html'
cssf.major = false
if idx > -1
fmts[ idx ].css = cssf.data
fmts[ idx ].cssPath = cssf.path
else
if that.inherits
# Found a CSS file without an HTML file in a theme that inherits
# from another theme. This is the override CSS file.
that.overrides = { file: cssf.path, data: cssf.data }
formatsHash
###
Load the theme explicitly, by following the 'formats' hash
in the theme's JSON settings file.
###
loadExplicit = (formatsHash) ->
# Housekeeping
tplFolder = PATH.join this.folder, 'src'
act = null
that = this
# 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
# the formatsHash object.
fmts = READFILES( tplFolder ).map (absPath) ->
act = null
# If this file is mentioned in the theme's JSON file under "transforms"
pathInfo = parsePath(absPath)
absPathSafe = absPath.trim().toLowerCase()
outFmt = _.find Object.keys( that.formats ), ( fmtKey ) ->
fmtVal = that.formats[ fmtKey ]
_.some fmtVal.transform, (fpath) ->
absPathB = PATH.join( that.folder, fpath ).trim().toLowerCase()
absPathB == absPathSafe
act = 'transform' if outFmt
# If this file lives in a specific format folder within the theme,
# such as "/latex" or "/html", then that format is the output format
# for all files within the folder.
if !outFmt
portion = pathInfo.dirname.replace tplFolder,''
if portion && portion.trim()
reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig
res = reg.exec portion
res && (outFmt = res[1])
# Otherwise, the output format is inferred from the filename, as in
# compact-[outputformat].[extension], for ex, compact-pdf.html.
if !outFmt
idx = pathInfo.name.lastIndexOf '-'
outFmt = if (idx == -1) then pathInfo.name else pathInfo.name.substr(idx + 1)
# We should have a valid output format now.
formatsHash[ outFmt ] =
formatsHash[ outFmt ] || {
outFormat: outFmt,
files: [],
symLinks: that.formats[ outFmt ].symLinks
};
# Create the file representation object.
obj =
action: act
orgPath: PATH.relative(that.folder, absPath)
path: absPath
ext: pathInfo.extname.slice(1)
title: friendlyName( outFmt )
pre: outFmt
# outFormat: outFmt || pathInfo.name,
data: FS.readFileSync( absPath, 'utf8' )
css: null
# Add this file to the list of files for this format type.
formatsHash[ outFmt ].files.push( obj )
obj
# Now, get all the CSS files...
@cssFiles = fmts.filter ( fmt ) -> fmt.ext == 'css'
# For each CSS file, get its corresponding HTML file
@cssFiles.forEach ( cssf ) ->
# For each CSS file, get its corresponding HTML file
idx = _.findIndex fmts, ( fmt ) ->
fmt.pre == cssf.pre && fmt.ext == 'html'
fmts[ idx ].css = cssf.data;
fmts[ idx ].cssPath = cssf.path;
# Remove CSS files from the formats array
fmts = fmts.filter ( fmt) -> fmt.ext != 'css'
formatsHash
###
Return a more friendly name for certain formats.
TODO: Refactor
###
friendlyName = ( val ) ->
val = val.trim().toLowerCase()
friendly = { yml: 'yaml', md: 'markdown', txt: 'text' }
friendly[val] || val
module.exports = FRESHTheme