<commit_before>
var sys = require("sys");
var tcp = require("net");

var Log = require("./common").Log;

var xml = require("../../vendor/node-xml/lib/node-xml");
var Do  = require("../../vendor/do/lib/do");

/**
 * XMPPParser takes Stanzas and generates a list of Stanzas for routing.  The
 * primary XMPP protocol functionality occurs here, including login, messaging,
 * presence, etc. - thus this class carries alot of state about the connection
 * @constructor
 */
exports.XMPPParser = function() {

    var streamID;
    var jid;

    var isAuthenticated = false;
    var resource        = undefined;

    // List of response message templates
    var messages = {
        stream: {
            stream: { element : "stream",
                      prefix  : "stream",
                      attrs   : {} },

            features: { element  : "features",
                        prefix   : "stream",
                        attrs    : {},
                        children : [{ element  : "mechanisms",
                                      attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-sasl" },
                                      children : [{ element  : "mechanism",
                                                    children : [ "PLAIN" ]},
                                                //  { element  : "mechanism",
                                                //    children : [ "DIGEST-MD5" ]},
                                                  { element  : "required" }]},
                                    { element  : "bind",
                                      attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-bind" },
                                      children : [{ element : "required" }]},
                                    { element  : "session",
                                      attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-session" },
                                      children : [{ element : "optional" }]}]}

        }
    }

    // List of incoming message processing functions - the object array
    // accessors let us look up the logic by looking at the function indexed
    // by the first element's name
    var namespaces = {
        stream: {
            stream: function(message) {
                streamID = Math.uuid();
                var response = Object.clone(messages.stream.stream);
                response.attrs.to = message.attrs.from;
                response.attrs.id = streamID;
                return [response].concat([namespaces.stream.features(message)]);
            },

            features: function(message) {
                var features = Object.clone(messages.stream.features);
                if (isAuthenticated) {
                    delete features.children.mechanisms;
                } else {
                    delete features.children.session;
                }
                if (resource)     { delete features.children.bind; }
                return [features];
            }
        }
    }

    // Parse a single Stanza
    // @return a list of Stanzas
    this.parse = function(message) {
        return function(callback, errorHandler) {
            var command = message.element.split(":");
            var x = namespaces[command[0]][command[1]](message);
            callback(x);
        }
    }
}

<commit_msg>Filled in Stream namespace<commit_after>
var sys = require("sys");
var tcp = require("net");

var Log = require("./common").Log;
var Do  = require("../../vendor/do/lib/do");

/**
 * XMPPParser takes Stanzas and generates a list of Stanzas for routing.  The
 * primary XMPP protocol functionality occurs here, including login, messaging,
 * presence, etc. - thus this class carries alot of state about the connection
 * @constructor
 */
exports.XMPPParser = function() {

    var streamID        = undefined;
    var jid             = undefined;

    var isAuthenticated = false;
    var resource        = undefined;

    // List of response message templates
    var messages = {
        stream: {
            stream:     { element : "stream",
			  prefix  : "stream",
			  attrs   : {} },

            features:   { element  : "features",
                          prefix   : "stream",
                          attrs    : {},
                          children : [{ element  : "mechanisms",
					attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-sasl" },
					children : [{ element  : "mechanism",
                                                      children : [ "PLAIN" ]},
                                                    //  { element  : "mechanism",
                                                    //    children : [ "DIGEST-MD5" ]},
                                                    { element  : "required" }]},
                                      { element  : "bind",
					attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-bind" },
					children : [{ element : "required" }]},
                                      { element  : "session",
					attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-session" },
					children : [{ element : "optional" }]}]},

	    failure:    { element  : "failure",
			  attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-sasl" },
			  children : [{ element : "temporary-auth-failure" }]},

	    success:    { element  : "success",
			  attrs    : { xmlns : "urn:ietf:params:xml:ns:xmpp-sasl" }},

	    presence:   { element  : "presence",
			  attrs    : { from : undefined,
				       to   : undefined,
				       id   : undefined }},

	    unpresence: { element  : "presence",
			  attrs    : { from : undefined,
				       to   : undefined,
				       type : "unavailable" }}
        }
    }

    // List of incoming message processing functions - the object array
    // accessors let us look up the logic by looking at the function indexed
    // by the first element's name
    var namespaces = {
        stream: {
            stream: function(message) {
                streamID = Math.uuid();
                var response = Object.clone(messages.stream.stream);
                response.attrs.to = message.attrs.from;
                response.attrs.id = streamID;
                return [response].concat([namespaces.stream.features(message)]);
            },

            features: function(message) {
                var features = Object.clone(messages.stream.features);
                if (isAuthenticated) {
                    delete features.children.mechanisms;
                } else {
                    delete features.children.session;
                }
                if (resource)     { delete features.children.bind; }
                return [features];
            },

	    disconnect: function(message) {
		// (defmethod parse :disconnect
		//   [_ state]
		//   (cons [:disconnect common/domain (jid state)]
		//         (loop [friends (accounts/get-friends-user-id (:user-id state))
		//                result []]
		//           (if (empty? friends)
		//             result
		//             (recur (rest friends)
		//                    (cons (Message [:unpresence]
		// 				  (:jid (first friends))
		// 				  (jid state)
		// 				  [])
		//                          result))))))

	    },

	    auth: function(message) {
		// (defmethod parse :auth
		//   [{{mechanism :mechanism} :attrs [password & _] :message} state]
		//   (condp = (if (nil? mechanism) "PLAIN" mechanism)
		//     "PLAIN" (let [chars (.decode (Base64.) (.getBytes password))
		// 		  username (apply str (map char (take-while pos? (drop 1 chars))))
		// 		  password (apply str (map char (drop 1 (drop-while pos? (drop 1 chars)))))
		// 		  user-id (:id (accounts/login username password))]
		// 	      (if (nil? user-id)
		// 		(do (log :info (str "failed to login as username " username))
		// 		    [(Message [:failure]
		// 			      (jid state)
		// 			      common/domain
		// 			      [])])
		//                 (do (log :info (str "logged in successfully as username " username))
		//                     (dosync (alter state assoc :username username :user-id user-id))
		//                     [(Message [:success]
		//                               (jid state)
		//                               common/domain
		//                               [])])))))
	    },

	    presence: function(message) {

		// (defmethod parse :presence
		//   [_ state]
		//   (loop [friends (map :jid (accounts/get-friends-user-id (:user-id @state)))
		//          result []]
		//     (if (empty? friends)
		//       result
		//       (recur (rest friends)
		//              (cons [(Message [:presence]
		// 			     (first friends)
		// 			     (jid state)
		// 			     [(gen-id)])]  ;  presence does not send reverse notification because it could block on opening a client channel
		//                    result)))))

	    }
        }
    }

    // Parse a single Stanza
    // @return a list of Stanzas
    this.parse = function(message) {
        return function(callback, errorHandler) {
            var command = message.element.split(":");
            var x = namespaces[command[0]][command[1]](message);
            callback(x);
        }
    }
}

<|endoftext|>


<commit_before>/**
 * @file 检查dep目录下面的东东是否是合法的package
 *
 * @author ielgnaw(wuji0223@gmail.com)
 */
var edp = require( 'edp-core' );
var fs = require( 'fs' );

var semver = require( 'semver' );

var invalidResource = [];

/**
 * 检查dep目录下的东西是否是合法的
 * 1. 检查是否存在 package.json
 * 2. 检查目录名是否符合semver的要求
 *
 * @param {string} file 文件的路径.
 */
function checkDepDir( dir ) {

    var pkgConfigPath = edp.path.join( dir, 'package.json' );
    if ( !fs.existsSync( pkgConfigPath ) ) {
        invalidResource.push( {
            path: dir,
            pkgPath: pkgConfigPath
        } );
        return;
    }

    var versionInfo = dir.slice(dir.lastIndexOf('/') + 1);

    if ( !semver.valid(versionInfo) ) {
        invalidResource.push( {
            path: dir
        } );
        return;
    }

}

module.exports = exports = function(args, opts) {
    edp.log.info( 'Checking dep directory...' );

    var util = require( './util' );

    var candidates = util.getCandidates(
        args,
        [ 'dep/*/*' ]
    );

    if ( candidates.length ) {
        var realCandidates = [];
        candidates.forEach(
            function ( item ) {
                if ( util.isIgnored( item, '.doctorignore' ) ) {
                    return;
                }
                checkDepDir( item );
            }
        );
    }

    if ( invalidResource.length ) {
        invalidResource.forEach(
            function ( item ) {
                edp.log.info( '%s', item.path );
                if ( item.pkgPath ) {
                    edp.log.warn( '→ No such file %s', item.pkgPath );
                }
                else {
                    edp.log.warn(
                        '→ The dep dir %s don\'t comply with semver spec',
                         item.path
                    );
                }
            }
        );
    }
};<commit_msg>optimize code<commit_after>/**
 * @file 检查dep目录下面的东东是否是合法的package
 *
 * @author ielgnaw(wuji0223@gmail.com)
 */
var edp = require( 'edp-core' );
var fs = require( 'fs' );

var semver = require( 'semver' );

/**
 * 检查dep目录下的东西是否是合法的
 * 1. 检查是否存在 package.json
 * 2. 检查目录名是否符合semver的要求
 *
 * @param {string} file 文件的路径.
 */
function checkDepDir( dir ) {

    var invalidResource = [];

    var pkgConfigPath = edp.path.join( dir, 'package.json' );
    if ( !fs.existsSync( pkgConfigPath ) ) {
        invalidResource.push( {
            path: dir,
            pkgPath: pkgConfigPath
        } );
    }

    var versionInfo = dir.slice( dir.lastIndexOf( '/' ) + 1 );

    if ( !semver.valid( versionInfo ) ) {
        invalidResource.push( {
            path: dir
        } );
    }

    function dump( item ) {
        if ( item.pkgPath ) {
            edp.log.warn( '→ No such file %s', item.pkgPath );
        }
        else {
            edp.log.warn(
                '→ The dep dir %s don\'t comply with semver spec',
                item.path
            );
        }
    }

    if ( invalidResource.length ) {
        edp.log.info( '%s', edp.path.relative( process.cwd(), dir ) );
        invalidResource.forEach( dump );
    }

}

