From 5f19f0a7df0cd9606572d6a893231635139e5eca Mon Sep 17 00:00:00 2001 From: devlinjd Date: Mon, 7 Dec 2015 09:51:00 -0500 Subject: [PATCH] Add baseline support for multifile themes. #rough --- src/core/theme.js | 74 ++++++++++++++++++++++++----------- src/fluentcmd.js | 37 +++++++++++++----- src/gen/html-generator.js | 4 +- src/gen/template-generator.js | 14 ++++--- 4 files changed, 90 insertions(+), 39 deletions(-) diff --git a/src/core/theme.js b/src/core/theme.js index 3f5c0eb..3fbcb01 100644 --- a/src/core/theme.js +++ b/src/core/theme.js @@ -12,7 +12,7 @@ Abstract theme representation. , PATH = require('path') , EXTEND = require('../utils/extend') , moment = require('moment') - , recursiveReadSync = require('recursive-readdir-sync'); + , RECURSIVE_READ_DIR = require('recursive-readdir-sync'); /** The Theme class is a representation of a FluentCV theme asset. @@ -40,47 +40,77 @@ Abstract theme representation. var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) ); EXTEND( true, this, themeInfo ); + var formatsHash = { }; + // Iterate over all files in the theme folder, producing an array, fmts, // containing info for each file. var tplFolder = PATH.join( themeFolder, 'src' ); - var fmts = recursiveReadSync( tplFolder ).map( function( file ) { - var absPath = PATH.join( tplFolder, file ); - var pathInfo = PATH.parse(absPath); - var temp = [ pathInfo.name, { - title: friendlyName(pathInfo.name), - pre: pathInfo.name, - ext: pathInfo.ext.slice(1), - path: absPath, - data: FS.readFileSync( absPath, 'utf8' ), - css: null - }]; - return temp; - }); + var fmts = RECURSIVE_READ_DIR( tplFolder ).map( + function( absPath ) { + + var pathInfo = PATH.parse(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. + var outFmt = ''; + var portion = pathInfo.dir.replace(tplFolder,''); + if( portion && portion.trim() ) { + var reg = /^(?:\/|\\)(html|latex|doc|pdf)(?:\/|\\)?/ig; + var 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 ) { + var idx = pathInfo.name.lastIndexOf('-'); + outFmt = ( idx === -1 ) ? pathInfo.name : pathInfo.name.substr( idx + 1 ) + } + + // We should have a valid output format now + formatsHash[ outFmt ] = formatsHash[outFmt] || { outFormat: outFmt, files: [] }; + + var obj = { + path: absPath, + ext: pathInfo.ext.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 ); + return obj; + } + ); // Add freebie formats every theme gets - fmts.push( [ 'json', { title: 'json', pre: 'json', ext: 'json', path: null, data: null } ] ); - fmts.push( [ 'yml', { title: 'yaml', pre: 'yml', ext: 'yml', path: null, data: null } ] ); + formatsHash[ 'json' ] = { title: 'json', outFormat: 'json', pre: 'json', ext: 'json', path: null, data: null }; + formatsHash[ 'yml' ] = { title: 'yaml', outFormat: 'yml', pre: 'yml', ext: 'yml', path: null, data: null }; // Now, get all the CSS files... - this.cssFiles = fmts.filter(function( fmt ){ return fmt[1].ext === 'css'; }); + this.cssFiles = fmts.filter(function( fmt ){ return fmt.ext === 'css'; }); // ...and assemble information on them this.cssFiles.forEach(function( cssf ) { // For each CSS file, get its corresponding HTML file var idx = _.findIndex(fmts, function( fmt ) { - return fmt[1].pre === cssf[1].pre && fmt[1].ext === 'html' + return fmt.pre === cssf.pre && fmt.ext === 'html' }); - fmts[ idx ][1].css = cssf[1].data; - fmts[ idx ][1].cssPath = cssf[1].path; + fmts[ idx ].css = cssf.data; + fmts[ idx ].cssPath = cssf.path; }); // Remove CSS files from the formats array fmts = fmts.filter( function( fmt) { - return fmt[1].ext !== 'css'; + return fmt.ext !== 'css'; }); // Create a hash out of the formats for this theme - this.formats = _.object( fmts ); + this.formats = formatsHash; // Set the official theme name this.name = PATH.parse( themeFolder ).name; diff --git a/src/fluentcmd.js b/src/fluentcmd.js index 8e61ad6..0c03a63 100644 --- a/src/fluentcmd.js +++ b/src/fluentcmd.js @@ -76,7 +76,7 @@ module.exports = function () { targets.push.apply(targets, fmat === '.all' ? Object.keys( theTheme.formats ).map(function(k){ var z = theTheme.formats[k]; - return { file: to.replace(/all$/g,z.pre), fmt: z } + return { file: to.replace(/all$/g,z.outFormat), fmt: z } }) : [{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]); }); @@ -95,17 +95,34 @@ module.exports = function () { */ function single( fi, theme ) { try { - var f = fi.file, fType = fi.fmt.ext, fName = path.basename(f,'.'+fType); - var fObj = _.property( fi.fmt.pre )( theme.formats ); - var fOut = path.join( f.substring( 0, f.lastIndexOf('.')+1 ) + fObj.pre); + var f = fi.file, fType = fi.fmt.outFormat, fName = path.basename(f,'.'+fType); - _log( 'Generating '.useful + fi.fmt.title.toUpperCase().useful.bold + ' resume: '.useful + - path.relative(process.cwd(), f ).useful.bold ); + if( fi.fmt.files && fi.fmt.files.length ) { + fi.fmt.files.forEach( function( form ) { - var theFormat = _fmts.filter( - function( fmt ) { return fmt.name === fi.fmt.pre; })[0]; - MKDIRP.sync( path.dirname( fOut ) ); // Ensure dest folder exists; - theFormat.gen.generate( rez, fOut, _opts ); + if( form.ext === 'css' ) + return; + + _log( 'Generating '.useful + form.title.toUpperCase().useful.bold + ' resume: '.useful + + path.relative(process.cwd(), f ).useful.bold ); + + var theFormat = _fmts.filter( + function( fmt ) { return fmt.name === form.pre; })[0]; + + MKDIRP.sync( path.dirname( f ) ); // Ensure dest folder exists; + theFormat.gen.generate( rez, f, _opts ); + + }); + } + else { + _log( 'Generating '.useful + fi.fmt.outFormat.toUpperCase().useful.bold + ' resume: '.useful + + path.relative(process.cwd(), f ).useful.bold ); + + var theFormat = _fmts.filter( + function( fmt ) { return fmt.name === fi.fmt.outFormat; })[0]; + MKDIRP.sync( path.dirname( f ) ); // Ensure dest folder exists; + theFormat.gen.generate( rez, f, _opts ); + } } catch( ex ) { _err( ex ); diff --git a/src/gen/html-generator.js b/src/gen/html-generator.js index a7740ad..4c33009 100644 --- a/src/gen/html-generator.js +++ b/src/gen/html-generator.js @@ -25,8 +25,8 @@ HTML resume generator for FluentCV. , outFolder = PATH.parse( info.outputFile ).dir, that = this; info.theme.cssFiles.forEach( function( f ) { - var fi = PATH.parse( f[1].path ); - FS.copySync( f[1].path, PATH.join( outFolder, fi.base ), { clobber: true }, function( e ) { + var fi = PATH.parse( f.path ); + FS.copySync( f.path, PATH.join( outFolder, fi.base ), { clobber: true }, function( e ) { throw { fluenterror: that.codes.copyCss, data: [cssSrc,cssDst] }; }); }); diff --git a/src/gen/template-generator.js b/src/gen/template-generator.js index 3f5b0d7..c25c956 100644 --- a/src/gen/template-generator.js +++ b/src/gen/template-generator.js @@ -96,12 +96,16 @@ Template-based resume generator base for FluentCV. // Load theme and CSS data var tplFolder = PATH.join( tFolder, 'src' ); var curFmt = theme.getFormat( this.format ); - var cssInfo = { file: curFmt.css ? curFmt.cssPath : null, data: curFmt.css || null }; - // Compile and invoke the template! - var mk = this.single( rez, curFmt.data, this.format, cssInfo, this.opts ); - this.onBeforeSave && (mk = this.onBeforeSave( { mk: mk, theme: theme, outputFile: f } )); - FS.writeFileSync( f, mk, { encoding: 'utf8', flags: 'w' } ); + var that = this; + curFmt.files.forEach(function(tplInfo){ + + var cssInfo = { file: tplInfo.css ? tplInfo.cssPath : null, data: tplInfo.css || null }; + // Compile and invoke the template! + var mk = that.single( rez, tplInfo.data, that.format, cssInfo, that.opts ); + that.onBeforeSave && (mk = that.onBeforeSave( { mk: mk, theme: theme, outputFile: f } )); + FS.writeFileSync( f, mk, { encoding: 'utf8', flags: 'w' } ); + }); },