@balderdash/sails-edge
Version:
API-driven framework for building realtime apps, using MVC conventions (based on Express and Socket.io)
206 lines (164 loc) • 6.54 kB
JavaScript
module.exports = function (sails) {
/**
* Module dependencies
*/
var Err = require('../../../errors'),
path = require('path'),
ChildProcess = require('child_process');
return {
/**
* Initialize this project's Grunt tasks
* and execute the environment-specific gruntfile
*
*/
initialize: function (cb) {
sails.log.verbose('Loading app Gruntfile...');
// Start task depending on environment
if(sails.config.environment === 'production'){
return this.runTask('prod', cb);
}
this.runTask('default', cb);
},
/**
* Fork Grunt child process
*
* @param {String} taskName - grunt task to run
* @param {Function} cb - optional, fires when the Grunt task has been started (non-production) or finished (production)
*/
runTask: function (taskName, cb_afterTaskStarted) {
cb_afterTaskStarted = cb_afterTaskStarted || function () {};
var pathToSails = path.resolve(__dirname, '../../../');
if (!taskName) {
taskName = '';
}
var execArgs = process.execArgv.slice(0);
if(execArgs.indexOf('--debug') !== -1) {
execArgs.splice(execArgs.indexOf('--debug'),1);
}
// Fork Grunt child process
var child = ChildProcess.fork(
// cwd for child process
path.join(__dirname, 'grunt-wrapper.js'),
// cmd args+opts for child process
[
taskName,
'--pathToSails='+pathToSails,
// Backwards compatibility for v0.9.x
'--gdsrc='+ pathToSails + '/node_modules'
],
// opts to pass to node's `child_process` logic
{
silent: true,
stdio: 'pipe',
execArgv: execArgs
}
);
var errorMsg = '';
var stackTrace = '';
// Log output as it comes in to the appropriate log channel
child.stdout.on('data', function (consoleMsg) {
// store all the output
consoleMsg = consoleMsg.toString();
errorMsg += consoleMsg;
// Clean out all the whitespace
var trimmedStackTrace = (typeof stackTrace === 'string') ? stackTrace : '';
trimmedStackTrace = trimmedStackTrace.replace(/[\n\s]*$/,'');
trimmedStackTrace = trimmedStackTrace.replace(/^[\n\s]*/,'');
var trimmedConsoleMsg = (typeof consoleMsg === 'string') ? consoleMsg : '';
trimmedConsoleMsg = trimmedConsoleMsg.replace(/[\n\s]*$/,'');
trimmedConsoleMsg = trimmedConsoleMsg.replace(/^[\n\s]*/,'');
// Remove '--force to continue' message since it makes no sense
// in this context:
trimmedConsoleMsg = trimmedConsoleMsg.replace(/Use --force to continue\./i, '');
trimmedStackTrace = trimmedStackTrace.replace(/Use --force to continue\./i, '');
// Find the Stack Trace related to this warning
stackTrace = errorMsg.substring(errorMsg.lastIndexOf('Running "'));
// if (consoleMsg.match(/Use --force to continue/)) {
// // sails.log.warn('** Grunt :: Warning **');
// // sails.log.warn(errorMsg,trimmedStackTrace);
// }
// Handle fatal errors, like missing grunt dependency, etc.
if (consoleMsg.match(/Fatal error/g)) {
// If no Gruntfile exists, don't crash- just display a warning.
if (consoleMsg.match(/Unable to find Gruntfile/i)) {
sails.log.info('Gruntfile could not be found.');
sails.log.info('(no grunt tasks will be run.)');
if(sails.config.environment === 'production'){
cb_afterTaskStarted();
}
return;
}
Err.fatal.__GruntAborted__(trimmedConsoleMsg, trimmedStackTrace);
return;
}
// Handle fatal Grunt errors by killing Sails process as well
if (consoleMsg.match(/Aborted due to warnings/)) {
sails.log.error('** Grunt :: An error occurred. **');
// sails.log.warn(trimmedStackTrace);
// sails.emit('hook:grunt:error', trimmedStackTrace);
Err.fatal.__GruntAborted__(trimmedConsoleMsg, trimmedStackTrace);
return;
}
if (consoleMsg.match(/ParseError/)) {
sails.log.warn('** Grunt :: Parse Warning **');
sails.log.warn(trimmedStackTrace);
}
// Only display console message if it has content besides whitespace
else if ( !consoleMsg.match(/^\s*$/) ) {
sails.log.verbose('Grunt :: ' + trimmedConsoleMsg);
}
});
// Handle general-case grunt output:
child.stdout.on('error', function (gruntOutput) {
sails.log.error('Grunt ::', _sanitize(gruntOutput));
});
child.stderr.on('data', function (gruntOutput) {
gruntOutput = _sanitize(gruntOutput);
// Ignore the "debugger listening" message from node --debug
if (gruntOutput.match(/debugger listening on port/)) {
return;
}
sails.log.error('Grunt ::', gruntOutput);
});
child.stderr.on('error', function (gruntOutput) {
sails.log.error('Grunt ::', _sanitize(gruntOutput));
});
// When process is complete, fire event on `sails`
child.on('exit', function (code, s) { // TODO: unused variable s
if ( code !== 0 ) return sails.emit('hook:grunt:error');
sails.emit('hook:grunt:done');
// Fire finish after grunt is done in production
if(sails.config.environment === 'production'){
cb_afterTaskStarted();
}
});
// Since there's likely a watch task involved, and we may need
// to flush the whole thing, we need a way to grab hold of the child process
// So we save a reference to it
sails.log.verbose('Tracking new grunt child process...');
sails.childProcesses.push(child);
// Go ahead and get out of here, since Grunt might sit there backgrounded
if(sails.config.environment !== 'production'){
cb_afterTaskStarted();
}
}
};
};
/**
* After ensuring a chunk is a string, trim any leading or
* trailing whitespace. If chunk cannot be nicely casted to a string,
* pass it straight through.
*
* @param {*} chunk
* @return {*}
*/
function _sanitize (chunk) {
if (chunk && typeof chunk === 'object' && chunk.toString) {
chunk = chunk.toString();
}
if (typeof chunk === 'string') {
chunk = chunk.replace(/^[\s\n]*/, '');
chunk = chunk.replace(/[\s\n]*$/, '');
}
return chunk;
}