module.exports = exports = function( args, opts ) {
    edp.log.info( 'Checking dep directory...' );

    var util = require( './util' );

    var candidates = util.getCandidates(
        args,
        [ 'dep/*/*' ]
    );

    if ( candidates.length ) {
        var realCandidates = [];
        candidates.forEach(
            function ( item ) {
                if ( util.isIgnored( item ) ) {
                    return;
                }
                checkDepDir( item );
            }
        );
    }
};<|endoftext|>


<commit_before>var puts = require('util').puts,
	http = require('http'),
	sexy = require('sexy-args');

exports.CheckNPM = function(options) {
	sexy.args([this, 'object1'], {
		'object1': {
			cacheSize: 64,
			packagePageUrl: 'search.npmjs.org',
			packagePagePath: '/_view/updated',
			limit: 5
		}
	}, function() {
		this.cache = [];
		this.options = options;
	});
};

exports.CheckNPM.prototype.checkForUpdates = function(callback) {
	var page = this.fetchPackagePage(callback);
};

exports.CheckNPM.prototype.fetchPackagePage = function(callback) {
	var client = http.createClient(80, this.options.packagePageUrl),
		_this = this;

	client.addListener('error', function(e) {
		puts(e);
	});

	var req = client.request('GET', this.options.packagePagePath + '?descending=true&limit='+ this.options.limit + '&include_docs=true', {
		Host: this.options.packagePageUrl,
		'User-Agent': 'npm-tweets',
		'Cache-Control': 'max-age=0',
		'Accept': 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
		'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
		'Accept-Encoding': 'text/html',
		'Accept-Language': 'en-US,en;q=0.8'
	});

	req.end();

	req.addListener('response', function(res){
		res.body = '';
		res.addListener('data', function(chunk){ res.body += chunk; });
		res.addListener('end', function(){
			try {
				_this.returnNewPackages( (JSON.parse(res.body)).rows , callback);
			} catch (e) {
				_this.returnNewPackages([], callback)
			}
		});
	});
};

exports.CheckNPM.prototype.returnNewPackages = function(rawPackages, callback) {
	var newPackages = this.getNewPackages(rawPackages);
	callback(newPackages);
};

exports.CheckNPM.prototype.getNewPackages = function(rawPackages) {
	var newPackages = [];
	for (var i = 0, rawPackage; (rawPackage = rawPackages[i]) != null; i++) {

		var mungedPackage = this.mungePackage(rawPackage);

		if (!this.inCache(mungedPackage)) {
			newPackages.unshift(mungedPackage);
			this.addToCache(mungedPackage);
		}

	}
	return newPackages;
};

exports.CheckNPM.prototype.mungePackage = function(rawPackage) {

	var distTags = rawPackage.doc['dist-tags'] || {};

	return {
		version: distTags.latest || false,
		description: rawPackage.doc.description || '',
		name: rawPackage.doc.name,
		url: 'http://' + this.options.packagePageUrl + '/#/' + escape(rawPackage.doc.name)
	}
};

exports.CheckNPM.prototype.seedCacheWithTwitterStream = function(twitterFeed) {
	for (var i = 0; i < twitterFeed.length; i++) {
		var text = twitterFeed[i].text;
		var name = text.match('([^:( ]*)')[1];
		var version = '';
		if (text.match(/\((.*)\)/)) {
			version = text.match(/\(([^:]*)\)/)[1];
		}
		var package = {
			name: name,
			version: version
		};
		this.addToCache(package);
	};
};

exports.CheckNPM.prototype.addToCache = function(package) {
	var newKey = this.createKey(package);
	this.cache.push(newKey);
	if (this.cache.length > this.options.cacheSize) {
		this.cache.shift();
	}
};

exports.CheckNPM.prototype.inCache = function(package) {
	var newKey = this.createKey(package);
	for (var i = 0, key; (key = this.cache[i]) != null; i++) {
		if (key == newKey) {
			return true;
		}
	}
	return false;
}

exports.CheckNPM.prototype.createKey = function(package) {
	return (package.name + package.version);
};<commit_msg>Updating packagePageUrl to point to isaacs.iriscouch.com.<commit_after>var puts = require('util').puts,
	http = require('http'),
	sexy = require('sexy-args');

exports.CheckNPM = function(options) {
	sexy.args([this, 'object1'], {
		'object1': {
			cacheSize: 64,
			packagePageUrl: 'https://isaacs.iriscouch.com/registry/_design/app',
			packagePagePath: '/_view/updated',
			limit: 5
		}
	}, function() {
		this.cache = [];
		this.options = options;
	});
};

exports.CheckNPM.prototype.checkForUpdates = function(callback) {
	var page = this.fetchPackagePage(callback);
};

exports.CheckNPM.prototype.fetchPackagePage = function(callback) {
	var client = http.createClient(80, this.options.packagePageUrl),
		_this = this;

	client.addListener('error', function(e) {
		puts(e);
	});

	var req = client.request('GET', this.options.packagePagePath + '?descending=true&limit='+ this.options.limit + '&include_docs=true', {
		Host: this.options.packagePageUrl,
		'User-Agent': 'npm-tweets',
		'Cache-Control': 'max-age=0',
		'Accept': 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
		'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
		'Accept-Encoding': 'text/html',
		'Accept-Language': 'en-US,en;q=0.8'
	});

	req.end();

	req.addListener('response', function(res){
		res.body = '';
		res.addListener('data', function(chunk){ res.body += chunk; });
		res.addListener('end', function(){
			try {
				_this.returnNewPackages( (JSON.parse(res.body)).rows , callback);
			} catch (e) {
				_this.returnNewPackages([], callback)
			}
		});
	});
};

exports.CheckNPM.prototype.returnNewPackages = function(rawPackages, callback) {
	var newPackages = this.getNewPackages(rawPackages);
	callback(newPackages);
};

exports.CheckNPM.prototype.getNewPackages = function(rawPackages) {
	var newPackages = [];
	for (var i = 0, rawPackage; (rawPackage = rawPackages[i]) != null; i++) {

		var mungedPackage = this.mungePackage(rawPackage);

		if (!this.inCache(mungedPackage)) {
			newPackages.unshift(mungedPackage);
			this.addToCache(mungedPackage);
		}

	}
	return newPackages;
};

exports.CheckNPM.prototype.mungePackage = function(rawPackage) {

	var distTags = rawPackage.doc['dist-tags'] || {};

	return {
		version: distTags.latest || false,
		description: rawPackage.doc.description || '',
		name: rawPackage.doc.name,
		url: 'http://' + this.options.packagePageUrl + '/#/' + escape(rawPackage.doc.name)
	}
};

exports.CheckNPM.prototype.seedCacheWithTwitterStream = function(twitterFeed) {
	for (var i = 0; i < twitterFeed.length; i++) {
		var text = twitterFeed[i].text;
		var name = text.match('([^:( ]*)')[1];
		var version = '';
		if (text.match(/\((.*)\)/)) {
			version = text.match(/\(([^:]*)\)/)[1];
		}
		var package = {
			name: name,
			version: version
		};
		this.addToCache(package);
	};
};

exports.CheckNPM.prototype.addToCache = function(package) {
	var newKey = this.createKey(package);
	this.cache.push(newKey);
	if (this.cache.length > this.options.cacheSize) {
		this.cache.shift();
	}
};

exports.CheckNPM.prototype.inCache = function(package) {
	var newKey = this.createKey(package);
	for (var i = 0, key; (key = this.cache[i]) != null; i++) {
		if (key == newKey) {
			return true;
		}
	}
	return false;
}

exports.CheckNPM.prototype.createKey = function(package) {
	return (package.name + package.version);
};<|endoftext|>


<commit_before>var _       = require('underscore'),
    Network = require('./network');

/**
 * Clearwing object
 * @constructor
 */
function Clearwing(options) {
	/** {@link Network} list */
	this.networks = [];

	/**
	 * Stored data
	 * @private
	 */
	this._data = {};

	/**
	 * Events
	 * @private
	 */
	this._events = {};

	/* set up data */
	this
		.set('nick', 'Fish' + Math.floor(Math.random() * 1000));
}

/**
 * Lookup a property
 * @param {...object} [namespace] Namespace name
 * @param {string} property Property name
 * @return {mixed}
 */
Clearwing.prototype.get = function() {
	return this._data[Clearwing.stringifyPropName(arguments)];
};

/**
 * Set a property
 * @param {...object} [namespace] Namespace name
 * @param {string} property Property name
 * @param {mixed} value New property value
 * @return {Clearwing}
 */
Clearwing.prototype.set = function() {
	this._data[Clearwing.stringifyPropName(_.toArray(arguments).slice(0, -1))] = arguments[arguments.length - 1];
	return this;
};

/**
 * Bind an event
 * @param {...object} [namespace] Namespace name
 * @param {string|regexp} event Event name
 * @param {function} callback Callback function
 * @return {Clearwing}
 */
Clearwing.prototype.on = function() {
	var ns       = _.toArray(arguments),
	    callback = ns.splice(-1).pop(),
	    cb       = callback,
	    event    = ns.splice(-1).pop(),
	    regexp   = event,
	    prop     = '',
	    that     = this;

	ns.push(_.isRegExp(regexp) ? 'msg' : event);
	prop = Clearwing.stringifyPropName(ns);

	if (!_.isFunction(callback))
		return this;

	if (_.isRegExp(regexp)) {
		callback = function(event) {
			if (regexp.test(event.text))
				event.text.match(regexp, cb.bind(that, event));
		};
	}

	(this._events[prop] = this._events[prop] || []).push(callback);

	return this;
};

/**
 * Emit an event
 * @param {...object} [namespace] Namespace name
 * @param {string} event Event name
 * @param {object} data Event data
 * @return {Clearwing}
 */
