mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2025-04-19 14:20:25 +01:00
Merge implicit and explicit generation paths, start emitting file transform & copy signals, fix various bugs, introduce new bugs, support better --debug outputs in the future.
293 lines
8.8 KiB
CoffeeScript
293 lines
8.8 KiB
CoffeeScript
###*
|
|
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 )
|
|
|
|
# Load theme files
|
|
formatsHash = _load.call @, 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. ###
|
|
_load = (formatsHash) ->
|
|
|
|
# Set up a hash of formats supported by this theme.
|
|
that = @
|
|
major = false
|
|
|
|
# Establish the base theme folder
|
|
tplFolder = PATH.join @folder, 'src'
|
|
|
|
copyOnly = ['.ttf','.otf', '.png','.jpg','.jpeg','.pdf']
|
|
|
|
# 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) ->
|
|
|
|
pathInfo = parsePath absPath
|
|
absPathSafe = absPath.trim().toLowerCase()
|
|
outFmt = ''
|
|
isMajor = false
|
|
|
|
# If this file is mentioned in the theme's JSON file under "transforms"
|
|
if that.formats
|
|
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
|
|
isMajor = true if outFmt
|
|
|
|
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.
|
|
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
|
|
|
|
act = if _.contains copyOnly, pathInfo.extname then 'copy' else 'transform'
|
|
|
|
# We should have a valid output format now.
|
|
formatsHash[ outFmt ] = formatsHash[outFmt] || {
|
|
outFormat: outFmt,
|
|
files: []
|
|
}
|
|
if that.formats?[ outFmt ]?.symLinks
|
|
formatsHash[ outFmt ].symLinks = that.formats[ outFmt ].symLinks
|
|
|
|
# Create the file representation object.
|
|
obj =
|
|
action: act
|
|
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
|
|
#
|
|
# pathInfo = parsePath absPath
|
|
# absPathSafe = absPath.trim().toLowerCase()
|
|
#
|
|
# # If this file is mentioned in the theme's JSON file under "transforms"
|
|
# 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
|