cleverstack-cli
Version:
Command line interface for CleverTech's CleverStack
279 lines (228 loc) • 8.65 kB
JavaScript
var path = require( 'path' )
, mkdirp = require( 'mkdirp' )
, async = require( 'async' )
, es = require( 'event-stream' )
, fs = require( 'fs' )
, tcase = require( 't-case' )
, Project = require( path.join( __dirname, 'project' ) )
, utils = require( path.join( __dirname, 'utils' ) );
function getTemplatePath( project, name ) {
return path.resolve( [__dirname, '..', 'templates', project.name].concat( name ).join( path.sep ) );
}
var getModulePath = exports.getModulePath = function ( project, name ) {
var n = name.split( path.sep );
var moduleName = n.splice( -1 )[0];
var proj = project.useCWD ? process.cwd( ) : project.moduleDir;
return path.resolve( [proj].concat( n.length > 0 ? n.join( path.sep ) : [], moduleName ).join( path.sep ) );
}
function touchDirs( project, tmpls, names, fn ) {
var dirs = [];
tmpls = Array.isArray( tmpls ) ? tmpls : [tmpls];
names = Array.isArray( names ) ? names : [names];
// compile a list of directories within the project's applicable (names) folders
async.each( tmpls, function ( tmpl, next ) {
var templateHome = getTemplatePath( project, tmpl );
var finder = require( 'findit' )( templateHome );
finder.on( 'directory', function ( dir ) {
names.forEach( function ( name ) {
dirs.push( dir.replace( path.resolve( path.join( __dirname, '..', 'templates', project.name ) ), getModulePath( project, name ) ) );
} );
} );
finder.on( 'end', next );
},
function ( err ) {
if (!!err) {
return fn( err );
}
async.each( dirs, function ( dir, next ) {
mkdirp( dir, next );
}, function ( err ) {
fn( err, dirs );
} );
} );
}
// remove export
function checkFiles( project, dirs, tmpls, names, fn ) {
tmpls = Array.isArray( tmpls ) ? tmpls : [tmpls];
names = Array.isArray( names ) ? names : [names];
async.each( names, function ( name, next ) {
async.each( tmpls, function ( tmpl, _next ) {
var n = fileName( tmpl, name.split( path.sep ).splice( -1 )[0] )
, filter = path.join( getModulePath( project, name ), tmpl );
// filter for the necessary dirs by "tmpl" path...
// it's OK if we have ExampleModel in the config folder... since that's not
// what we're piping to for models anyway...
async.each( dirs.filter( function ( dir ) {
return filter.indexOf( dir ) > -1;
} ), function ( filePath, __next ) {
fs.exists( path.resolve( path.join( filePath, n ) ), function ( exists ) {
__next( exists ? ( n + ' already exists within ' + filePath ) : null );
} );
}, _next );
}, next );
}, fn );
}
function fileName( tmpl, name ) {
return tcase.classCase( name + ' ' + tmpl.replace( /s$/i, '' ) ) + '.js';
}
function transformFile( from, to, name, fn ) {
var file = fs.createWriteStream( to );
var read = fs.createReadStream( from, { encoding: 'utf-8' } )
.pipe( es.replace( '{{Template}}', tcase.classCase( name ) ) )
.pipe( es.replace( '{{TemplateName}}', tcase.classCase( name ) ) )
.pipe( es.replace( '{{template-name}}', tcase.paramCase( name ) ) )
.pipe( es.replace( '{{_template_}}', tcase.snakeCase( name ) ) )
.pipe( es.replace( '{{template_name}}', tcase.snakeCase( name ) ) )
.pipe( file );
read.on( 'error', fn );
file
.on( 'error', fn )
.on( 'close', function ( ) {
fn( null, to );
} );
}
function renderTemplate( dirs, names, tmpls, project, fn ) {
var files = [];
tmpls = Array.isArray( tmpls ) ? tmpls : [tmpls];
async.eachSeries( names, function ( name, next ) {
async.eachSeries( tmpls, function ( tmpl, _next) {
var filter = path.join( getModulePath( project, name ), tmpl );
async.eachSeries( dirs.filter( function ( dir ) {
return filter.indexOf( dir ) > -1;
} ), function ( dir, __next ) {
var _files = [];
var templateHome = getTemplatePath( project, tmpl );
var finder = require( 'findit' )( templateHome );
finder.on( 'file', function ( file ) {
_files.push( file );
} );
finder.on( 'end', function ( ) {
async.eachSeries( _files, function ( file, ___next ) {
var _fileName = name.split( path.sep ).splice( -1 )[0] + '-' + tmpl;
if (_fileName[_fileName.length-1] === "s") {
_fileName = _fileName.substr(0, _fileName.length-1);
}
var fileName = file.replace( templateHome, '' )
.replace( 'Template', tcase.snakeCase( _fileName ) )
.replace( 'template', tcase.classCase( _fileName ) )
, from = file
, to = path.resolve( path.join( getModulePath( project, name ) , tmpl, fileName ) );
transformFile( from, to, name, function ( err, filePath) {
if (!!err) {
return ___next( err );
}
files.push( filePath );
___next( );
} );
}, function ( err ) {
__next();
} );
} );
}, _next );
}, next );
},
function ( err ) {
fn( err, files );
} );
}
function generateRun( projects, tmpls, names, fn ) {
projects = Array.isArray( projects ) ? projects : [projects];
async.eachSeries( projects, function ( project, next ) {
touchDirs( project, tmpls, names, function ( err, dirs ) {
if (!!err) {
return utils.fail( err );
}
checkFiles( project, dirs, tmpls, names, function ( err ) {
if (!!err) {
return utils.fail( err );
}
renderTemplate( dirs, names, tmpls, project, function ( err, files ) {
if (!!err) {
return utils.fail( err );
}
files.forEach( function ( file ) {
utils.success( 'Created ' + file );
} );
next( );
} );
} );
} );
}, fn );
}
var generate = exports.generate = function( project, tmpls, names, fn ) {
if (arguments.length < 4) {
fn = names;
names = tmpls;
tmpls = project;
project = null;
}
fn = typeof fn === "function" ? fn : function noop(){};
names = Array.isArray( names ) ? names : [names];
if (!!project) {
generateRun( project, tmpls, names, fn );
} else {
Project.locations( true ).then( function ( projects ) {
generateRun( projects, tmpls, names, fn );
}, fn );
}
}
// function scaffoldFile( _path, fileName, project, name, fn ) {
// var to = path.resolve( path.join( process.cwd( ), _path ) )
// , from = path.resolve( path.join( __dirname, '..', 'templates', project, _path, fileName ) );
// console.log('to', to);
// console.log(from);
// throw new Error('hi');
// mkdirp( to, function ( err ) {
// if (!!err) {
// return utils.fail( err );
// }
// transformFile( from, path.join( to, fileName ), name, function ( err ) {
// if (!!err) {
// return utils.fail( err );
// }
// utils.success( 'Created ' + path.join( to, fileName ) );
// fn( );
// } );
// } );
// }
function scaffoldProject( project, name, filterOut, fn ) {
var templateDirs = fs.readdirSync( path.resolve( path.join( __dirname, '..', 'templates', project.name ) ) );
templateDirs = templateDirs.filter( function ( f ) {
var stat = fs.statSync( path.resolve( path.join( __dirname, '..', 'templates', project.name, f ) ) );
return stat.isDirectory( ) && filterOut.indexOf( f ) === -1;
} );
console.log(project);
if (Array.isArray( project )) {
project.map( function ( p ) {
p.useCWD = true;
} );
} else {
project.useCWD = true;
}
console.log("GEN", project, templateDirs, name);
// throw new Error('hi');
generate( project, templateDirs, name, fn );
}
module.exports.scaffold = function( name, filterOut, fn ) {
filterOut = Array.isArray( filterOut ) ? filterOut : [];
// Just for easability, we'll add an 's' to the end of the filters to cover a quasi-plural form
filterOut = filterOut.concat(
filterOut.map( function ( filter ) {
return filter + 's';
} )
);
Project.locations( ).then( function ( projects ) {
async.eachSeries( projects, function ( p, next ) {
scaffoldProject( p, name, filterOut, next );
}, function ( err ) {
if (!!err) {
return utils.fail( err );
}
if (typeof fn === "function") {
return fn( );
}
utils.success( 'Scaffold complete.' );
} );
} )
.error( fn );
}