Clearwing.prototype.emit = function() {
	if (arguments.length < 2)
		throw new Error('#emit() expects at least two arguments');

	var ns    = _.toArray(arguments),
	    data  = ns.splice(-1).pop(),
	    prop  = '',
	    props = [],
	    that  = this,
	    replace = [
		    [/(user).[^ ]+/, '$1'],
		    [/(user).[^ ]+ /, ''],
		    [/(channel).[^ ]+/, '$1'],
		    [/(channel).[^ ]+ /, ''],
		    [/(network).[^ ]+/, '$1'],
		    [/(network).[^ ]+ /, '']
	    ];

	prop = Clearwing.stringifyPropName(ns);

	props.push(prop);

	replace.forEach(function(re) {
		props.forEach(function(prop) {
			props.push(prop.replace.apply(prop, re));
		});
	});

	props = _.chain(props).uniq().sortBy(function(e) {
		return (/network([. ]|$)/.test(e) || -800) +
		       (/channel([. ]|$)/.test(e) || -400) +
		       (/user([. ]|$)/.test(e)    || -200) +
		       e.length;
	}).value();

	props.forEach(function(event) {
		that._events[event].forEach(function(cb) {
			cb.call(that, data);
		});
	});

	return this;
};

/**
 * Helper function for flattening lists into property-safe values
 * @param {array} prop Property name array
 * @return {string}
 */
Clearwing.stringifyPropName = function(prop) {
	// Convert [{ network: 'foobar' }, 'option']
	// to 'network.foobar option'
	return _.map(prop, function(a) {
		return _.isObject(a) && !_.isArray(a) ?
		       _.chain(a).pairs().flatten().join('.').value() :
		       a;
	}).join(' ').toLowerCase();
};

/**
 * Helper function to convert flat property names into array form
 * @param {string} prop Property string
 * @return {array}
 */
Clearwing.parsePropName = function(prop) {
	return _.map(prop.toLowerCase().split(' '), function(s) {
		// Yes, a.split('.', 2) would give us two elements, but
		// they would be equivalent to a.split('.').slice(0, 2)
		var a = s.split('.'),
		    o = {};

		if (a.length < 2)
			return s;

		o[a[0]] = a.slice(1).join('.');
		return o;
	});
};

/**
 * Lookup a network by name
 *
 * Note: This function will also create a new {@link Network} if one is not found
 *
 * See {@link Clearwing#hasNetwork} if you want to know if a network exists
 * @param {string} network Network name
 * @return {Network}
 */
Clearwing.prototype.network = function(network) {
	return this.networks.filter(function(n) { return n._name == network.toLowerCase(); }).pop() ||
	       this.networks[this.networks.push(new Network(this, network)) - 1];
};

/**
 * See if a network exists
 * @param {string} network Network name
 * @return {bool} True if network exists, false if it does not
 */
Clearwing.prototype.hasNetwork = function(network) {
	return !!this.networks.filter(function(n) { return n._name == network.toLowerCase(); }).length;
};

/**
 * Use a plugin
 * @param {function} plugin
 * @return {Clearwing}
 */
Clearwing.prototype.use = function(plugin) {
	if (_.isFunction(plugin))
		plugin.call(this, this);

	return this;
};

module.exports = Clearwing;
<commit_msg>Rework Clearwing#emit() errors<commit_after>var _       = require('underscore'),
    Network = require('./network');

/**
 * Clearwing object
 * @constructor
 */
function Clearwing(options) {
	/** {@link Network} list */
	this.networks = [];

	/**
	 * Stored data
	 * @private
	 */
	this._data = {};

	/**
	 * Events
	 * @private
	 */
	this._events = {};

	/* set up data */
	this
		.set('nick', 'Fish' + Math.floor(Math.random() * 1000));
}

/**
 * Lookup a property
 * @param {...object} [namespace] Namespace name
 * @param {string} property Property name
 * @return {mixed}
 */
Clearwing.prototype.get = function() {
	return this._data[Clearwing.stringifyPropName(arguments)];
};

/**
 * Set a property
 * @param {...object} [namespace] Namespace name
 * @param {string} property Property name
 * @param {mixed} value New property value
 * @return {Clearwing}
 */
Clearwing.prototype.set = function() {
	this._data[Clearwing.stringifyPropName(_.toArray(arguments).slice(0, -1))] = arguments[arguments.length - 1];
	return this;
};

/**
 * Bind an event
 * @param {...object} [namespace] Namespace name
 * @param {string|regexp} event Event name
 * @param {function} callback Callback function
 * @return {Clearwing}
 */
Clearwing.prototype.on = function() {
	var ns       = _.toArray(arguments),
	    callback = ns.splice(-1).pop(),
	    cb       = callback,
	    event    = ns.splice(-1).pop(),
	    regexp   = event,
	    prop     = '',
	    that     = this;

	ns.push(_.isRegExp(regexp) ? 'msg' : event);
	prop = Clearwing.stringifyPropName(ns);

	if (!_.isFunction(callback))
		return this;

	if (_.isRegExp(regexp)) {
		callback = function(event) {
			if (regexp.test(event.text))
				event.text.match(regexp, cb.bind(that, event));
		};
	}

	(this._events[prop] = this._events[prop] || []).push(callback);

	return this;
};

/**
 * Emit an event
 * @param {...object} [namespace] Namespace name
 * @param {string} event Event name
 * @param {object} data Event data
 * @return {Clearwing}
 */
Clearwing.prototype.emit = function() {
	var ns    = _.toArray(arguments),
	    data  = ns.splice(-1).pop(),
	    prop  = '',
	    props = [],
	    that  = this,
	    replace = [
		    [/(user).[^ ]+/, '$1'],
		    [/(user).[^ ]+ /, ''],
		    [/(channel).[^ ]+/, '$1'],
		    [/(channel).[^ ]+ /, ''],
		    [/(network).[^ ]+/, '$1'],
		    [/(network).[^ ]+ /, '']
	    ];

	if (arguments.length < 2)
		throw new Error('#emit() expects at least two arguments');

	if (!_.isObject(arguments[arguments.length - 1]))
		throw new Error('#emit() expected argument ' + arguments.length + ' to be object');

	if (!_.isString(arguments[arguments.length - 2]))
		throw new Error('#emit() expected argument ' + (arguments.length - 1) + ' to be string');

	prop = Clearwing.stringifyPropName(ns);

	props.push(prop);

	replace.forEach(function(re) {
		props.forEach(function(prop) {
			props.push(prop.replace.apply(prop, re));
		});
	});

	props = _.chain(props).uniq().sortBy(function(e) {
		return (/network([. ]|$)/.test(e) || -800) +
		       (/channel([. ]|$)/.test(e) || -400) +
		       (/user([. ]|$)/.test(e)    || -200) +
		       e.length;
	}).value();

	props.forEach(function(event) {
		that._events[event].forEach(function(cb) {
			cb.call(that, data);
		});
	});

	return this;
};

/**
 * Helper function for flattening lists into property-safe values
 * @param {array} prop Property name array
 * @return {string}
 */
Clearwing.stringifyPropName = function(prop) {
	// Convert [{ network: 'foobar' }, 'option']
	// to 'network.foobar option'
	return _.map(prop, function(a) {
		return _.isObject(a) && !_.isArray(a) ?
		       _.chain(a).pairs().flatten().join('.').value() :
		       a;
	}).join(' ').toLowerCase();
};

/**
 * Helper function to convert flat property names into array form
 * @param {string} prop Property string
 * @return {array}
 */
Clearwing.parsePropName = function(prop) {
	return _.map(prop.toLowerCase().split(' '), function(s) {
		// Yes, a.split('.', 2) would give us two elements, but
		// they would be equivalent to a.split('.').slice(0, 2)
		var a = s.split('.'),
		    o = {};

		if (a.length < 2)
			return s;

		o[a[0]] = a.slice(1).join('.');
		return o;
	});
};

/**
 * Lookup a network by name
 *
 * Note: This function will also create a new {@link Network} if one is not found
 *
 * See {@link Clearwing#hasNetwork} if you want to know if a network exists
 * @param {string} network Network name
 * @return {Network}
 */
Clearwing.prototype.network = function(network) {
	return this.networks.filter(function(n) { return n._name == network.toLowerCase(); }).pop() ||
	       this.networks[this.networks.push(new Network(this, network)) - 1];
};

/**
 * See if a network exists
 * @param {string} network Network name
 * @return {bool} True if network exists, false if it does not
 */
Clearwing.prototype.hasNetwork = function(network) {
	return !!this.networks.filter(function(n) { return n._name == network.toLowerCase(); }).length;
};

/**
 * Use a plugin
 * @param {function} plugin
 * @return {Clearwing}
 */
Clearwing.prototype.use = function(plugin) {
	if (_.isFunction(plugin))
		plugin.call(this, this);

	return this;
};

module.exports = Clearwing;
<|endoftext|>


<commit_before>var fs = require('fs');
var stackTrace = require('stack-trace');
var asyncEach = require('async-each');
var hljs = require('highlight.js');
var sep = require('path').sep;
var errorView = require('./template/errorView.js');
var instanceRecycler = require('./instanceRecycler.js');
var helper = require('ima-helpers');
var templateProcessor = require('./templateProcessor.js');

var renderedSPAs = {};

hljs.configure({
	tabReplace: '  ',
	lineNodes: true
});

