/*
* @package jscodesniffer
* @author sheiko
* @license MIT
* @copyright (c) Dmitry Sheiko http://www.dsheiko.com
* jscs standard:Jquery
* jshint unused:false
* Code style: http://docs.jquery.com/JQuery_Core_Style_Guidelines
*/
/**
* A module representing Cli
* @module lib/Cli
*/
// UMD boilerplate according to https://github.com/umdjs/umd
if ( typeof module === "object" && typeof define !== "function" ) {
/**
* Override AMD `define` function for RequireJS
* @param {function( function, Object, Object )} factory
*/
var define = function ( factory ) {
module.exports = factory( require, exports, module );
};
}
define(function() {
/**
* @access private
*/
var utils = {
/**
* @access public
* @param {string} str
* @returns {string}
*/
ltrim: function( str ) {
var lTrimRe = /^\s+/;
// normalize: ltrim
return str.replace( lTrimRe, "" );
}
},
/**
* CLI services
* @constructor
* @alias module:lib/Cli
* @param {type} fsContainer
* @param {type} pathContainer
*/
Cli = function( fsContainer, pathContainer ) {
var startTime = process.hrtime();
// Dependency injection
fsContainer = fsContainer || {};
pathContainer = pathContainer || {};
/** @lends module:lib/Cli.prototype */
return {
/**
* @type {number}
*/
fileCount: 0,
/**
* Display header information (copyright)
* @param {string} version
*/
printHeader: function( version ) {
console.log( " JsCodeSniffer " + version + " (https://github.com/dsheiko/jscodesniffer) ");
},
/**
* Display boy in the case of successful bypass
* @param {string} standard
*/
printSuccess: function( standard ) {
console.log( " \033[0;32m>>>\033[0m All the files appear to be valid according to '" + standard + "' standard" );
},
/**
* Display footer information
*/
printFooter: function() {
console.log( " " + this.fileCount + " file(s) validated in " + this.getElapsedTime() );
},
/**
* Determine script execution time
* @return {String}
*/
getElapsedTime: function() {
var precision = 0,
elapsed = process.hrtime( startTime )[ 1 ] / 1000000, // divide by a million to get nano to milli
out = process.hrtime( startTime )[ 0 ] + "s, " + elapsed.toFixed( precision ) + "ms";
startTime = process.hrtime(); // reset the timer
return out;
},
/**
* @callback dirIteratorCb
* @param {string} pathArg
* @param {string} text
*/
/**
* Apply callback to the file
* @access public
* @param {string} pathArg
* @param {dirIteratorCb} fn
*/
applyToFile: function( pathArg, fn ) {
this.fileCount++;
fn( pathArg, fsContainer.readFileSync( pathArg, "utf-8" ) );
},
/**
* Apply callback to every file within the directory recursively
* @access public
* @param {string} pathArg
* @param {dirIteratorCb} fn
*/
applyToEveryFileInDir: function( pathArg, fn ) {
var that = this,
dir = fsContainer.readdirSync( pathArg ),
stat;
dir && dir.forEach(function( file ){
var re = /\.js$/gi,
excludeRe = /\.min\.js$/gi;
stat = fsContainer.statSync( pathArg + "/" + file );
stat.isFile() && re.test( file ) && !excludeRe.test( file ) &&
that.applyToFile( pathArg + "/" + file, fn );
stat.isDirectory() && that.applyToEveryFileInDir( pathArg + "/" + file, fn );
});
},
/**
* Apply callback to every file within the path recursively
* @access public
* @param {string} pathArg
* @param {dirIteratorCb} fn
*/
applyToEveryFileInPath: function( pathArg, fn ) {
var stat,
reNormPath = /\/+$/;
pathArg = pathArg.replace( reNormPath, "" );
if ( !fsContainer.existsSync( pathArg ) ) {
throw new ReferenceError( pathArg + " doesn't exist\n" );
}
stat = fsContainer.statSync( pathArg );
return stat.isFile() ? this.applyToFile( pathArg, fn ) : this.applyToEveryFileInDir( pathArg, fn );
},
/**
* Get object with project info
* @access public
* @returns {Object}
*/
getProjectInfo: function() {
var project, plain;
try {
plain = fsContainer.readFileSync( pathContainer.join( __dirname, "..", "package.json" ), "utf-8" );
project = JSON.parse( plain );
} catch ( e ) {
throw new ReferenceError( "Cannot read package.json\n" );
}
return project;
},
/**
* Populate options with ones founds in args
* @access public
* @param {Array} args
* @param {Object} options - reference
* @returns {Object}
*/
parseCliOptions: function( args, options ) {
var re = /^--/g;
args.slice( 2 ).forEach(function( arg ){
var slices;
// normalize: ltrim
arg = utils.ltrim( arg );
if ( arg.indexOf( "--" ) === 0 ) {
slices = arg.split( "=" );
options[ slices[0].replace( re, "" ) ] = slices[ 1 ] || null;
}
});
return options;
},
/**
* Find file name or path given in CLI arguments
* @access public
* @param {string[]} args
* @returns {string[]}
*/
findPathsInCliArgs: function( args ) {
var out = args.slice( 2 ).filter(function( arg ){
arg = utils.ltrim( arg );
return arg.indexOf( "-" ) !== 0;
});
return out || [ "." ];
},
/**
* If checkstyle already exists (e.g. built by phpcs), extend it with actual report body
* @access public
* @param {string} file
* @returns {string}
*/
extractExistingReportBody: function( file ) {
var data,
// Extract body of the existing report
re1 = /^\s*<\?xml.*?\?>/i,
re2 = /^\s*<checkstyle.*?>/i,
re3 = /<\/checkstyle>\s*$/i;
if ( !fsContainer.existsSync( file ) ) {
return "";
}
data = fsContainer.readFileSync( file, "utf-8" );
return data.replace( re1, "" ).replace( re2, "" ).replace( re3, "" );
},
/**
* Find in project root (any parent) directory and read local sniffer configuration
* Just like .jshintrc
* @access public
* @param {string} pathArg
* @returns {Object}
*/
readRealtimeConfig: function( pathArg ) {
var LOCAL_CFG_FILE = ".jscsrc",
parentPath,
cfgPath = pathContainer.join( pathArg, LOCAL_CFG_FILE );
if ( fsContainer.existsSync( cfgPath ) ){
return JSON.parse( fsContainer.readFileSync( cfgPath, "utf-8" ) );
}
parentPath = pathContainer.join( pathArg, "/../" );
if ( parentPath.length > 1 && fsContainer.statSync( parentPath ).isDirectory() ) {
this.readRealtimeConfig( parentPath );
}
return {};
}
};
};
return Cli;
});