module.exports = ((environment, logger, languageLoader, appFactory) => {
	appFactory();

	var _displayDetails = (err, req, res) => {
		var stack = stackTrace.parse(err);
		var fileIndex = 1;

		logger.error('The application crashed due to an uncaught exception', { err });

		asyncEach(stack, function getContentInfo(item, cb) {
			// exclude core node modules and node modules
			if (
				(item.fileName) &&
				(item.fileName.indexOf(sep) !== -1) &&
				!/node_modules/.test(item.fileName)
				!/internal/.test(item.fileName)
			) {
				fs.readFile(item.fileName, 'utf-8', function(err, content) {
					if (err) {
						return cb(err);
					}

					content = hljs.highlight('javascript', content);

					// start a few lines before the error or at the beginning of the file
					var start = Math.max(item.lineNumber - 11, 0);
					var lines = content.value.split('\n').map((line) => {
						return '' + line + '';
					});
					// end a few lines after the error or the last line of the file
					var end = Math.min(item.lineNumber + 10, lines.length);
					var snippet = lines.slice(start, end);
					// array starts at 0 but lines numbers begin with 1, so we have to
					// subtract 1 to get the error line position in the array
					var errLine = item.lineNumber - start - 1;

					snippet[errLine] = snippet[errLine].replace('', '');

					item.content = snippet.join('\n');
					item.errLine = errLine;
					item.startLine = start;
					item.id = 'file-' + fileIndex;

					fileIndex++;

					cb(null, item);
				});
			} else {
				cb();
			}
		}, (e, items) => {
			if (!Array.isArray(items)) {
				items = [];
			}

			items = items.filter((item) => {
				return !!item;
			});

			// if something bad happened while processing the stacktrace
			// make sure to return something useful
			if (e) {
				logger.error('Failed to display error page', { e });
				return res.send(err.stack);
			}

			res.send(errorView(err, items));
		});
	};

	var _initApp = (req, res, appMain) => {
		var bootConfig = _getBootConfig(req, res);
		var app = instanceRecycler.getInstance();

		Object.assign(
			bootConfig,
			appMain.getInitialAppConfigFunctions(),
			appMain.ima.getInitialPluginConfig(),
			appMain.ima.getInitialImaConfigFunctions()
		);
		app.bootstrap
			.run(bootConfig);

		return app;
	};

	var showStaticErrorPage = (error, req, res) => {
		logger.error('Failed to display error page, displaying the static error page', { error });

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/error.html', 'utf-8', (e, content) => {
				var status = 500;
				res.status(status);

				if (e) {
					res.send('500');
					reject(e);
				}

				res.send(content);

				resolve({ content, status, error });
			});
		});
	};

	var showStaticSPAPage = (req, res) => {
		var bootConfig = _getBootConfig(req, res);
		var status = 200;

		var cacheKey = [
			bootConfig.settings.$Protocol,
			bootConfig.settings.$Language,
			bootConfig.settings.$Host,
			bootConfig.settings.$Root,
			bootConfig.settings.$LanguagePartPath
		].join('|');
		if (renderedSPAs[cacheKey]) {
			res.status(status);
			res.send(renderedSPAs[cacheKey]);

			return Promise.resolve({
				content: renderedSPAs[cacheKey],
				status,
				SPA: true
			});
		}

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/spa.html', 'utf-8', (error, content) => {
				if (error) {
					showStaticErrorPage(error, req, res);
					reject(error);
				} else {
					content = templateProcessor(content, bootConfig.settings);

					renderedSPAs[cacheKey] = content;

					res.status(status);
					res.send(content);

					resolve({ content, status, SPA: true, error: null });
				}
			});
		});
	};

	var _haveToServeSPA = (req, app) => {
		var userAgent = req.headers['user-agent'] || '';
		var isAllowedServeSPA = environment.$Server.serveSPA.allow;
		var isServerBusy = instanceRecycler.isReachToMaxConcurrencyRequests();
		var isAllowedUserAgent = !environment.$Server.serveSPA.blackListReg.test(userAgent);
		var canBeRouteServeAsSPA = true;
		var router = app.oc.get('$Router');

		try {
			var routeInfo = router.getCurrentRouteInfo();
			var routeOptions = routeInfo.route.getOptions();

			if (routeOptions.serveSPA === false) {
				canBeRouteServeAsSPA = false;
			}
		} catch (e) {}

		return isAllowedServeSPA && isServerBusy && isAllowedUserAgent && canBeRouteServeAsSPA;
	};

	var _getBootConfig = (req, res) => {
		var language = res.locals.language;
		var languagePartPath = res.locals.languagePartPath;
		var host = res.locals.host;
		var root = res.locals.root;
		var protocol = res.locals.protocol;

		var dictionary = languageLoader(language);

		var bootConfig = {
			services: {
				request: req,
				response: res,
				$IMA: {},
				dictionary: {
					$Language: language,
					dictionary: dictionary
				},
				router: {
					$Protocol: protocol,
					$Host: host,
					$Root: root,
					$LanguagePartPath: languagePartPath
				}
			},
			settings: {
				$Debug: environment.$Debug,
				$Env: environment.$Env,
				$Version: environment.$Version,
				$App: environment.$App || {},
				$Protocol: protocol,
				$Language: language,
				$Host: host,
				$Root: root,
				$LanguagePartPath: languagePartPath
			}
		};

		return bootConfig;
	};

	var _applyError = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleError({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((fatalError) => {
					showStaticErrorPage(fatalError, req, res);
					instanceRecycler.clearInstance(app);

					return Promise.reject(fatalError);
				});
		} catch (e) {
			showStaticErrorPage(e, req, res);
			instanceRecycler.clearInstance(app);
			promise = Promise.reject(e);
		}

		return promise;
	};

	var _applyNotFound = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleNotFound({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return _applyError(error, req, res, app);
				});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _applyRedirect = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			app.oc.get('$Router').redirect(error.getParams().url, { httpStatus: error.getHttpStatus() });
			instanceRecycler.clearInstance(app);
			promise = Promise.resolve({
				content: null,
				status: error.getHttpStatus(),
				error: error
			});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _importAppMain = () => {
		return $IMA.Loader
			.import('app/main')
			.then((appMain) => {
				if (!instanceRecycler.isInitialized()) {
					instanceRecycler.init(appMain.ima.createImaApp, environment.$Server.concurrency);
				}

				return appMain;
			});
	};

	var errorHandler = (error, req, res, app) => {
		var returnPromise = Promise.reject(error);

		if (environment.$Debug) {

			if (app) {
				instanceRecycler.clearInstance(app);
			}

			_displayDetails(error, req, res);
		} else {
			var appPromise = Promise.resolve(app);

			if (!app) {
				appPromise = _importAppMain()
					.then((appMain) => {
						return _initApp(req, res, appMain);
					});
			}

			returnPromise = appPromise
				.then((app) => {
					var router = app.oc.get('$Router');
					app.oc.get('$Cache').clear();

					if (router.isClientError(error)) {
						return _applyNotFound(error, req, res, app);
					} else if (router.isRedirection(error)) {
						return _applyRedirect(error, req, res, app);
					} else {
						return _applyError(error, req, res, app);
					}
				})
				.catch((e) => {
					showStaticErrorPage(e, req, res);

					instanceRecycler.clearInstance(app);
					returnPromise = Promise.reject(e);
				});
		}

		return returnPromise;
	};

	var _generateResponse = (req, res, app) => {
		var returnPromise = Promise.reject(new Error());
		var router = app.oc.get('$Router');

		try {
			returnPromise = router
				.route(router.getPath())
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return errorHandler(error, req, res, app);
				});
		} catch (e) {
			returnPromise = errorHandler(e, req, res, app);
		}

		return returnPromise;
	};

	var requestHandler = (req, res) => {
		if (environment.$Env === 'dev') {
			appFactory();
			instanceRecycler.clear();
		}

		return _importAppMain()
			.then((appMain) => {
				var app = _initApp(req, res, appMain);

				if (_haveToServeSPA(req, app)) {
					instanceRecycler.clearInstance(app);
					return showStaticSPAPage(req, res);
				}

				return _generateResponse(req, res, app);
			});
	};

	return {
		errorHandler,
		requestHandler,
		showStaticErrorPage,
		showStaticSPAPage
	};
});
<commit_msg>#! response may be send only once<commit_after>var fs = require('fs');
var stackTrace = require('stack-trace');
var asyncEach = require('async-each');
var hljs = require('highlight.js');
var sep = require('path').sep;
var errorView = require('./template/errorView.js');
var instanceRecycler = require('./instanceRecycler.js');
var helper = require('ima-helpers');
var templateProcessor = require('./templateProcessor.js');

var renderedSPAs = {};

hljs.configure({
	tabReplace: '  ',
	lineNodes: true
});

module.exports = ((environment, logger, languageLoader, appFactory) => {
	appFactory();

	var _displayDetails = (err, req, res) => {
		var stack = stackTrace.parse(err);
		var fileIndex = 1;

		logger.error('The application crashed due to an uncaught exception', { err });

		asyncEach(stack, function getContentInfo(item, cb) {
			// exclude core node modules and node modules
			if (
				(item.fileName) &&
				(item.fileName.indexOf(sep) !== -1) &&
				!/node_modules/.test(item.fileName)
				!/internal/.test(item.fileName)
			) {
				fs.readFile(item.fileName, 'utf-8', function(err, content) {
					if (err) {
						return cb(err);
					}

					content = hljs.highlight('javascript', content);

					// start a few lines before the error or at the beginning of the file
					var start = Math.max(item.lineNumber - 11, 0);
					var lines = content.value.split('\n').map((line) => {
						return '' + line + '';
					});
					// end a few lines after the error or the last line of the file
					var end = Math.min(item.lineNumber + 10, lines.length);
					var snippet = lines.slice(start, end);
					// array starts at 0 but lines numbers begin with 1, so we have to
					// subtract 1 to get the error line position in the array
					var errLine = item.lineNumber - start - 1;

					snippet[errLine] = snippet[errLine].replace('', '');

					item.content = snippet.join('\n');
					item.errLine = errLine;
					item.startLine = start;
					item.id = 'file-' + fileIndex;

					fileIndex++;

					cb(null, item);
				});
			} else {
				cb();
			}
		}, (e, items) => {
			if (!Array.isArray(items)) {
				items = [];
			}

			items = items.filter((item) => {
				return !!item;
			});

			// if something bad happened while processing the stacktrace
			// make sure to return something useful
			if (e) {
				logger.error('Failed to display error page', { e });
				res.send(err.stack);
			} else {
				res.send(errorView(err, items));
			}
		});
	};

	var _initApp = (req, res, appMain) => {
		var bootConfig = _getBootConfig(req, res);
		var app = instanceRecycler.getInstance();

		Object.assign(
			bootConfig,
			appMain.getInitialAppConfigFunctions(),
			appMain.ima.getInitialPluginConfig(),
			appMain.ima.getInitialImaConfigFunctions()
		);
		app.bootstrap
			.run(bootConfig);

		return app;
	};

	var showStaticErrorPage = (error, req, res) => {
		logger.error('Failed to display error page, displaying the static error page', { error });

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/error.html', 'utf-8', (e, content) => {
				var status = 500;
				res.status(status);

				if (e) {
					res.send('500');
					reject(e);
				}

				res.send(content);

				resolve({ content, status, error });
			});
		});
	};

	var showStaticSPAPage = (req, res) => {
		var bootConfig = _getBootConfig(req, res);
		var status = 200;

		var cacheKey = [
			bootConfig.settings.$Protocol,
			bootConfig.settings.$Language,
			bootConfig.settings.$Host,
			bootConfig.settings.$Root,
			bootConfig.settings.$LanguagePartPath
		].join('|');
		if (renderedSPAs[cacheKey]) {
			res.status(status);
			res.send(renderedSPAs[cacheKey]);

			return Promise.resolve({
				content: renderedSPAs[cacheKey],
				status,
				SPA: true
			});
		}

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/spa.html', 'utf-8', (error, content) => {
				if (error) {
					showStaticErrorPage(error, req, res);
					reject(error);
				} else {
					content = templateProcessor(content, bootConfig.settings);

					renderedSPAs[cacheKey] = content;

					res.status(status);
					res.send(content);

					resolve({ content, status, SPA: true, error: null });
				}
			});
		});
	};

	var _haveToServeSPA = (req, app) => {
		var userAgent = req.headers['user-agent'] || '';
		var isAllowedServeSPA = environment.$Server.serveSPA.allow;
		var isServerBusy = instanceRecycler.isReachToMaxConcurrencyRequests();
		var isAllowedUserAgent = !environment.$Server.serveSPA.blackListReg.test(userAgent);
		var canBeRouteServeAsSPA = true;
		var router = app.oc.get('$Router');

		try {
			var routeInfo = router.getCurrentRouteInfo();
			var routeOptions = routeInfo.route.getOptions();

			if (routeOptions.serveSPA === false) {
				canBeRouteServeAsSPA = false;
			}
		} catch (e) {}

		return isAllowedServeSPA && isServerBusy && isAllowedUserAgent && canBeRouteServeAsSPA;
	};

	var _getBootConfig = (req, res) => {
		var language = res.locals.language;
		var languagePartPath = res.locals.languagePartPath;
		var host = res.locals.host;
		var root = res.locals.root;
		var protocol = res.locals.protocol;

		var dictionary = languageLoader(language);

		var bootConfig = {
			services: {
				request: req,
				response: res,
				$IMA: {},
				dictionary: {
					$Language: language,
					dictionary: dictionary
				},
				router: {
					$Protocol: protocol,
					$Host: host,
					$Root: root,
					$LanguagePartPath: languagePartPath
				}
			},
			settings: {
				$Debug: environment.$Debug,
				$Env: environment.$Env,
				$Version: environment.$Version,
				$App: environment.$App || {},
				$Protocol: protocol,
				$Language: language,
				$Host: host,
				$Root: root,
				$LanguagePartPath: languagePartPath
			}
		};

		return bootConfig;
	};

	var _applyError = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleError({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((fatalError) => {
					showStaticErrorPage(fatalError, req, res);
					instanceRecycler.clearInstance(app);

					return Promise.reject(fatalError);
				});
		} catch (e) {
			showStaticErrorPage(e, req, res);
			instanceRecycler.clearInstance(app);
			promise = Promise.reject(e);
		}

		return promise;
	};

	var _applyNotFound = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleNotFound({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return _applyError(error, req, res, app);
				});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _applyRedirect = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			app.oc.get('$Router').redirect(error.getParams().url, { httpStatus: error.getHttpStatus() });
			instanceRecycler.clearInstance(app);
			promise = Promise.resolve({
				content: null,
				status: error.getHttpStatus(),
				error: error
			});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _importAppMain = () => {
		return $IMA.Loader
			.import('app/main')
			.then((appMain) => {
				if (!instanceRecycler.isInitialized()) {
					instanceRecycler.init(appMain.ima.createImaApp, environment.$Server.concurrency);
				}

				return appMain;
			});
	};

	var errorHandler = (error, req, res, app) => {
		var returnPromise = Promise.reject(error);

		if (environment.$Debug) {

			if (app) {
				instanceRecycler.clearInstance(app);
			}

			_displayDetails(error, req, res);
		} else {
			var appPromise = Promise.resolve(app);

			if (!app) {
				appPromise = _importAppMain()
					.then((appMain) => {
						return _initApp(req, res, appMain);
					});
			}

			returnPromise = appPromise
				.then((app) => {
					var router = app.oc.get('$Router');
					app.oc.get('$Cache').clear();

					if (router.isClientError(error)) {
						return _applyNotFound(error, req, res, app);
					} else if (router.isRedirection(error)) {
						return _applyRedirect(error, req, res, app);
					} else {
						return _applyError(error, req, res, app);
					}
				})
				.catch((e) => {
					showStaticErrorPage(e, req, res);

					instanceRecycler.clearInstance(app);
					returnPromise = Promise.reject(e);
				});
		}

		return returnPromise;
	};

	var _generateResponse = (req, res, app) => {
		var returnPromise = Promise.reject(new Error());
		var router = app.oc.get('$Router');

		try {
			returnPromise = router
				.route(router.getPath())
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return errorHandler(error, req, res, app);
				});
		} catch (e) {
			returnPromise = errorHandler(e, req, res, app);
		}

		return returnPromise;
	};

	var requestHandler = (req, res) => {
		if (environment.$Env === 'dev') {
			appFactory();
			instanceRecycler.clear();
		}

		return _importAppMain()
			.then((appMain) => {
				var app = _initApp(req, res, appMain);

				if (_haveToServeSPA(req, app)) {
					instanceRecycler.clearInstance(app);
					return showStaticSPAPage(req, res);
				}

				return _generateResponse(req, res, app);
			});
	};

	return {
		errorHandler,
		requestHandler,
		showStaticErrorPage,
		showStaticSPAPage
	};
});
<|endoftext|>


<commit_before>var fs = require('fs');
var stackTrace = require('stack-trace');
var asyncEach = require('async-each');
var hljs = require('highlight.js');
var sep = require('path').sep;
var errorView = require('./template/errorView.js');
var instanceRecycler = require('./instanceRecycler.js');
var helper = require('ima-helpers');
var templateProcessor = require('./templateProcessor.js');

var renderedSPAs = {};

hljs.configure({
	tabReplace: '  ',
	lineNodes: true
});

module.exports = ((environment, logger, languageLoader, appFactory) => {
	appFactory();

	var _displayDetails = (err, req, res) => {
		var stack = stackTrace.parse(err);
		var fileIndex = 1;

		logger.error('The application crashed due to an uncaught exception', { err });

		asyncEach(stack, function getContentInfo(item, cb) {
			// exclude core node modules and node modules
			if (
				(item.fileName) &&
				(item.fileName.indexOf(sep) !== -1) &&
				!/node_modules/.test(item.fileName) &&
				!/internal/.test(item.fileName)
			) {
				fs.readFile(item.fileName, 'utf-8', function(err, content) {
					if (err) {
						return cb(err);
					}

					content = hljs.highlight('javascript', content);

					// start a few lines before the error or at the beginning of the file
					var start = Math.max(item.lineNumber - 11, 0);
					var lines = content.value.split('\n').map((line) => {
						return '' + line + '';
					});
					// end a few lines after the error or the last line of the file
					var end = Math.min(item.lineNumber + 10, lines.length);
					var snippet = lines.slice(start, end);
					// array starts at 0 but lines numbers begin with 1, so we have to
					// subtract 1 to get the error line position in the array
					var errLine = item.lineNumber - start - 1;

					snippet[errLine] = snippet[errLine].replace('', '');

					item.content = snippet.join('\n');
					item.errLine = errLine;
					item.startLine = start;
					item.id = 'file-' + fileIndex;

					fileIndex++;

					cb(null, item);
				});
			} else {
				cb();
			}
		}, (e, items) => {
			if (!Array.isArray(items)) {
				items = [];
			}

			items = items.filter((item) => {
				return !!item;
			});

			// if something bad happened while processing the stacktrace
			// make sure to return something useful
			if (e) {
				logger.error('Failed to display error page', { e });
				res.send(err.stack);
			} else {
				res.send(errorView(err, items));
			}
		});
	};

	var _initApp = (req, res, appMain) => {
		var bootConfig = _getBootConfig(req, res);
		var app = instanceRecycler.getInstance();

		Object.assign(
			bootConfig,
			appMain.getInitialAppConfigFunctions(),
			appMain.ima.getInitialPluginConfig(),
			appMain.ima.getInitialImaConfigFunctions()
		);
		app.bootstrap
			.run(bootConfig);

		return app;
	};

	var showStaticErrorPage = (error, req, res) => {
		logger.error('Failed to display error page, displaying the static error page', { error });

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/error.html', 'utf-8', (e, content) => {
				var status = 500;
				res.status(status);

				if (e) {
					res.send('500');
					reject(e);
				}

				res.send(content);

				resolve({ content, status, error });
			});
		});
	};

	var showStaticSPAPage = (req, res) => {
		var bootConfig = _getBootConfig(req, res);
		var status = 200;

		var cacheKey = [
			bootConfig.settings.$Protocol,
			bootConfig.settings.$Language,
			bootConfig.settings.$Host,
			bootConfig.settings.$Root,
			bootConfig.settings.$LanguagePartPath
		].join('|');
		if (renderedSPAs[cacheKey]) {
			res.status(status);
			res.send(renderedSPAs[cacheKey]);

			return Promise.resolve({
				content: renderedSPAs[cacheKey],
				pageState: {},
				status,
				SPA: true
			});
		}

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/spa.html', 'utf-8', (error, content) => {
				if (error) {
					showStaticErrorPage(error, req, res);
					reject(error);
				} else {
					content = templateProcessor(content, bootConfig.settings);

					renderedSPAs[cacheKey] = content;

					res.status(status);
					res.send(content);

					resolve({ content, status, SPA: true, error: null });
				}
			});
		});
	};

	var _haveToServeSPA = (req, app) => {
		var userAgent = req.headers['user-agent'] || '';
		var isAllowedServeSPA = environment.$Server.serveSPA.allow;
		var isServerBusy = instanceRecycler.isReachToMaxConcurrencyRequests();
		var isAllowedUserAgent = !environment.$Server.serveSPA.blackListReg.test(userAgent);
		var canBeRouteServeAsSPA = true;
		var routeInfo = _getRouteInfo(app);

		if (routeInfo && routeInfo.route.getOptions().allowSPA === false) {
			canBeRouteServeAsSPA = false;
		}

		return isAllowedServeSPA && isServerBusy && isAllowedUserAgent && canBeRouteServeAsSPA;
	};

	var _getBootConfig = (req, res) => {
		var language = res.locals.language;
		var languagePartPath = res.locals.languagePartPath;
		var host = res.locals.host;
		var root = res.locals.root;
		var protocol = res.locals.protocol;

		var dictionary = languageLoader(language);

		var bootConfig = {
			services: {
				request: req,
				response: res,
				$IMA: {},
				dictionary: {
					$Language: language,
					dictionary: dictionary
				},
				router: {
					$Protocol: protocol,
					$Host: host,
					$Root: root,
					$LanguagePartPath: languagePartPath
				}
			},
			settings: {
				$Debug: environment.$Debug,
				$Env: environment.$Env,
				$Version: environment.$Version,
				$App: environment.$App || {},
				$Protocol: protocol,
				$Language: language,
				$Host: host,
				$Root: root,
				$LanguagePartPath: languagePartPath
			}
		};

		return bootConfig;
	};

	var _applyError = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleError({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((fatalError) => {
					showStaticErrorPage(fatalError, req, res);
					instanceRecycler.clearInstance(app);

					return Promise.reject(fatalError);
				});
		} catch (e) {
			showStaticErrorPage(e, req, res);
			instanceRecycler.clearInstance(app);
			promise = Promise.reject(e);
		}

		return promise;
	};

	var _applyNotFound = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleNotFound({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return _applyError(error, req, res, app);
				});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _applyRedirect = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			app.oc.get('$Router').redirect(error.getParams().url, { httpStatus: error.getHttpStatus() });
			instanceRecycler.clearInstance(app);
			promise = Promise.resolve({
				content: null,
				pageState: {}
				status: error.getHttpStatus(),
				error: error
			});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _importAppMain = () => {
		return $IMA.Loader
			.import('app/main')
			.then((appMain) => {
				if (!instanceRecycler.isInitialized()) {
					instanceRecycler.init(appMain.ima.createImaApp, environment.$Server.concurrency);
				}

				return appMain;
			});
	};

	var errorHandler = (error, req, res, app) => {
		var returnPromise;

		if (environment.$Debug) {

			if (app) {
				instanceRecycler.clearInstance(app);
			}

			returnPromise = Promise.reject(error);
			_displayDetails(error, req, res);
		} else {
			var appPromise = Promise.resolve(app);

			if (!app) {
				appPromise = _importAppMain()
					.then((appMain) => {
						return _initApp(req, res, appMain);
					});
			}

			returnPromise = appPromise
				.then((app) => {
					var router = app.oc.get('$Router');
					app.oc.get('$Cache').clear();

					if (router.isClientError(error)) {
						return _applyNotFound(error, req, res, app);
					} else if (router.isRedirection(error)) {
						return _applyRedirect(error, req, res, app);
					} else {
						return _applyError(error, req, res, app);
					}
				})
				.catch((e) => {
					showStaticErrorPage(e, req, res);

					instanceRecycler.clearInstance(app);
					returnPromise = Promise.reject(e);
				});
		}

		return returnPromise;
	};

	var _getRouteInfo = (app) => {
		var router = app.oc.get('$Router');
		var routeInfo = null;

		try {
			routeInfo = router.getCurrentRouteInfo();
		} catch (e) {}

		return routeInfo;
	};

	var _addImaToResponse = (req, res, app) => {
		var routeName = 'other';
		var routeInfo = _getRouteInfo(app);

		if (routeInfo) {
			routeName = routeInfo.route.getName();
		}

		res.$IMA = res.$IMA || { routeName };
	};

	var _generateResponse = (req, res, app) => {
		var returnPromise;
		var router = app.oc.get('$Router');

		try {
			returnPromise = router
				.route(router.getPath())
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return errorHandler(error, req, res, app);
				});
		} catch (e) {
			returnPromise = errorHandler(e, req, res, app);
		}

		return returnPromise;
	};

	var requestHandler = (req, res) => {
		if (environment.$Env === 'dev') {
			appFactory();
			instanceRecycler.clear();
		}

		return _importAppMain()
			.then((appMain) => {
				var app = _initApp(req, res, appMain);
				_addImaToResponse(req, res, app);

				if (_haveToServeSPA(req, app)) {
					instanceRecycler.clearInstance(app);
					return showStaticSPAPage(req, res);
				}

				return _generateResponse(req, res, app);
			});
	};

	return {
		errorHandler,
		requestHandler,
		showStaticErrorPage,
		showStaticSPAPage
	};
});
<commit_msg>#! fix missing comma<commit_after>var fs = require('fs');
var stackTrace = require('stack-trace');
var asyncEach = require('async-each');
var hljs = require('highlight.js');
var sep = require('path').sep;
var errorView = require('./template/errorView.js');
var instanceRecycler = require('./instanceRecycler.js');
var helper = require('ima-helpers');
var templateProcessor = require('./templateProcessor.js');

var renderedSPAs = {};

hljs.configure({
	tabReplace: '  ',
	lineNodes: true
});

module.exports = ((environment, logger, languageLoader, appFactory) => {
	appFactory();

	var _displayDetails = (err, req, res) => {
		var stack = stackTrace.parse(err);
		var fileIndex = 1;

		logger.error('The application crashed due to an uncaught exception', { err });

		asyncEach(stack, function getContentInfo(item, cb) {
			// exclude core node modules and node modules
			if (
				(item.fileName) &&
				(item.fileName.indexOf(sep) !== -1) &&
				!/node_modules/.test(item.fileName) &&
				!/internal/.test(item.fileName)
			) {
				fs.readFile(item.fileName, 'utf-8', function(err, content) {
					if (err) {
						return cb(err);
					}

					content = hljs.highlight('javascript', content);

					// start a few lines before the error or at the beginning of the file
					var start = Math.max(item.lineNumber - 11, 0);
					var lines = content.value.split('\n').map((line) => {
						return '' + line + '';
					});
					// end a few lines after the error or the last line of the file
					var end = Math.min(item.lineNumber + 10, lines.length);
					var snippet = lines.slice(start, end);
					// array starts at 0 but lines numbers begin with 1, so we have to
					// subtract 1 to get the error line position in the array
					var errLine = item.lineNumber - start - 1;

					snippet[errLine] = snippet[errLine].replace('', '');

					item.content = snippet.join('\n');
					item.errLine = errLine;
					item.startLine = start;
					item.id = 'file-' + fileIndex;

					fileIndex++;

					cb(null, item);
				});
			} else {
				cb();
			}
		}, (e, items) => {
			if (!Array.isArray(items)) {
				items = [];
			}

			items = items.filter((item) => {
				return !!item;
			});

			// if something bad happened while processing the stacktrace
			// make sure to return something useful
			if (e) {
				logger.error('Failed to display error page', { e });
				res.send(err.stack);
			} else {
				res.send(errorView(err, items));
			}
		});
	};

	var _initApp = (req, res, appMain) => {
		var bootConfig = _getBootConfig(req, res);
		var app = instanceRecycler.getInstance();

		Object.assign(
			bootConfig,
			appMain.getInitialAppConfigFunctions(),
			appMain.ima.getInitialPluginConfig(),
			appMain.ima.getInitialImaConfigFunctions()
		);
		app.bootstrap
			.run(bootConfig);

		return app;
	};

	var showStaticErrorPage = (error, req, res) => {
		logger.error('Failed to display error page, displaying the static error page', { error });

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/error.html', 'utf-8', (e, content) => {
				var status = 500;
				res.status(status);

				if (e) {
					res.send('500');
					reject(e);
				}

				res.send(content);

				resolve({ content, status, error });
			});
		});
	};

	var showStaticSPAPage = (req, res) => {
		var bootConfig = _getBootConfig(req, res);
		var status = 200;

		var cacheKey = [
			bootConfig.settings.$Protocol,
			bootConfig.settings.$Language,
			bootConfig.settings.$Host,
			bootConfig.settings.$Root,
			bootConfig.settings.$LanguagePartPath
		].join('|');
		if (renderedSPAs[cacheKey]) {
			res.status(status);
			res.send(renderedSPAs[cacheKey]);

			return Promise.resolve({
				content: renderedSPAs[cacheKey],
				pageState: {},
				status,
				SPA: true
			});
		}

		return new Promise((resolve, reject) => {
			fs.readFile('./build/static/html/spa.html', 'utf-8', (error, content) => {
				if (error) {
					showStaticErrorPage(error, req, res);
					reject(error);
				} else {
					content = templateProcessor(content, bootConfig.settings);

					renderedSPAs[cacheKey] = content;

					res.status(status);
					res.send(content);

					resolve({ content, status, SPA: true, error: null });
				}
			});
		});
	};

	var _haveToServeSPA = (req, app) => {
		var userAgent = req.headers['user-agent'] || '';
		var isAllowedServeSPA = environment.$Server.serveSPA.allow;
		var isServerBusy = instanceRecycler.isReachToMaxConcurrencyRequests();
		var isAllowedUserAgent = !environment.$Server.serveSPA.blackListReg.test(userAgent);
		var canBeRouteServeAsSPA = true;
		var routeInfo = _getRouteInfo(app);

		if (routeInfo && routeInfo.route.getOptions().allowSPA === false) {
			canBeRouteServeAsSPA = false;
		}

		return isAllowedServeSPA && isServerBusy && isAllowedUserAgent && canBeRouteServeAsSPA;
	};

	var _getBootConfig = (req, res) => {
		var language = res.locals.language;
		var languagePartPath = res.locals.languagePartPath;
		var host = res.locals.host;
		var root = res.locals.root;
		var protocol = res.locals.protocol;

		var dictionary = languageLoader(language);

		var bootConfig = {
			services: {
				request: req,
				response: res,
				$IMA: {},
				dictionary: {
					$Language: language,
					dictionary: dictionary
				},
				router: {
					$Protocol: protocol,
					$Host: host,
					$Root: root,
					$LanguagePartPath: languagePartPath
				}
			},
			settings: {
				$Debug: environment.$Debug,
				$Env: environment.$Env,
				$Version: environment.$Version,
				$App: environment.$App || {},
				$Protocol: protocol,
				$Language: language,
				$Host: host,
				$Root: root,
				$LanguagePartPath: languagePartPath
			}
		};

		return bootConfig;
	};

	var _applyError = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleError({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((fatalError) => {
					showStaticErrorPage(fatalError, req, res);
					instanceRecycler.clearInstance(app);

					return Promise.reject(fatalError);
				});
		} catch (e) {
			showStaticErrorPage(e, req, res);
			instanceRecycler.clearInstance(app);
			promise = Promise.reject(e);
		}

		return promise;
	};

	var _applyNotFound = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			promise = app.oc.get('$Router')
				.handleNotFound({ error })
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return _applyError(error, req, res, app);
				});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _applyRedirect = (error, req, res, app) => {
		var promise = Promise.reject(error);

		try {
			app.oc.get('$Router').redirect(error.getParams().url, { httpStatus: error.getHttpStatus() });
			instanceRecycler.clearInstance(app);
			promise = Promise.resolve({
				content: null,
				pageState: {},
				status: error.getHttpStatus(),
				error: error
			});
		} catch (e) {
			promise = _applyError(e, req, res, app);
		}

		return promise;
	};

	var _importAppMain = () => {
		return $IMA.Loader
			.import('app/main')
			.then((appMain) => {
				if (!instanceRecycler.isInitialized()) {
					instanceRecycler.init(appMain.ima.createImaApp, environment.$Server.concurrency);
				}

				return appMain;
			});
	};

	var errorHandler = (error, req, res, app) => {
		var returnPromise;

		if (environment.$Debug) {

			if (app) {
				instanceRecycler.clearInstance(app);
			}

			returnPromise = Promise.reject(error);
			_displayDetails(error, req, res);
		} else {
			var appPromise = Promise.resolve(app);

			if (!app) {
				appPromise = _importAppMain()
					.then((appMain) => {
						return _initApp(req, res, appMain);
					});
			}

			returnPromise = appPromise
				.then((app) => {
					var router = app.oc.get('$Router');
					app.oc.get('$Cache').clear();

					if (router.isClientError(error)) {
						return _applyNotFound(error, req, res, app);
					} else if (router.isRedirection(error)) {
						return _applyRedirect(error, req, res, app);
					} else {
						return _applyError(error, req, res, app);
					}
				})
				.catch((e) => {
					showStaticErrorPage(e, req, res);

					instanceRecycler.clearInstance(app);
					returnPromise = Promise.reject(e);
				});
		}

		return returnPromise;
	};

	var _getRouteInfo = (app) => {
		var router = app.oc.get('$Router');
		var routeInfo = null;

		try {
			routeInfo = router.getCurrentRouteInfo();
		} catch (e) {}

		return routeInfo;
	};

	var _addImaToResponse = (req, res, app) => {
		var routeName = 'other';
		var routeInfo = _getRouteInfo(app);

		if (routeInfo) {
			routeName = routeInfo.route.getName();
		}

		res.$IMA = res.$IMA || { routeName };
	};

	var _generateResponse = (req, res, app) => {
		var returnPromise;
		var router = app.oc.get('$Router');

		try {
			returnPromise = router
				.route(router.getPath())
				.then((response) => {
					instanceRecycler.clearInstance(app);

					return response;
				})
				.catch((error) => {
					return errorHandler(error, req, res, app);
				});
		} catch (e) {
			returnPromise = errorHandler(e, req, res, app);
		}

		return returnPromise;
	};

	var requestHandler = (req, res) => {
		if (environment.$Env === 'dev') {
			appFactory();
			instanceRecycler.clear();
		}

		return _importAppMain()
			.then((appMain) => {
				var app = _initApp(req, res, appMain);
				_addImaToResponse(req, res, app);

				if (_haveToServeSPA(req, app)) {
					instanceRecycler.clearInstance(app);
					return showStaticSPAPage(req, res);
				}

				return _generateResponse(req, res, app);
			});
	};

	return {
		errorHandler,
		requestHandler,
		showStaticErrorPage,
		showStaticSPAPage
	};
});
<|endoftext|>


<commit_before>module.exports = function cloneDeep(o) {
    var newO,
        i;

    if (typeof o !== 'object') {
        return o;
    }
    if (!o) {
        return o;
    }

    if ('[object Array]' === Object.prototype.toString.apply(o)) {
        newO = [];
        for (i = 0; i < o.length; i += 1) {
            newO[i] = cloneDeep(o[i]);
        }
        return newO;
    }

    newO = {};
    for (i in o) {
        if (o.hasOwnProperty(i)) {
            newO[i] = cloneDeep(o[i]);
        }
    }
    return newO;
};
<commit_msg>Micro perf enhancement (#49)<commit_after>var isA = require('./isA');

module.exports = function cloneDeep(o) {
    var newO;

    if (!o || typeof o !== 'object') {
        return o;
    }

    if (isA(o, Array)) {
        newO = [];
        for (var i = 0, j = o.length; i < j; i += 1) {
            newO[i] = cloneDeep(o[i]);
        }
        return newO;
    }

    newO = {};
    for (i in o) {
        if (o.hasOwnProperty(i)) {
            newO[i] = cloneDeep(o[i]);
        }
    }
    return newO;
};
<|endoftext|>


<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var _ = require('underscore');
var fs = require('fs');
var path = require('path');

var utils = require('./util/utils.js');

function CmdLoader(topCmd, mode) {
  this.topCmd = topCmd;
  this.cmdMode = mode;
  this.cmdMetadataFile = path.join(__dirname, 'plugins.' + this.cmdMode  + '.json');
  this.cmdBasePath = __dirname;
}

_.extend(CmdLoader.prototype, {
  harvestPlugins: function (topLevelOnly) {
    var self = this;
    var plugins = this._loadCmdsFromFolder(path.join(this.cmdBasePath, 'commands'), false);
    plugins.forEach(function (plugin) { plugin.init(self.topCmd); });

    if (!topLevelOnly) {
      // Load mode specific plugins
      var modePlugins = this._loadCmdsFromFolder(path.join(this.cmdBasePath, 'commands', this.cmdMode), true);
      modePlugins.forEach(function (plugin) { plugin.init(self.topCmd); });
    }
  },

  harvestModules: function () {
    var self = this;

    var basePath = path.dirname(__filename);

    var walkPath = path.join(basePath, '../node_modules');
    var harvestPaths = [walkPath];

    while (path.basename(walkPath) === 'node_modules' && path.dirname(walkPath) !== 'npm') {
      var nextPath = path.join(walkPath, '../..');
      if (nextPath === walkPath) {
        break;
      }
      harvestPaths.push(nextPath);
      walkPath = nextPath;
    }

    var modules = [];
    harvestPaths.forEach(function (harvestPath) {
      modules = modules.concat(self._loadCmdsFromNodeModules(harvestPath));
    });

    modules.forEach(function (module) {
      module.plugin.init(self.topCmd);
    });
  },

  initFromCmdMetadata: function (AzureCli) {
    var self = this;
    var initCategory = function (category, parent) {
      function process(entity, entityParent) {
        var newEntity = new AzureCli(entity.name, entityParent);

        if (entity.description) {
          newEntity._description = entity.description;
        }
        newEntity.description = function () {
          return newEntity._description;
        };

        newEntity.fullName = function () {
          return entity.fullName;//TODO: why this field is treated differently?
        };

        newEntity._usage = entity.usage;
        newEntity.usage = function () {
          return newEntity._usage;
        };

        newEntity.filePath = entity.filePath ?
          path.resolve(self.cmdBasePath, path.normalize(entity.filePath)) : entity.filePath;
        newEntity.stub = true;//TODO needed?

        if (entity.options) {
          for (var o in entity.options) {
            newEntity.option(entity.options[o].flags, entity.options[o].description);
          }
        }

        return newEntity;
      }

      var newCategory = category;
      //can't invoke "process" for top category, which would new up a top AzureCli and
      //get us into an infinite loop.
      if (parent) {
        newCategory = process(category, parent);
      }

      for (var i = 0 ; i < category.commands.length; i++) {
        newCategory.commands[i] = (process(category.commands[i], newCategory));
      }

      if (!newCategory.categories) {
        newCategory.categories = {};
      }

      for (var j in category.categories) {
        newCategory.categories[j] = initCategory(category.categories[j], newCategory);
      }

      return newCategory;
    };

    var data = fs.readFileSync(this.cmdMetadataFile);
    var cachedPlugins = JSON.parse(data);
    var plugins = initCategory(cachedPlugins);

    this.topCmd.commands = plugins.commands;
    this.topCmd.categories = plugins.categories;
  },

  saveCmdMetadata: function () {
    var metadate = this._serializeCategory(this.topCmd);
    fs.writeFileSync(this.cmdMetadataFile, JSON.stringify(metadate));
  },

  cmdMetadataExists: function () {
    return utils.pathExistsSync(this.cmdMetadataFile);
  },

  _loadCmdsFromFolder: function (scanPath, recursively) {
    var results = utils.getFiles(scanPath, recursively);

    results = results.filter(function (filePath) {
      var extname = path.extname(filePath);
      if (filePath.substring(0, 5) === 'tmp--') {
        return false;
      } else if (extname !== '.js' && extname !== '._js') {
        //Skip unrelated/temp files
        return false;
      }
      return true;
    });

    if (process.env.PRECOMPILE_STREAMLINE_FILES) {
      results = results.filter(function (filePath) {
        if (filePath.substring(filePath.length - 4) === '._js') {
          return false;
        }
        return true;
      });
    }

    // sort them so they load in a predictable order
    results = results.sort();

    // skip directories
    results = results.filter(function (filePath) {
      return fs.statSync(filePath).isFile();
    });

    // load modules
    results = results.map(function (filePath) {
      return require(filePath);
    });

    // look for exports.init
    results = results.filter(function (entry) {
      return entry.init !== undefined;
    });
    return results;
  },

  _loadCmdsFromNodeModules: function (scanPath) {
    var results = fs.readdirSync(scanPath);

    results = results.map(function (moduleName) {
      return {
        moduleName: moduleName,
        modulePath: path.join(scanPath, moduleName)
      };
    });

    results = results.filter(function (item) {
      try {
        item.moduleStat = fs.statSync(item.modulePath);
      } catch (error) {
        return false;
      }
      return item.moduleStat.isDirectory();
    });

    results = results.filter(function (item) {
      item.packagePath = path.join(item.modulePath, 'package.json');
      item.packageStat = utils.pathExistsSync(item.packagePath) ? fs.statSync(item.packagePath) : undefined;
      return item.packageStat && item.packageStat.isFile();
    });

    results = results.filter(function (item) {
      try {
        item.packageInfo = JSON.parse(fs.readFileSync(item.packagePath));
        return item.packageInfo && item.packageInfo.plugins && item.packageInfo.plugins['azure-cli'];
      }
        catch (err) {
        return false;
      }
    });

    results = this._flatten(results.map(function (item) {
      var plugins = item.packageInfo.plugins['azure-cli'];
      if (!_.isArray(plugins)) {
        plugins = [plugins];
      }

      return plugins.map(function (relativePath) {
        return {
          context: item,
          pluginPath: path.join(item.modulePath, relativePath)
        };
      });
    }));

    results = results.filter(function (item) {
      item.plugin = require(item.pluginPath);
      return item.plugin.init;
    });

    return results;
  },

  _flatten: function (arrays) {
    var result = [];
    arrays.forEach(function (array) {
      result = result.concat(array);
    });
    return result;
  },

  _serializeIndividualEntity: function (entity) {
    //TODO: split out the concept of category or command
    var cmdOrCat = {};

    if (entity.name) {
      cmdOrCat.name = entity.name;
    }

    if (entity.description) {
      cmdOrCat.description = entity.description();
    }

    if (entity.fullName) {
      cmdOrCat.fullName = entity.fullName();
    }

    if (entity.usage) {
      cmdOrCat.usage = entity.usage();
    }

    if (entity.filePath) {
      cmdOrCat.filePath = path.relative(this.cmdBasePath, entity.filePath);
    }

    if (entity.options) {
      cmdOrCat.options = entity.options;
    }

    return cmdOrCat;
  },

  _serializeCategory: function (category) {
    var cat = this._serializeIndividualEntity(category);
    cat.commands = [];
    cat.categories = {};

    if (category.commands) {
      for (var i in category.commands) {
        cat.commands.push(this._serializeIndividualEntity(category.commands[i]));
      }
    }

    if (category.categories) {
      for (var j in category.categories) {
        var currentCategory = this._serializeCategory(category.categories[j]);
        cat.categories[currentCategory.name] = currentCategory;
      }
    }

    return cat;
  }

});

module.exports = CmdLoader;<commit_msg>deal with back slash in the file path<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var _ = require('underscore');
var fs = require('fs');
var path = require('path');

var utils = require('./util/utils.js');

function CmdLoader(topCmd, mode) {
  this.topCmd = topCmd;
  this.cmdMode = mode;
  this.cmdMetadataFile = path.join(__dirname, 'plugins.' + this.cmdMode  + '.json');
  this.cmdBasePath = __dirname;
}

_.extend(CmdLoader.prototype, {
  harvestPlugins: function (topLevelOnly) {
    var self = this;
    var plugins = this._loadCmdsFromFolder(path.join(this.cmdBasePath, 'commands'), false);
    plugins.forEach(function (plugin) { plugin.init(self.topCmd); });

    if (!topLevelOnly) {
      // Load mode specific plugins
      var modePlugins = this._loadCmdsFromFolder(path.join(this.cmdBasePath, 'commands', this.cmdMode), true);
      modePlugins.forEach(function (plugin) { plugin.init(self.topCmd); });
    }
  },

  harvestModules: function () {
    var self = this;

    var basePath = path.dirname(__filename);

    var walkPath = path.join(basePath, '../node_modules');
    var harvestPaths = [walkPath];

    while (path.basename(walkPath) === 'node_modules' && path.dirname(walkPath) !== 'npm') {
      var nextPath = path.join(walkPath, '../..');
      if (nextPath === walkPath) {
        break;
      }
      harvestPaths.push(nextPath);
      walkPath = nextPath;
    }

    var modules = [];
    harvestPaths.forEach(function (harvestPath) {
      modules = modules.concat(self._loadCmdsFromNodeModules(harvestPath));
    });

    modules.forEach(function (module) {
      module.plugin.init(self.topCmd);
    });
  },

  initFromCmdMetadata: function (AzureCli) {
    var self = this;
    var initCategory = function (category, parent) {
      function process(entity, entityParent) {
        var newEntity = new AzureCli(entity.name, entityParent);

        if (entity.description) {
          newEntity._description = entity.description;
        }
        newEntity.description = function () {
          return newEntity._description;
        };

        newEntity.fullName = function () {
          return entity.fullName;//TODO: why this field is treated differently?
        };

        newEntity._usage = entity.usage;
        newEntity.usage = function () {
          return newEntity._usage;
        };

        newEntity.filePath = entity.filePath ?
          path.resolve(self.cmdBasePath, entity.filePath.split('\\').join('/')) : entity.filePath;
        //console.log('@@@@my normalized stuff:' + newEntity.filePath);
        //console.log('@@@@my join stuff:' + path.join(self.cmdBasePath, entity.filePath));
        newEntity.stub = true;//TODO needed?

        if (entity.options) {
          for (var o in entity.options) {
            newEntity.option(entity.options[o].flags, entity.options[o].description);
          }
        }

        return newEntity;
      }

      var newCategory = category;
      //can't invoke "process" for top category, which would new up a top AzureCli and
      //get us into an infinite loop.
      if (parent) {
        newCategory = process(category, parent);
      }

      for (var i = 0 ; i < category.commands.length; i++) {
        newCategory.commands[i] = (process(category.commands[i], newCategory));
      }

      if (!newCategory.categories) {
        newCategory.categories = {};
      }

      for (var j in category.categories) {
        newCategory.categories[j] = initCategory(category.categories[j], newCategory);
      }

      return newCategory;
    };

    var data = fs.readFileSync(this.cmdMetadataFile);
    var cachedPlugins = JSON.parse(data);
    var plugins = initCategory(cachedPlugins);

    this.topCmd.commands = plugins.commands;
    this.topCmd.categories = plugins.categories;
  },

  saveCmdMetadata: function () {
    var metadate = this._serializeCategory(this.topCmd);
    fs.writeFileSync(this.cmdMetadataFile, JSON.stringify(metadate));
  },

  cmdMetadataExists: function () {
    return utils.pathExistsSync(this.cmdMetadataFile);
  },

  _loadCmdsFromFolder: function (scanPath, recursively) {
    var results = utils.getFiles(scanPath, recursively);

    results = results.filter(function (filePath) {
      var extname = path.extname(filePath);
      if (filePath.substring(0, 5) === 'tmp--') {
        return false;
      } else if (extname !== '.js' && extname !== '._js') {
        //Skip unrelated/temp files
        return false;
      }
      return true;
    });

    if (process.env.PRECOMPILE_STREAMLINE_FILES) {
      results = results.filter(function (filePath) {
        if (filePath.substring(filePath.length - 4) === '._js') {
          return false;
        }
        return true;
      });
    }

    // sort them so they load in a predictable order
    results = results.sort();

    // skip directories
    results = results.filter(function (filePath) {
      return fs.statSync(filePath).isFile();
    });

    // load modules
    results = results.map(function (filePath) {
      return require(filePath);
    });

    // look for exports.init
    results = results.filter(function (entry) {
      return entry.init !== undefined;
    });
    return results;
  },

  _loadCmdsFromNodeModules: function (scanPath) {
    var results = fs.readdirSync(scanPath);

    results = results.map(function (moduleName) {
      return {
        moduleName: moduleName,
        modulePath: path.join(scanPath, moduleName)
      };
    });

    results = results.filter(function (item) {
      try {
        item.moduleStat = fs.statSync(item.modulePath);
      } catch (error) {
        return false;
      }
      return item.moduleStat.isDirectory();
    });

    results = results.filter(function (item) {
      item.packagePath = path.join(item.modulePath, 'package.json');
      item.packageStat = utils.pathExistsSync(item.packagePath) ? fs.statSync(item.packagePath) : undefined;
      return item.packageStat && item.packageStat.isFile();
    });

    results = results.filter(function (item) {
      try {
        item.packageInfo = JSON.parse(fs.readFileSync(item.packagePath));
        return item.packageInfo && item.packageInfo.plugins && item.packageInfo.plugins['azure-cli'];
      }
        catch (err) {
        return false;
      }
    });

    results = this._flatten(results.map(function (item) {
      var plugins = item.packageInfo.plugins['azure-cli'];
      if (!_.isArray(plugins)) {
        plugins = [plugins];
      }

      return plugins.map(function (relativePath) {
        return {
          context: item,
          pluginPath: path.join(item.modulePath, relativePath)
        };
      });
    }));

    results = results.filter(function (item) {
      item.plugin = require(item.pluginPath);
      return item.plugin.init;
    });

    return results;
  },

  _flatten: function (arrays) {
    var result = [];
    arrays.forEach(function (array) {
      result = result.concat(array);
    });
    return result;
  },

  _serializeIndividualEntity: function (entity) {
    //TODO: split out the concept of category or command
    var cmdOrCat = {};

    if (entity.name) {
      cmdOrCat.name = entity.name;
    }

    if (entity.description) {
      cmdOrCat.description = entity.description();
    }

    if (entity.fullName) {
      cmdOrCat.fullName = entity.fullName();
    }

    if (entity.usage) {
      cmdOrCat.usage = entity.usage();
    }

    if (entity.filePath) {
      cmdOrCat.filePath = path.relative(this.cmdBasePath, entity.filePath);
    }

    if (entity.options) {
      cmdOrCat.options = entity.options;
    }

    return cmdOrCat;
  },

  _serializeCategory: function (category) {
    var cat = this._serializeIndividualEntity(category);
    cat.commands = [];
    cat.categories = {};

    if (category.commands) {
      for (var i in category.commands) {
        cat.commands.push(this._serializeIndividualEntity(category.commands[i]));
      }
    }

    if (category.categories) {
      for (var j in category.categories) {
        var currentCategory = this._serializeCategory(category.categories[j]);
        cat.categories[currentCategory.name] = currentCategory;
      }
    }

    return cat;
  }

});

module.exports = CmdLoader;<|endoftext|>