<commit_before>"use strict";

var fs      = require("fs"),
    path    = require("path"),
    es      = require("event-stream"),
    plugError = require('plugin-error'),
    replaceExt = require('replace-ext'),
    glob    = require('glob'),
    frontMatter = require('front-matter'),

    _DIRECTIVE_REGEX = /^(.*=\s*([\w\.\*\/-]+)\s*:\s*([\w\.\*\/-]+\.html?\s*))$/gm
;

/**
 *
 * @param file
 * @returns {HTMLElement}
 * @private
 */
function _parse(file){

    if (fs.existsSync(file)) {
        var bufferContents = fs.readFileSync(file),
            parsed = frontMatter(bufferContents.toString().replace(/\s+/g, ' '));

        return parsed.body;

    } else {
        throw new plugError('gulp-html-to-json', 'File not found: ' + fullPath);
    }

}

var hasOwnProperty = Object.prototype.hasOwnProperty;

function isEmpty(obj) {

    // null and undefined are "empty"
    if (obj === null || typeof obj === 'undefined') return true;

    // Assume if it has a length property with a non-zero value
    // that that property is correct.
    if (obj.length > 0)    return false;
    if (obj.length === 0)  return true;

    // If it isn't an object at this point
    // it is empty, but it can't be anything *but* empty
    // Is it empty?  Depends on your application.
    if (typeof obj !== "object") return true;

    // Otherwise, does it have any properties of its own?
    // Note that this doesn't handle
    // toString and valueOf enumeration bugs in IE < 9
    for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) return false;
    }

    return true;
}

/**
 *
 * @param dirname
 * @param p
 * @return {*}
 */
function indName (dirname, p) {

    if (!isEmpty(p) && !isEmpty(p.filename)) {

        return p.filename;
    }

    var n = dirname.split('.');

    n.pop();

    var nLast = n.pop(), nArr = [];

    if (nLast.indexOf('/') >= 0) {

        nArr = nLast.split('/')

    } else {

        nArr = nLast.split('\\')
    }

    return nArr.pop();
}

/**
 *
 * @param path
 * @param fname
 * @param useAsVariable
 * @returns {*}
 */
function replaceFilename (path, fname, useAsVariable) {

    var uvar = useAsVariable || false;

    var filename = path.replace(/^.*(\\|\/|\:)/, '');
    var nFname = path.replace(filename, fname);
    var ext = (uvar) ? ".js" : '.json';
    return replaceExt(nFname,  ext);
}

/**
 * Control HTML to JSON
 * @param fileContents
 * @param filePath
 * @param output
 * @param p
 */
function htmlToJsonController (fileContents, filePath, output, p) {

    var matches;
    var includePaths = false;

    while (matches = _DIRECTIVE_REGEX.exec(fileContents)) {

        var relPath     = path.dirname(filePath),
            fullPath    = path.join(relPath,  matches[3].replace(/['"]/g, '')).trim(),
            jsonVar     = matches[2],
            extension   = matches[3].split('.').pop();

        var fileMatches = [];

        if (p.includePaths) {

            if (typeof p.includePaths == "string") {
                // Arrayify the string
                includePaths = [params.includePaths];
            }else if (Array.isArray(p.includePaths)) {
                // Set this array to the includepaths
                includePaths = p.includePaths;
            }

            var includePath = '';

            for (var y = 0; y < includePaths.length; y++) {

                includePath = path.join(includePaths[y], matches[3].replace(/['"]/g, '')).trim();

                if (fs.existsSync(includePath)) {

                    var globResults = glob.sync(includePath, {mark: true});
                    fileMatches = fileMatches.concat(globResults);

                }

            }

        } else {

            var globResults = glob.sync(fullPath, {mark: true});
            fileMatches = fileMatches.concat(globResults);

        }

        try {

            fileMatches.forEach(function(value){

                var _inc = _parse(value);

                if (_inc.length > 0) {

                    var ind = (jsonVar.trim() == '*') ? indName(value) : jsonVar;
                    output[ind] = _inc;
                }
            })

        } catch (err) {
            console.log(err)
        }

    }
}

/**
 * Make Angular Template
 * @param p
 * @param json
 * @returns {string}
 */
function angularTemplate (p, json) {

    var prefix = (p.prefix !== "") ? p.prefix + "." : "",
        tpl = 'angular.module("'+ prefix +  p.filename +'",["ng"]).run(["$templateCache",';

    tpl += 'function($templateCache) {';

    for (var key in json) {
        if (json.hasOwnProperty(key)) {
            tpl += '$templateCache.put("'+ key +'",';
            tpl += JSON.stringify(json[key]);
            tpl += ');'
        }
    }

    tpl += '}])';

    return tpl;
}

/**
 * Module Exports htmlToJson
 * @param p
 * @returns {*}
 */
module.exports = function(p) {

    var sr = false;

    p = p || {};

    if (isEmpty(p.filename)) {
        sr = true;
    }

    function htmToJson(file, callback) {

        if (file.isNull()) {
            throw new plugError('gulp-html-to-json', 'File is Null');
        }

        if (file.isStream()) {
            throw new plugError('gulp-html-to-json', 'stream not supported');
        }

        if (file.isBuffer()) {

            var outputJson = {};

            htmlToJsonController(String(file.contents), file.path, outputJson, p);

            p.filename = indName(file.path, p);
            p.prefix || (p.prefix = "");
            p.useAsVariable || (p.useAsVariable = false);
            p.isAngularTemplate || (p.isAngularTemplate = false);

            if(p.isAngularTemplate) {
                var output = angularTemplate(p, outputJson);
                file.path = replaceFilename(file.path, p.filename, p.useAsVariable);
                file.contents = new Buffer(output);

            } else {
                file.path = replaceFilename(file.path, p.filename, p.useAsVariable);

                var exVars = (p.useAsVariable) ? "var " + p.filename + "=" : "";
                file.contents = new Buffer(exVars + JSON.stringify(outputJson));
            }
        }

        if (sr) {
            delete  p['filename'];
        }

        callback(null, file);
    }

    return es.map(htmToJson)
};
<commit_msg>Add semicolon to better support minification of variable declaration<commit_after>"use strict";

var fs      = require("fs"),
    path    = require("path"),
    es      = require("event-stream"),
    plugError = require('plugin-error'),
    replaceExt = require('replace-ext'),
    glob    = require('glob'),
    frontMatter = require('front-matter'),

    _DIRECTIVE_REGEX = /^(.*=\s*([\w\.\*\/-]+)\s*:\s*([\w\.\*\/-]+\.html?\s*))$/gm
;

/**
 *
 * @param file
 * @returns {HTMLElement}
 * @private
 */
function _parse(file){

    if (fs.existsSync(file)) {
        var bufferContents = fs.readFileSync(file),
            parsed = frontMatter(bufferContents.toString().replace(/\s+/g, ' '));

        return parsed.body;

    } else {
        throw new plugError('gulp-html-to-json', 'File not found: ' + fullPath);
    }

}

var hasOwnProperty = Object.prototype.hasOwnProperty;

function isEmpty(obj) {

    // null and undefined are "empty"
    if (obj === null || typeof obj === 'undefined') return true;

    // Assume if it has a length property with a non-zero value
    // that that property is correct.
    if (obj.length > 0)    return false;
    if (obj.length === 0)  return true;

    // If it isn't an object at this point
    // it is empty, but it can't be anything *but* empty
    // Is it empty?  Depends on your application.
    if (typeof obj !== "object") return true;

    // Otherwise, does it have any properties of its own?
    // Note that this doesn't handle
    // toString and valueOf enumeration bugs in IE < 9
    for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) return false;
    }

    return true;
}

/**
 *
 * @param dirname
 * @param p
 * @return {*}
 */
function indName (dirname, p) {

    if (!isEmpty(p) && !isEmpty(p.filename)) {

        return p.filename;
    }

    var n = dirname.split('.');

    n.pop();

    var nLast = n.pop(), nArr = [];

    if (nLast.indexOf('/') >= 0) {

        nArr = nLast.split('/')

    } else {

        nArr = nLast.split('\\')
    }

    return nArr.pop();
}

/**
 *
 * @param path
 * @param fname
 * @param useAsVariable
 * @returns {*}
 */
function replaceFilename (path, fname, useAsVariable) {

    var uvar = useAsVariable || false;

    var filename = path.replace(/^.*(\\|\/|\:)/, '');
    var nFname = path.replace(filename, fname);
    var ext = (uvar) ? ".js" : '.json';
    return replaceExt(nFname,  ext);
}

/**
 * Control HTML to JSON
 * @param fileContents
 * @param filePath
 * @param output
 * @param p
 */
function htmlToJsonController (fileContents, filePath, output, p) {

    var matches;
    var includePaths = false;

    while (matches = _DIRECTIVE_REGEX.exec(fileContents)) {

        var relPath     = path.dirname(filePath),
            fullPath    = path.join(relPath,  matches[3].replace(/['"]/g, '')).trim(),
            jsonVar     = matches[2],
            extension   = matches[3].split('.').pop();

        var fileMatches = [];

        if (p.includePaths) {

            if (typeof p.includePaths == "string") {
                // Arrayify the string
                includePaths = [params.includePaths];
            }else if (Array.isArray(p.includePaths)) {
                // Set this array to the includepaths
                includePaths = p.includePaths;
            }

            var includePath = '';

            for (var y = 0; y < includePaths.length; y++) {

                includePath = path.join(includePaths[y], matches[3].replace(/['"]/g, '')).trim();

                if (fs.existsSync(includePath)) {

                    var globResults = glob.sync(includePath, {mark: true});
                    fileMatches = fileMatches.concat(globResults);

                }

            }

        } else {

            var globResults = glob.sync(fullPath, {mark: true});
            fileMatches = fileMatches.concat(globResults);

        }

        try {

            fileMatches.forEach(function(value){

                var _inc = _parse(value);

                if (_inc.length > 0) {

                    var ind = (jsonVar.trim() == '*') ? indName(value) : jsonVar;
                    output[ind] = _inc;
                }
            })

        } catch (err) {
            console.log(err)
        }

    }
}

/**
 * Make Angular Template
 * @param p
 * @param json
 * @returns {string}
 */
function angularTemplate (p, json) {

    var prefix = (p.prefix !== "") ? p.prefix + "." : "",
        tpl = 'angular.module("'+ prefix +  p.filename +'",["ng"]).run(["$templateCache",';

    tpl += 'function($templateCache) {';

    for (var key in json) {
        if (json.hasOwnProperty(key)) {
            tpl += '$templateCache.put("'+ key +'",';
            tpl += JSON.stringify(json[key]);
            tpl += ');'
        }
    }

    tpl += '}])';

    return tpl;
}

/**
 * Module Exports htmlToJson
 * @param p
 * @returns {*}
 */
module.exports = function(p) {

    var sr = false;

    p = p || {};

    if (isEmpty(p.filename)) {
        sr = true;
    }

    function htmToJson(file, callback) {

        if (file.isNull()) {
            throw new plugError('gulp-html-to-json', 'File is Null');
        }

        if (file.isStream()) {
            throw new plugError('gulp-html-to-json', 'stream not supported');
        }

        if (file.isBuffer()) {

            var outputJson = {};

            htmlToJsonController(String(file.contents), file.path, outputJson, p);

            p.filename = indName(file.path, p);
            p.prefix || (p.prefix = "");
            p.useAsVariable || (p.useAsVariable = false);
            p.isAngularTemplate || (p.isAngularTemplate = false);

            if(p.isAngularTemplate) {
                var output = angularTemplate(p, outputJson);
                file.path = replaceFilename(file.path, p.filename, p.useAsVariable);
                file.contents = new Buffer(output);

            } else {
                file.path = replaceFilename(file.path, p.filename, p.useAsVariable);

                var exVars = (p.useAsVariable) ? "var " + p.filename + "=" : "";
				var fileContents = exVars + JSON.stringify(outputJson) + p.useAsVariable ? ";" : "";
                file.contents = new Buffer(fileContents);
            }
        }

        if (sr) {
            delete  p['filename'];
        }

        callback(null, file);
    }

    return es.map(htmToJson)
};
<|endoftext|>


<commit_before>module.exports = [
    "don't worry. no one actually knows what they're doing."
  , "this is Hard Stuff, but you can do it!"
  , "getting started is hard, you did it, congratulations!"
  , "If you can see this message it means you got it working and that's something to celebrate! ['hip','hip']!"
  , "you are the best around."
  , "nothing is ever going to keep you down!"
  , "computers are super hard, but you can defeat them!"
  , "it's okay to need a break, everyone needs one!"
  , "you got this."
  , "whoah you're pretty good at this."
  , "all the cats in the land bow down to your cool computer skills."
  , "there's a brazillion different ways to solve every problem - you only need one."
  , "oh, cool! check out what you just learned there!"
  , "learning something is the first step towards being awesome at it."
  , "don't be afraid to ask questions!"
  , "rad beans! lets try another!"
  , "impressive..."
  , "this is amazing! keep with the good work"
  , "you've already defeated so many obstacles to get here—keep going! you can do it."
  , "even the most accomplished computer people in the universe all make it up as they go along! this is okay."
  , "is your code a parking ticket? because it looks _fine_."
  , "node is cool, and so are you. :sparkles:"
  , "wowowowowow you've got some kick-ass node skillz."
  , "I can't believe what I'm seeing, you're awesome."
  , "I'm pretty sure you're now fully qualified fantastic human."
  , "looks like you're pretty much awesome now."
  , "omg - I'm falling head over heels for your sweet skills."
  , "look how far you've come! you're awesome!!"
  , "the best way to learn is by doing, and you are doing it good!"
  , "¡Lo estás haciendo muy bien!"
  , "keep going! you can do it!"
  , "mistakes are how we learn. keep making them! you're doing awesome!"
  , "✧･ﾟ\: \*✧･ﾟ\:\* you are super cool! \*\:･ﾟ✧\*\:･ﾟ✧"
  , "\✲ﾟ｡\.\(✿╹◡╹\)ﾉ☆\.｡₀\:\*ﾟ\✲ﾟ\*\:₀｡wow i cant believe how awesome this is"
  , "\"No bird soars in a calm\" - Wilbur Wright"
  , "Ayyyyyyy! so much WoW! very aWesome!!!"
  , "you finished a hard problem and learned something new. doesn't it feel great?"
  , "\"Nothing is impossible, the word itself says \'I\'m possible!\'\" - Audrey Hepburn"
  , "Never leave house without your towel."
  , "If 42 doesn't answer your question, try a prime number."
  , "i'm pretty new to this."
  , "You've got 99 problems but node ain't one"
  , "Hakuna Matata!"
  , "Reboot your life, everyday"
  , "\"May the force be with you, Node\"-JavaScript"
  , "Alright!! Way to go!!"
  , “The expert at anything, was once a beginner.”
  , "les petit ruisseaux font les grandes rivieres"
  , "It's never too late!"
  , "Who's awesome? YOU'RE AWESOME!!!"
  , "did you ever know that you're my hero? you're everything i would like to be."
  , "charge forward!"
  , "one more step. you're almost there!"
  , "what lovely code you have!"
  , "Meow, you're paw-some"
  , "You totally did the thing!"
  , "Donne tout ce que tu as et dis toi que ce n'est qu'un mauvais moment à passer ! Bon courage !"
  , "Get to da choppah! Nao!"
  , "your code looks purrr-fect!"
  , "You're the best! You can do anything!"
  , "Alright , let's go"
  , "Don't hate, caffeinate"
];
<commit_msg>replace curly quotes with straight quotes<commit_after>module.exports = [
    "don't worry. no one actually knows what they're doing."
  , "this is Hard Stuff, but you can do it!"
  , "getting started is hard, you did it, congratulations!"
  , "If you can see this message it means you got it working and that's something to celebrate! ['hip','hip']!"
  , "you are the best around."
  , "nothing is ever going to keep you down!"
  , "computers are super hard, but you can defeat them!"
  , "it's okay to need a break, everyone needs one!"
  , "you got this."
  , "whoah you're pretty good at this."
  , "all the cats in the land bow down to your cool computer skills."
  , "there's a brazillion different ways to solve every problem - you only need one."
  , "oh, cool! check out what you just learned there!"
  , "learning something is the first step towards being awesome at it."
  , "don't be afraid to ask questions!"
  , "rad beans! lets try another!"
  , "impressive..."
  , "this is amazing! keep with the good work"
  , "you've already defeated so many obstacles to get here—keep going! you can do it."
  , "even the most accomplished computer people in the universe all make it up as they go along! this is okay."
  , "is your code a parking ticket? because it looks _fine_."
  , "node is cool, and so are you. :sparkles:"
  , "wowowowowow you've got some kick-ass node skillz."
  , "I can't believe what I'm seeing, you're awesome."
  , "I'm pretty sure you're now fully qualified fantastic human."
  , "looks like you're pretty much awesome now."
  , "omg - I'm falling head over heels for your sweet skills."
  , "look how far you've come! you're awesome!!"
  , "the best way to learn is by doing, and you are doing it good!"
  , "¡Lo estás haciendo muy bien!"
  , "keep going! you can do it!"
  , "mistakes are how we learn. keep making them! you're doing awesome!"
  , "✧･ﾟ\: \*✧･ﾟ\:\* you are super cool! \*\:･ﾟ✧\*\:･ﾟ✧"
  , "\✲ﾟ｡\.\(✿╹◡╹\)ﾉ☆\.｡₀\:\*ﾟ\✲ﾟ\*\:₀｡wow i cant believe how awesome this is"
  , "\"No bird soars in a calm\" - Wilbur Wright"
  , "Ayyyyyyy! so much WoW! very aWesome!!!"
  , "you finished a hard problem and learned something new. doesn't it feel great?"
  , "\"Nothing is impossible, the word itself says \'I\'m possible!\'\" - Audrey Hepburn"
  , "Never leave house without your towel."
  , "If 42 doesn't answer your question, try a prime number."
  , "i'm pretty new to this."
  , "You've got 99 problems but node ain't one"
  , "Hakuna Matata!"
  , "Reboot your life, everyday"
  , "\"May the force be with you, Node\"-JavaScript"
  , "Alright!! Way to go!!"
  , "The expert at anything, was once a beginner."
  , "les petit ruisseaux font les grandes rivieres"
  , "It's never too late!"
  , "Who's awesome? YOU'RE AWESOME!!!"
  , "did you ever know that you're my hero? you're everything i would like to be."
  , "charge forward!"
  , "one more step. you're almost there!"
  , "what lovely code you have!"
  , "Meow, you're paw-some"
  , "You totally did the thing!"
  , "Donne tout ce que tu as et dis toi que ce n'est qu'un mauvais moment à passer ! Bon courage !"
  , "Get to da choppah! Nao!"
  , "your code looks purrr-fect!"
  , "You're the best! You can do anything!"
  , "Alright , let's go"
  , "Don't hate, caffeinate"
];
<|endoftext|>


<commit_before>var promise = require( 'bluebird' );
var romis = require( 'romis' );
var fs = require( 'fs' );
var path = require( 'path' );
var EventEmitter = require( 'events' ).EventEmitter;

module.exports = storeRedis;

var ROOM_DOES_NOT_EXIST = promise.reject( 'There is no room by that id' );
ROOM_DOES_NOT_EXIST.catch( function() {} ); // this is to ensure there isnt a logged error

// Caching LUA Redis Commands
var LUA_GET_ROOM_ID = fs.readFileSync( path.join( __dirname, 'lua/getRoomID.lua' ), 'utf8' );
var LUA_GENERATE_KEYS = fs.readFileSync( path.join( __dirname, 'lua/generateKeys.lua' ), 'utf8' );

/**
 * storeRedis is used to store data in memory on one process. This is not ideal
 * for clustered servers as each process may have different data. Use another
 * memory store if your server is clustered however use this memory store just
 * to get up and running fast or if your server is not clustered.
 *
 * @param {redis} redis You should pass a redis client that will be used to write
 *                      to redis with.
 * @return {storeRedis} An new instance of storeRedis
 */
function storeRedis( redis ) {

	if( !( this instanceof storeRedis ) )
		return new storeRedis( redis );

	EventEmitter.call( this );

	this.redis = romis.fromRedis( redis );

	this.redis.on( 'message', function( channel, message ) {

		message = JSON.parse( message );

		this.emit( message.roomID, message );
	}.bind( this ));
}

var p = storeRedis.prototype = Object.create( EventEmitter.prototype );

/**
 * The init function should be called before doing anything.
 * This will setup the redis db the way it should be.
 *
 * @return {Promise} This promise will resolve once we're good to go
 */
p.init = function() {

	return this._reset()
		   .then( this._generateKeys.bind( this ) );
};

/**
 * This will create a roomID, roomDataObject, and users array for the room
 *
 * @return {Promise} This promise will return a room id once the room is created
 */
p.createRoom = function( userID ) {

	return this._getNextRoomId()
	.then( function( id ) {

		return this.joinRoom( userID, id )
		.then( function() {

			return id;
		});
	}.bind( this ));
};

/**
 * Join an existing room
 *
 * @param  {String} userID an id for the user whose joining
 * @param  {String} roomID an id for the room the user is joing
 * @return {Promise} A promise which will resolve and return a roomID
 */
p.joinRoom = function( userID, roomID ) {

	var redis = this.redis;
	var setName = 'roomUsers:' + roomID;
	var getUsers = this.getUsers.bind( this, roomID );
	var emitter = this;

	return redis.sismember( setName, userID )
	.then( function( isMember ) {

		if( !isMember ) {

			return redis.sadd( setName, userID )
			.then( getUsers )
			.then( function( users ) {

				var message = {

					roomID: roomID,
					action: 'join',
					user: userID,
					users: users
				};

				emitter.emit( roomID, message );
				redis.publish( 'user', JSON.stringify( message ) );

				return roomID;
			});
		}
	})
	.then( function() {

		return roomID;
	});
};

/**
 * Remove a user from a room
 *
 * @param  {String} userID an id for the user whose leaving
 * @param  {String} roomID an id for the room the user is leaving
 * @return {Promise} When this promise resolves it will send the number of users in the room
 */
p.leaveRoom = function( userID, roomID ) {

	var redis = this.redis;
	var setName = 'roomUsers:' + roomID;
	var getUsers = this.getUsers.bind( this, roomID );
	var emitter = this;

	return redis.sismember( setName, userID )
	.then( function() {

		return redis.srem( setName, userID )
		.then( function() {

			return getUsers()
			.then( function( users ) {

				var message = {
					roomID: roomID,
					action: 'leave',
					user: userID,
					users: users
				};

				emitter.emit( roomID, message );
				redis.publish( 'user', JSON.stringify( message ) );

				return roomID;
			});
		});
	})
	.catch( function() {

		return promise.reject( 'User ' + userID + ' is not in the room: ' + roomID );
	});
};

/**
 * Get all users for a room
 *
 * @param  {String} roomID room id where to retrieve users
 * @return {Promise} A promise is returned which will return all users in a room
 */
p.getUsers = function( roomID ) {

	var setName = 'roomUsers:' + roomID;

	return this.redis.smembers( setName );
};

/**
 * get a key which can be used to enter a room vs entering room via
 * roomID
 *
 * @param  {String} roomID id of the room you'd like a key for
 * @return {Promise} A promise will be returned which will return a roomKey on success
 */
p.getKey = function( roomID ) {

	var key;

	return this.redis.spop( 'roomKeys' )
	.then( function( nKey ) {

		key = nKey;

		if( key ) {

			return promise.all( [

				this._setRoomIdForKey( roomID, key ),
				this._setKeyForRoomID( roomID, key )
			])
			.then( function() {

				return key;
			});
		} else {

			return promise.reject( 'Run out of keys' );
		}
	}.bind( this ));
};

/**
 * return a room key so someone else can use it.
 *
 * @param  {String} roomID id of the room you'll be returning a key for
 * @param  {String} key the key you'd like to return
 * @return {Promise} This promise will succeed when the room key was returned
 */
p.returnKey = function( roomID, key ) {

	return this.getRoomIdForKey( key )
	.then( function( savedRoomId ) {

		if( savedRoomId == roomID ) {

			return this._deleteKeyIdLookups( roomID, key )
			.then( function() {

				return this.redis.sadd( 'roomKeys', key );
			}.bind( this ));
		} else {

			return promise.reject( 'roomID and roomID for key do not match' );
		}
	}.bind( this ));
};

/**
 * return the room id for the given key
 *
 * @param  {String} key key used to enter the room
 * @return {Promise} This promise will succeed with the room id and fail if no room id exists for key
 */
p.getRoomIdForKey = function( key ) {

	return this.redis.hget( 'keyForRoomID', key )
	.then( function( savedRoomId ) {

		if( savedRoomId ) {

			return promise.resolve( parseInt( savedRoomId ) );
		} else {

			return promise.reject();
		}
	});
};

/**
 * set a variable on the rooms data object
 *
 * @param {String} roomID id for the room whose
 * @param {String} key variable name/key that you want to set
 * @param {*} value Value you'd like to set for the variable
 * @return {Promise} once this promise succeeds the rooms variable will be set
 */
p.setRoomDataVar = function( roomID, key, value ) {

	var redis = this.redis;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return redis.hset( 'roomData:' + roomID, key, value )
			.then( function( countSet ) {

				return value;
			});
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	});
};

/**
 * get a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to get
 * @return {Promise} once this promise succeeds it will return the variable value it will fail if the variable does not exist
 */
p.getRoomDataVar = function( roomID, key ) {

	var redis = this.redis;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return redis.hget( 'roomData:' + roomID, key )
			.then( function( value ) {

				return value == null ? undefined : value;
			});
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	});
};

/**
 * delete a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to delete
 * @return {Promise} once this promise succeeds it will return the value that was stored before delete
 */
p.delRoomDataVar = function( roomID, key ) {

	var redis = this.redis,
		rVal;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return this.getRoomDataVar( roomID, key )
			.then( function( value ) {

				rVal = value;
			})
			.then( function() {

				return redis.hdel( 'roomData:' + roomID, key );
			})
			.then( function() {

				return rVal;
			})
			.catch();
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	}.bind( this ));
};

/**
 * Receive the data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like data for
 * @return {Promise} This promise will succeed when data is received for the room
 */
p.getRoomData = function( roomID ) {

	var redis = this.redis;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return redis.hgetall( 'roomData:' + roomID )
			.then( function( data ) {

				return data === null ? {} : data;
			});
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	});
};

/**
 * Set data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like to set data for
 * @return {Promise} This promise will succeed when data is set for the room
 */
p.setRoomData = function( roomID, data ) {

	var redis = this.redis,
		name = 'roomData:' + roomID;

	return redis.hmset( 'roomData:' + roomID, data )
	.then( function() {

		return data;
	});

	return redis.watch( name )
	.then( function() {

		return redis.multi()
		.set( name, JSON.stringify( data ) )
		.exec();
	}.bind( this ))
};

/**
 * Set room as public
 *
 * @param  {String} roomID id for the room you'd like to make public
 * @return {Promise} This promise will succeed when the room as been set as public
 */
p.makeRoomPublic = function( roomID ) {

	var redis = this.redis;

	return this.getRoomData( roomID ).then( redis.rpush( 'public', roomID ) );
};

/**
 * Set room as private, removing it from the publicly available list
 *
 * @param  {String} roomID id for the room you'd like to make private
 * @return {Promise} This promise will succeed when the room as been set as private
 */
p.makeRoomPrivate = function( roomID ) {

	var redis = this.redis;

	return redis.lrem( 'public', 0, roomID );
};

/**
 * Gets the first available public room
 *
 * @return {Promise} This promise will succeed when the room has been retrieved
 */
p.getPublicRoom = function() {

	var redis = this.redis;

	return redis.lindex( 'public', 0 );
};

/**
 * _generateKeys is a function which will create and store a set of
 * keys which can be used to enter a room instead of a room id
 *
 * @private
 */
p._generateKeys = function() {

	var redis = this.redis;

	return redis.eval( LUA_GENERATE_KEYS , 0, 5 );
};

p._doesRoomExist = function( roomID ) {

	return this.redis.scard( 'roomUsers:' + roomID )
	.then( function( countUsers ) {

		return countUsers > 0;
	});
};

p._setRoomIdForKey = function( roomID, key ) {

	return this.redis.hset( 'roomIDForKey', roomID, key );
};

p._setKeyForRoomID = function( roomID, key ) {

	return this.redis.hset( 'keyForRoomID', key, roomID );
};

p._deleteKeyIdLookups = function( roomID, key ) {

	var redis = this.redis;

	return promise.all( [

		redis.hdel( 'roomIDForKey', roomID ),
		redis.hdel( 'keyForRoomID', key )
	]);
};

p._reset = function() {

	var redis = this.redis;

	return redis.set( 'nextRoomID', 0 )
	.then( function() {

		return redis.flushdb();
	});
};

p._getNextRoomId = function() {

	var redis = this.redis;

	return redis.eval( LUA_GET_ROOM_ID , 0 );
};<commit_msg>Renamed functions to follow convention<commit_after>var promise = require( 'bluebird' );
var romis = require( 'romis' );
var fs = require( 'fs' );
var path = require( 'path' );
var EventEmitter = require( 'events' ).EventEmitter;

module.exports = storeRedis;

var ROOM_DOES_NOT_EXIST = promise.reject( 'There is no room by that id' );
ROOM_DOES_NOT_EXIST.catch( function() {} ); // this is to ensure there isnt a logged error

// Caching LUA Redis Commands
var LUA_GET_ROOM_ID = fs.readFileSync( path.join( __dirname, 'lua/getRoomID.lua' ), 'utf8' );
var LUA_GENERATE_KEYS = fs.readFileSync( path.join( __dirname, 'lua/generateKeys.lua' ), 'utf8' );

/**
 * storeRedis is used to store data in memory on one process. This is not ideal
 * for clustered servers as each process may have different data. Use another
 * memory store if your server is clustered however use this memory store just
 * to get up and running fast or if your server is not clustered.
 *
 * @param {redis} redis You should pass a redis client that will be used to write
 *                      to redis with.
 * @return {storeRedis} An new instance of storeRedis
 */
function storeRedis( redis ) {

	if( !( this instanceof storeRedis ) )
		return new storeRedis( redis );

	EventEmitter.call( this );

	this.redis = romis.fromRedis( redis );

	this.redis.on( 'message', function( channel, message ) {

		message = JSON.parse( message );

		this.emit( message.roomID, message );
	}.bind( this ));
}

var p = storeRedis.prototype = Object.create( EventEmitter.prototype );

/**
 * The init function should be called before doing anything.
 * This will setup the redis db the way it should be.
 *
 * @return {Promise} This promise will resolve once we're good to go
 */
p.init = function() {

	return this._reset()
		   .then( this._generateKeys.bind( this ) );
};

/**
 * This will create a roomID, roomDataObject, and users array for the room
 *
 * @return {Promise} This promise will return a room id once the room is created
 */
p.createRoom = function( userID ) {

	return this._getNextRoomId()
	.then( function( id ) {

		return this.joinRoom( userID, id )
		.then( function() {

			return id;
		});
	}.bind( this ));
};

/**
 * Join an existing room
 *
 * @param  {String} userID an id for the user whose joining
 * @param  {String} roomID an id for the room the user is joing
 * @return {Promise} A promise which will resolve and return a roomID
 */
p.joinRoom = function( userID, roomID ) {

	var redis = this.redis;
	var setName = 'roomUsers:' + roomID;
	var getUsers = this.getUsers.bind( this, roomID );
	var emitter = this;

	return redis.sismember( setName, userID )
	.then( function( isMember ) {

		if( !isMember ) {

			return redis.sadd( setName, userID )
			.then( getUsers )
			.then( function( users ) {

				var message = {

					roomID: roomID,
					action: 'join',
					user: userID,
					users: users
				};

				emitter.emit( roomID, message );
				redis.publish( 'user', JSON.stringify( message ) );

				return roomID;
			});
		}
	})
	.then( function() {

		return roomID;
	});
};

/**
 * Remove a user from a room
 *
 * @param  {String} userID an id for the user whose leaving
 * @param  {String} roomID an id for the room the user is leaving
 * @return {Promise} When this promise resolves it will send the number of users in the room
 */
p.leaveRoom = function( userID, roomID ) {

	var redis = this.redis;
	var setName = 'roomUsers:' + roomID;
	var getUsers = this.getUsers.bind( this, roomID );
	var emitter = this;

	return redis.sismember( setName, userID )
	.then( function() {

		return redis.srem( setName, userID )
		.then( function() {

			return getUsers()
			.then( function( users ) {

				var message = {
					roomID: roomID,
					action: 'leave',
					user: userID,
					users: users
				};

				emitter.emit( roomID, message );
				redis.publish( 'user', JSON.stringify( message ) );

				return roomID;
			});
		});
	})
	.catch( function() {

		return promise.reject( 'User ' + userID + ' is not in the room: ' + roomID );
	});
};

/**
 * Get all users for a room
 *
 * @param  {String} roomID room id where to retrieve users
 * @return {Promise} A promise is returned which will return all users in a room
 */
p.getUsers = function( roomID ) {

	var setName = 'roomUsers:' + roomID;

	return this.redis.smembers( setName );
};

/**
 * get a key which can be used to enter a room vs entering room via
 * roomID
 *
 * @param  {String} roomID id of the room you'd like a key for
 * @return {Promise} A promise will be returned which will return a roomKey on success
 */
p.getKey = function( roomID ) {

	var key;

	return this.redis.spop( 'roomKeys' )
	.then( function( nKey ) {

		key = nKey;

		if( key ) {

			return promise.all( [

				this._setRoomIdForKey( roomID, key ),
				this._setKeyForRoomID( roomID, key )
			])
			.then( function() {

				return key;
			});
		} else {

			return promise.reject( 'Run out of keys' );
		}
	}.bind( this ));
};

/**
 * return a room key so someone else can use it.
 *
 * @param  {String} roomID id of the room you'll be returning a key for
 * @param  {String} key the key you'd like to return
 * @return {Promise} This promise will succeed when the room key was returned
 */
p.returnKey = function( roomID, key ) {

	return this.getRoomIdForKey( key )
	.then( function( savedRoomId ) {

		if( savedRoomId == roomID ) {

			return this._deleteKeyIdLookups( roomID, key )
			.then( function() {

				return this.redis.sadd( 'roomKeys', key );
			}.bind( this ));
		} else {

			return promise.reject( 'roomID and roomID for key do not match' );
		}
	}.bind( this ));
};

/**
 * return the room id for the given key
 *
 * @param  {String} key key used to enter the room
 * @return {Promise} This promise will succeed with the room id and fail if no room id exists for key
 */
p.getRoomIdForKey = function( key ) {

	return this.redis.hget( 'keyForRoomID', key )
	.then( function( savedRoomId ) {

		if( savedRoomId ) {

			return promise.resolve( parseInt( savedRoomId ) );
		} else {

			return promise.reject();
		}
	});
};

/**
 * set a variable on the rooms data object
 *
 * @param {String} roomID id for the room whose
 * @param {String} key variable name/key that you want to set
 * @param {*} value Value you'd like to set for the variable
 * @return {Promise} once this promise succeeds the rooms variable will be set
 */
p.setRoomDataVar = function( roomID, key, value ) {

	var redis = this.redis;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return redis.hset( 'roomData:' + roomID, key, value )
			.then( function( countSet ) {

				return value;
			});
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	});
};

/**
 * get a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to get
 * @return {Promise} once this promise succeeds it will return the variable value it will fail if the variable does not exist
 */
p.getRoomDataVar = function( roomID, key ) {

	var redis = this.redis;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return redis.hget( 'roomData:' + roomID, key )
			.then( function( value ) {

				return value == null ? undefined : value;
			});
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	});
};

/**
 * delete a variable from the rooms data object
 *
 * @param {String} roomID id for the room
 * @param {String} key variable name/key that you want to delete
 * @return {Promise} once this promise succeeds it will return the value that was stored before delete
 */
p.delRoomDataVar = function( roomID, key ) {

	var redis = this.redis,
		rVal;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return this.getRoomDataVar( roomID, key )
			.then( function( value ) {

				rVal = value;
			})
			.then( function() {

				return redis.hdel( 'roomData:' + roomID, key );
			})
			.then( function() {

				return rVal;
			})
			.catch();
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	}.bind( this ));
};

/**
 * Receive the data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like data for
 * @return {Promise} This promise will succeed when data is received for the room
 */
p.getRoomData = function( roomID ) {

	var redis = this.redis;

	return this._doesRoomExist( roomID )
	.then( function( exists ) {

		if( exists ) {

			return redis.hgetall( 'roomData:' + roomID )
			.then( function( data ) {

				return data === null ? {} : data;
			});
		} else {

			return ROOM_DOES_NOT_EXIST;
		}
	});
};

/**
 * Set data stored for a room.
 *
 * @param  {String} roomID id for the room you'd like to set data for
 * @return {Promise} This promise will succeed when data is set for the room
 */
p.setRoomData = function( roomID, data ) {

	var redis = this.redis,
		name = 'roomData:' + roomID;

	return redis.hmset( 'roomData:' + roomID, data )
	.then( function() {

		return data;
	});

	return redis.watch( name )
	.then( function() {

		return redis.multi()
		.set( name, JSON.stringify( data ) )
		.exec();
	}.bind( this ))
};

/**
 * Set room as public
 *
 * @param  {String} roomID id for the room you'd like to make public
 * @return {Promise} This promise will succeed when the room as been set as public
 */
p.setRoomPublic = function( roomID ) {

	var redis = this.redis;

	return this.getRoomData( roomID ).then( redis.rpush( 'public', roomID ) );
};

/**
 * Set room as private, removing it from the publicly available list
 *
 * @param  {String} roomID id for the room you'd like to make private
 * @return {Promise} This promise will succeed when the room as been set as private
 */
p.setRoomPrivate = function( roomID ) {

	var redis = this.redis;

	return redis.lrem( 'public', 0, roomID );
};

/**
 * Gets the first available public room
 *
 * @return {Promise} This promise will succeed when the room has been retrieved
 */
p.getPublicRoom = function() {

	var redis = this.redis;

	return redis.lindex( 'public', 0 );
};

/**
 * _generateKeys is a function which will create and store a set of
 * keys which can be used to enter a room instead of a room id
 *
 * @private
 */
p._generateKeys = function() {

	var redis = this.redis;

	return redis.eval( LUA_GENERATE_KEYS , 0, 5 );
};

p._doesRoomExist = function( roomID ) {

	return this.redis.scard( 'roomUsers:' + roomID )
	.then( function( countUsers ) {

		return countUsers > 0;
	});
};

p._setRoomIdForKey = function( roomID, key ) {

	return this.redis.hset( 'roomIDForKey', roomID, key );
};

p._setKeyForRoomID = function( roomID, key ) {

	return this.redis.hset( 'keyForRoomID', key, roomID );
};

p._deleteKeyIdLookups = function( roomID, key ) {

	var redis = this.redis;

	return promise.all( [

		redis.hdel( 'roomIDForKey', roomID ),
		redis.hdel( 'keyForRoomID', key )
	]);
};

p._reset = function() {

	var redis = this.redis;

	return redis.set( 'nextRoomID', 0 )
	.then( function() {

		return redis.flushdb();
	});
};

p._getNextRoomId = function() {

	var redis = this.redis;

	return redis.eval( LUA_GET_ROOM_ID , 0 );
};<|endoftext|>


<commit_before>module.exports = {
  website: {
    assets: ".",
    html: {
      "body:start": function(current) {
        return require("fs").readFileSync("./HEADER.md")
      }
    }
  }
};<commit_msg>Try to use basePath and log 'current' for inspection<commit_after>module.exports = {
  website: {
    assets: ".",
    html: {
      "body:start": function(current) {
        var fs = require("fs");
        console.log("### ADD-HEADER ###")
        console.dir(current);
        var filePath = current.basePath + "/HEADER.md";
        try {
            return fs.readFileSync(filePath);
        } catch(err) {
            // pass
        }
      }
    }
  }
};<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var fs = require('graceful-fs');
var _ = require('lodash');
var through = require('through2');
var convert = require('convert-source-map');

class Wallabify {

  constructor(opts, initializer) {
    this._patchModuleDependenciesModule();

    this._opts = opts || {};
    this._initializer = initializer;

    this._b = null;
    this._browserifyCache = {};
    this._affectedFilesCache = {};
    this._initRequired = false;
    this._allTrackedFiles = {};
  }

  createPostprocessor() {
    var self = this;
    try {
      this._browserify = require('browserify');
    }
    catch (e) {
      console.error('Browserify node module is not found, missing `npm install browserify --save-dev`?');
      return;
    }

    // wallaby.js postprocessor for browserify
    // The postprocessor creates and reuses browserify instance.
    // It splices browserify pipeline so that file concatenation doesn't happen.
    // Instead, the postprocessor creates new files and lets wallaby.js to serve them to browser as requested.
    // This allows to leverage file-based browser caching as opposed to always reload a full bundle.
    // When separate files load, they only add a function with the file body to the cache object.
    // Actual loading happens when window.__browserify.loadTests is called (from bootstrap function).

    return wallaby => {
      var affectedFiles = wallaby.affectedFiles;
      if (!self._b || wallaby.anyFilesAdded || wallaby.anyFilesDeleted) {
        self._initRequired = true;
        self._affectedFilesCache = {};
        self._allTrackedFiles = _.reduce(wallaby.allFiles, function (memo, file) {
          memo[file.fullPath] = file;
          return memo;
        }, {});
        affectedFiles = wallaby.allFiles;

        self._b = self._createBrowserify({
          entries: _.map(wallaby.allTestFiles, testFile => testFile.fullPath),
          paths: wallaby.nodeModulesDir ? [wallaby.nodeModulesDir] : [],
          cache: {}, packageCache: {}, fullPaths: true
        });

        self._browserifyCache = self._b._options.cache;

        self._b.on('dep', function (dep) {
          if (typeof dep.id === 'string') {
            var key = dep.id;
            if (!self._browserifyCache[key]) {
              self._browserifyCache[key] = dep;
              // external files are cached by file name as well to avoid re-processing them every time
              if (dep.expose) {
                self._browserifyCache[dep.file] = dep;
              }
              // new file that has not been cached before (node module or a source file)
              self._affectedFilesCache[key] = dep;
            }
          }
        });

        // no need to pack the bundle, wallaby.js serves files one by one to leverage browser caching
        self._b.pipeline.splice('pack');
      }

      // todo: handle node modules external update
      // todo: bundle node_modules into a single file

      // removing changed files tracked by wallaby.js from browserify cache
      if (!self._initRequired) {
        _.each(affectedFiles, file => {
          delete self._browserifyCache[file.fullPath];
        });
      }

      return new Promise(
        function (resolve, reject) {
          try {
            // incremental bundling
            self._b.bundle()
              .on('data', () => {
              })
              .on('error', err => reject(err))
              .on('end', () => resolve());
          } catch (err) {
            reject(err);
          }
        })
        .then(function () {
          var createFilePromises = [];

          // test loader for wallaby.js
          // works exactly as browserify bundle loading, but with separate files as opposed to a single bundle file
          if (self._initRequired) {
            self._initRequired = false;

            createFilePromises.push(wallaby.createFile({
              order: -1,  // need to be the first file to load
              path: 'wallabify.js',
              content: Wallabify._getLoaderContent()
            }));
          }

          // handling changed files tracked by wallaby.js
          _.each(affectedFiles, function (file) {
            var cached = self._browserifyCache[file.fullPath];
            if (cached) {
              var code = cached.source;
              var sourceMap;
              var sourceMapConverter = convert.fromSource(code);
              if (sourceMapConverter) {
                sourceMap = sourceMapConverter.toJSON();
                code = convert.removeComments(code);
              }

              // cloning an original file and browserify-ing it
              createFilePromises.push(wallaby.createFile({
                // adding the suffix to store browserified file along with the original copies
                path: file.path + '.bro.js',
                original: file,
                content: Wallabify._wallabifyFile(file.fullPath, code, cached.deps),
                sourceMap: sourceMap
              }));
              delete self._affectedFilesCache[file.fullPath];
            }
          });

          // handling externally added and not tracked files (such as node modules and external files)
          try {
            _.each(self._affectedFilesCache, function (file) {
              if (!file.expose && !~file.id.indexOf(wallaby.nodeModulesDir)) {
                throw new Error('File ' + file.id + ' is neither a node module nor an external dependency.');
              }
              createFilePromises.push(wallaby.createFile({
                path: file.expose
                  ? path.join('browserify_external', file.id, 'external.js')
                  : path.join('browserify_node_modules', path.relative(wallaby.nodeModulesDir, file.id)),
                content: Wallabify._wallabifyFile(file.id, file.source, file.deps)
              }));
            });
          }
          catch (e) {
            return Promise.reject(e);
          }

          // resetting till next incremental bundle run
          self._affectedFilesCache = {};

          return Promise.all(createFilePromises);
        });
    }
  }

  _createBrowserify(mandatoryOpts) {
    var paths = mandatoryOpts.paths.concat(this._opts.paths || []);
    var mergedOpts = _.extend(this._opts, mandatoryOpts);
    mergedOpts.paths = paths;

    var instance = this._browserify(mergedOpts);
    if (this._initializer && _.isFunction(this._initializer)) {
      return this._initializer(instance) || instance;
    }

    return instance;
  }

  static _wallabifyFile(id, content, deps) {
    return 'window.__browserify.cache["' + id + '"] = [function(require, module, exports) {'
      + content + '\n}, ' + JSON.stringify(deps) + '];';
  }

  static _getLoaderContent() {
    return 'window.__browserify = {};'
      + 'window.__browserify.cache = {};'
      + 'window.__browserify.loadTests = function () {'
        // browser pack prelude
      + '(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module \'"+o+"\'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})'
        // passing accumulated files and entry points (browserified tests for the current sandbox)
      + '(window.__browserify.cache, {}, (function(){ var testIds = []; for(var i = 0, len = wallaby.loadedTests.length; i < len; i++) { var test = wallaby.loadedTests[i]; if (test.substr(-7) === ".bro.js") testIds.push(wallaby.baseDir + test.substr(0, test.length - 7)); } return testIds; })()); };'
  }

  _patchModuleDependenciesModule() {
    var wallabify = this;
    // patching module dependencies to use wallaby cache
    try {
      require('browserify/node_modules/module-deps').prototype.readFile = function (file, id) {
        var self = this;
        var tr = through();
        if (this.cache && this.cache[file]) {
          tr.push(this.cache[file].source);
          tr.push(null);
          return tr;
        }

        // wallaby.js may already have the file cached (if not, it will just read it from disk)
        if (wallabify._allTrackedFiles && wallabify._allTrackedFiles[file]) {
          wallabify._allTrackedFiles[file].getContent().then(function (source) {
            tr.push(source);
            tr.push(null);
          });
          return tr;
        }

        var rs = fs.createReadStream(file);
        rs.on('error', function (err) {
          self.emit('error', err)
        });
        this.emit('file', file, id);
        return rs;
      };
    }
    catch (e) {
      // not critical
      console.warn('Failed to patch `module-deps` module, wallaby.js file cache will not be used.'
      + '\nIt\'s not a critical issue, however tests will run faster when wallaby.js file cache is used.');
    }
  }
}

module.exports = function (opts, initializer) {
  return new Wallabify(opts, initializer).createPostprocessor();
};<commit_msg>Replaced window.__browserify window.__moduleBundler<commit_after>'use strict';

var path = require('path');
var fs = require('graceful-fs');
var _ = require('lodash');
var through = require('through2');
var convert = require('convert-source-map');

class Wallabify {

  constructor(opts, initializer) {
    this._patchModuleDependenciesModule();

    this._opts = opts || {};
    this._initializer = initializer;

    this._b = null;
    this._browserifyCache = {};
    this._affectedFilesCache = {};
    this._initRequired = false;
    this._allTrackedFiles = {};
  }

  createPostprocessor() {
    var self = this;
    try {
      this._browserify = require('browserify');
    }
    catch (e) {
      console.error('Browserify node module is not found, missing `npm install browserify --save-dev`?');
      return;
    }

    // wallaby.js postprocessor for browserify
    // The postprocessor creates and reuses browserify instance.
    // It splices browserify pipeline so that file concatenation doesn't happen.
    // Instead, the postprocessor creates new files and lets wallaby.js to serve them to browser as requested.
    // This allows to leverage file-based browser caching as opposed to always reload a full bundle.
    // When separate files load, they only add a function with the file body to the cache object.
    // Actual loading happens when window.__moduleBundler.loadTests is called (from bootstrap function).

    return wallaby => {
      var affectedFiles = wallaby.affectedFiles;
      if (!self._b || wallaby.anyFilesAdded || wallaby.anyFilesDeleted) {
        self._initRequired = true;
        self._affectedFilesCache = {};
        self._allTrackedFiles = _.reduce(wallaby.allFiles, function (memo, file) {
          memo[file.fullPath] = file;
          return memo;
        }, {});
        affectedFiles = wallaby.allFiles;

        self._b = self._createBrowserify({
          entries: _.map(wallaby.allTestFiles, testFile => testFile.fullPath),
          paths: wallaby.nodeModulesDir ? [wallaby.nodeModulesDir] : [],
          cache: {}, packageCache: {}, fullPaths: true
        });

        self._browserifyCache = self._b._options.cache;

        self._b.on('dep', function (dep) {
          if (typeof dep.id === 'string') {
            var key = dep.id;
            if (!self._browserifyCache[key]) {
              self._browserifyCache[key] = dep;
              // external files are cached by file name as well to avoid re-processing them every time
              if (dep.expose) {
                self._browserifyCache[dep.file] = dep;
              }
              // new file that has not been cached before (node module or a source file)
              self._affectedFilesCache[key] = dep;
            }
          }
        });

        // no need to pack the bundle, wallaby.js serves files one by one to leverage browser caching
        self._b.pipeline.splice('pack');
      }

      // todo: handle node modules external update
      // todo: bundle node_modules into a single file

      // removing changed files tracked by wallaby.js from browserify cache
      if (!self._initRequired) {
        _.each(affectedFiles, file => {
          delete self._browserifyCache[file.fullPath];
        });
      }

      return new Promise(
        function (resolve, reject) {
          try {
            // incremental bundling
            self._b.bundle()
              .on('data', () => {
              })
              .on('error', err => reject(err))
              .on('end', () => resolve());
          } catch (err) {
            reject(err);
          }
        })
        .then(function () {
          var createFilePromises = [];

          // test loader for wallaby.js
          // works exactly as browserify bundle loading, but with separate files as opposed to a single bundle file
          if (self._initRequired) {
            self._initRequired = false;

            createFilePromises.push(wallaby.createFile({
              order: -1,  // need to be the first file to load
              path: 'wallabify.js',
              content: Wallabify._getLoaderContent()
            }));
          }

          // handling changed files tracked by wallaby.js
          _.each(affectedFiles, function (file) {
            var cached = self._browserifyCache[file.fullPath];
            if (cached) {
              var code = cached.source;
              var sourceMap;
              var sourceMapConverter = convert.fromSource(code);
              if (sourceMapConverter) {
                sourceMap = sourceMapConverter.toJSON();
                code = convert.removeComments(code);
              }

              // cloning an original file and browserify-ing it
              createFilePromises.push(wallaby.createFile({
                // adding the suffix to store browserified file along with the original copies
                path: file.path + '.bro.js',
                original: file,
                content: Wallabify._wallabifyFile(file.fullPath, code, cached.deps),
                sourceMap: sourceMap
              }));
              delete self._affectedFilesCache[file.fullPath];
            }
          });

          // handling externally added and not tracked files (such as node modules and external files)
          try {
            _.each(self._affectedFilesCache, function (file) {
              if (!file.expose && !~file.id.indexOf(wallaby.nodeModulesDir)) {
                throw new Error('File ' + file.id + ' is neither a node module nor an external dependency.');
              }
              createFilePromises.push(wallaby.createFile({
                path: file.expose
                  ? path.join('browserify_external', file.id, 'external.js')
                  : path.join('browserify_node_modules', path.relative(wallaby.nodeModulesDir, file.id)),
                content: Wallabify._wallabifyFile(file.id, file.source, file.deps)
              }));
            });
          }
          catch (e) {
            return Promise.reject(e);
          }

          // resetting till next incremental bundle run
          self._affectedFilesCache = {};

          return Promise.all(createFilePromises);
        });
    }
  }

  _createBrowserify(mandatoryOpts) {
    var paths = mandatoryOpts.paths.concat(this._opts.paths || []);
    var mergedOpts = _.extend(this._opts, mandatoryOpts);
    mergedOpts.paths = paths;

    var instance = this._browserify(mergedOpts);
    if (this._initializer && _.isFunction(this._initializer)) {
      return this._initializer(instance) || instance;
    }

    return instance;
  }

  static _wallabifyFile(id, content, deps) {
    return 'window.__moduleBundler.cache["' + id + '"] = [function(require, module, exports) {'
      + content + '\n}, ' + JSON.stringify(deps) + '];';
  }

  static _getLoaderContent() {
    return 'window.__moduleBundler = {};'
      + 'window.__moduleBundler.cache = {};'
      + 'window.__moduleBundler.loadTests = function () {'
        // browser pack prelude
      + '(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module \'"+o+"\'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})'
        // passing accumulated files and entry points (browserified tests for the current sandbox)
      + '(window.__moduleBundler.cache, {}, (function(){ var testIds = []; for(var i = 0, len = wallaby.loadedTests.length; i < len; i++) { var test = wallaby.loadedTests[i]; if (test.substr(-7) === ".bro.js") testIds.push(wallaby.baseDir + test.substr(0, test.length - 7)); } return testIds; })()); };'
  }

  _patchModuleDependenciesModule() {
    var wallabify = this;
    // patching module dependencies to use wallaby cache
    try {
      require('browserify/node_modules/module-deps').prototype.readFile = function (file, id) {
        var self = this;
        var tr = through();
        if (this.cache && this.cache[file]) {
          tr.push(this.cache[file].source);
          tr.push(null);
          return tr;
        }

        // wallaby.js may already have the file cached (if not, it will just read it from disk)
        if (wallabify._allTrackedFiles && wallabify._allTrackedFiles[file]) {
          wallabify._allTrackedFiles[file].getContent().then(function (source) {
            tr.push(source);
            tr.push(null);
          });
          return tr;
        }

        var rs = fs.createReadStream(file);
        rs.on('error', function (err) {
          self.emit('error', err)
        });
        this.emit('file', file, id);
        return rs;
      };
    }
    catch (e) {
      // not critical
      console.warn('Failed to patch `module-deps` module, wallaby.js file cache will not be used.'
      + '\nIt\'s not a critical issue, however tests will run faster when wallaby.js file cache is used.');
    }
  }
}

module.exports = function (opts, initializer) {
  return new Wallabify(opts, initializer).createPostprocessor();
};<|endoftext|>


<commit_before>flat = require('flatjson');
xlsx = require('node-xlsx');

module.exports = function (obj, sheetname, delimiter, filter) {
    var sheet = sheetname || "Sheet 1";

    var input = [];
    if (obj instanceof Array) {
        input = obj;
    }
    else {
        input.push(obj);
    }

    //build headers
    var headers = []
    for ( var i = 0; i < input.length; i++ ) {
        var fo = flat(input[i], delimiter, filter);
        var keys = Object.keys(fo);
        for (var j = 0; j < keys.length; j++ ) {
            if ( headers.indexOf(keys[j] < 0)) {
                headers.push(keys[j]);
            }
        }
    }

    var data = [];
    data.push(headers);
    for ( var i = 0; i < input.length; i++ ) {
        var actual_data = []
        var fo = flat(input[i], delimiter, filter);
        for (key in headers) {
            actual_data.push(fo[headers[key]]);
        }
        data.push(actual_data);
    }
    var buffer = xlsx.build([{name: sheet, data: data}]);
    return buffer;
}<commit_msg>corrected key lookup bug<commit_after>flat = require('flatjson');
xlsx = require('node-xlsx');

module.exports = function (obj, sheetname, delimiter, filter) {
    var sheet = sheetname || "Sheet 1";

    var input = [];
    if (obj instanceof Array) {
        input = obj;
    }
    else {
        input.push(obj);
    }

    //build headers
    var headers = []
    for ( var i = 0; i < input.length; i++ ) {
        var fo = flat(input[i], delimiter, filter);
        var keys = Object.keys(fo);
        for (var j = 0; j < keys.length; j++ ) {
            if ( headers.indexOf(keys[j]) < 0) {
                headers.push(keys[j]);
            }
        }
    }

    var data = [];
    data.push(headers);
    for ( var i = 0; i < input.length; i++ ) {
        var actual_data = []
        var fo = flat(input[i], delimiter, filter);
        for (key in headers) {
            actual_data.push(fo[headers[key]]);
        }
        data.push(actual_data);
    }
    var buffer = xlsx.build([{name: sheet, data: data}]);
    return buffer;
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

(function () {
  'use strict';

  if (process.env.NODE_ENV !== 'production') {
    // Load the .env file, that sets process.env.
    require('dotenv').load();
  }

  const express = require('express');
  const bodyParser = require('body-parser');

  const FB = require('./connectors/facebook');
  const Bot = require('./bot');

  // LETS MAKE A SERVER!
  const app = express();
  const serverInstance = app.listen(process.env.PORT, () => {
    console.log('> Running on port', process.env.PORT);
  });

  // PARSE THE BODY
  app.use(bodyParser.json());

  // Setup Email
  const nodemailer = require('nodemailer');

  const transporter = nodemailer.createTransport({
    service: 'Gmail',
    auth: {
      user: process.env.EMAIL_ID,
      pass: process.env.EMAIL_PASS
    }
  });

  app.get('/fitbitauth/:fbid', (req, res) => {
    if (req.params.secret) {
      // Authenticate with fitbit and save details in database
      // TODO send request to fitbit with the headers
      res.send(
        '
Work in progress
'
      );
      // Save fitbitId and trackerId to user object

    } else {
      res.send(
        '
Please specify a Facebook ID
'
      );
    }

  });

  /**
   * Backup the sessions json object via email attachment.
   */
  app.get('/email/backup/:secret', (req, res) => {
    if (req.params.secret === process.env.CRON_SECRET) {
      // const mailOptions = {
      //   attachments: [{
      //     filename: (new Date()).toDateString().split(' ').join('_') + '.json',
      //     content: JSON.stringify(Bot.sessions),
      //     contentType: 'application/json'
      //   }],

      //   from: process.env.EMAIL_ID,
      //   to: process.env.EMAIL_ID,
      //   subject: 'BACKUP', // Subject line
      //   text: (new Date()).toLocaleString() + '\nChatbot backup of ' + Object.keys(Bot.sessions).length + ' users.\n\n' + JSON.stringify(Bot.sessions)
      // };

      // transporter.sendMail(mailOptions, (error, info) => {
      //   if (error) {
      //     console.log(error);
      //     console.log(error.message);
      //     res.json({response: error, message: error.message});
      //   } else {
      //     console.log('Message sent: ' + info.response);
      //     res.json({response: info.response});
      //   }
      // });
    }
    res.json({response: 'Not created'});
  });

  // Index page
  app.get('/', (req, res) => {
    res.send(
      '
Version ' + require('./package.json').version + '
' +
      '
nodejs server, index page .... hello world i am a chat bot'
    );
  });

  // app.get('/reminders/:time_of_day/:secret', (req, res) => {
  //   if (req.params.secret === process.env.CRON_SECRET) {

  //     let quickReplyActions = [
  //       'Completed Habit',
  //       'Not Today',
  //       'Snooze'
  //     ];

  //     // Validate the time of day
  //     let message = 'Wow this is a strange time.'; // Default
  //     if (req.params.time_of_day === 'morning') {
  //       message = 'Good morning';
  //     } else if (req.params.time_of_day === 'afternoon') {
  //       message = 'Afternoon';
  //     } else if (req.params.time_of_day === 'evening') {
  //       message = 'Good evening';
  //     } else if (req.params.time_of_day === 'night') {
  //       message = 'You\'re up late';
  //       // Remove snooze if its the night
  //       quickReplyActions =  [
  //         'Completed Habit',
  //         'Not Today'
  //       ];
  //     } else {
  //       res.send('Wow you should not be here. [' + req.params.time_of_day + ']');
  //       throw new Error('Reached invalid timeofday call');
  //     }

  //     // Debug Logging!
  //     console.log('We got a reminder at time: ' + req.params.time_of_day);

  //     const time = req.params.time_of_day.toUpperCase();

  //     // Get all users based on time
  //     // Setup online database, airtable
  //     const base = require('airtable').base('app5u2vOBmkjxLp4M');

  //     let i = 0;
  //     base('Users').select({
  //       filterByFormula: '({reminderTime} = "' + time + '")'
  //     }).eachPage(function page(records, fetchNextPage) {

  //       records.forEach(record => {
  //         console.log('Found user ' + record.get('fbid'));

  //         FB.newMessage(record.get('fbid'),
  //           Bot.createQuickReply(
  //             'Hey, have you completed your daily ' + Bot.convertToFriendlyName(record.get('habit')) + '?',
  //             quickReplyActions
  //           )
  //         );
  //         i++;
  //         console.log('Looking for next page');
  //         fetchNextPage();
  //       });

  //     }, function done(err) {
  //       if (err) {
  //         console.error(err);
  //         throw new Error(err);
  //       }
  //       console.log('Sent ' + time + ' reminders to ' + i + ' users.');
  //       res.send('Sent ' + time + ' reminders to ' + i + ' users.');
  //     });

  //   } else {
  //     res.send('Secret invalid.');
  //   }
  // });

  // For facebook to verify
  app.get('/webhooks', (req, res) => {
    if (req.query['hub.verify_token'] === process.env.FB_VERIFY_TOKEN) {
      res.status(200).send(req.query['hub.challenge']);
    } else {
      console.log('Error wrong fb verify token, make sure validation tokens match.');
      res.status(403).send('> Error, wrong fb verify token');
    }
  });

  /**
   * Facebook Messenger webhook, to receive messages via Messenger.
   */
  app.post('/webhooks', (req, res) => {
    console.log('> Receiving Message');

    // Extract message content
    const entry = FB.getMessageEntry(req.body);
    console.log(entry);

    // If the message is valid
    if (entry && entry.message) {
      if (entry.message && entry.message.quick_reply) {
        console.log('QR> ' + entry.message.quick_reply.payload);
      } else {
        console.log('MSG> ' + entry.message.text);
      }

      // Process the message and decide on the response
      Bot.read(entry.sender.id, entry.message, (senderFbid, reply, anotherReply) => {
        console.log('-- from bot to user vv --');
        console.log(JSON.stringify(reply));

        // Send message to that user
        FB.newMessage(senderFbid, reply, (msg, data) => {
          if (data.error) {
            console.log('Error sending new fb message');
            console.log(msg); // Log received info
            console.log(data); // Log recieved info
          } else if (typeof anotherReply !== 'undefined' && anotherReply !== null) {
            // Check if we want to double message the user
            setTimeout(() => {
              FB.newMessage(senderFbid, anotherReply, (msg, data) => {
                if (data.error) {
                  console.log('Error sending new second reply fb message');
                  console.log(msg); // Log received info
                  console.log(data); // Log recieved info
                }
              });
            }, 2000); // 2 second gap between messages
          }
        });
      });
    } else {
      console.log('Invalid entry/message or attachment found.');
      console.log(JSON.stringify(entry));
      console.log(JSON.stringify(req.body));
      // process.exit(1);
    }

    res.sendStatus(200);
  });

  module.exports = {
    shutdown() {
      console.log('Server shutting down');
      serverInstance.close();
    }
  };
})();
<commit_msg>Added basic fitbit auth for demo<commit_after>#!/usr/bin/env node

(function () {
  'use strict';

  if (process.env.NODE_ENV !== 'production') {
    // Load the .env file, that sets process.env.
    require('dotenv').load();
  }

  const express = require('express');
  const bodyParser = require('body-parser');
  const url = require('url');

  const FB = require('./connectors/facebook');
  const Bot = require('./bot');

  // LETS MAKE A SERVER!
  const app = express();
  const serverInstance = app.listen(process.env.PORT, () => {
    console.log('> Running on port', process.env.PORT);
  });

  // PARSE THE BODY
  app.use(bodyParser.json());

  // Setup Email
  const nodemailer = require('nodemailer');

  const transporter = nodemailer.createTransport({
    service: 'Gmail',
    auth: {
      user: process.env.EMAIL_ID,
      pass: process.env.EMAIL_PASS
    }
  });

  app.get('/fitbitauth/:fbid', (req, res) => {
    if (req.params.fbid) {
      res.redirect(url.format({
        pathname: 'https://www.fitbit.com/oauth2/authorize',
        query: {
          response_type: 'code',
          client_id:  process.env.FITBIT_CLIENT_ID,
          scope: 'settings'
        }
      }));
    } else {
      res.send(
        '
Please specify a Facebook ID
'
      );
    }
  });

  /**
   * Backup the sessions json object via email attachment.
   */
  app.get('/email/backup/:secret', (req, res) => {
    if (req.params.secret === process.env.CRON_SECRET) {
      // const mailOptions = {
      //   attachments: [{
      //     filename: (new Date()).toDateString().split(' ').join('_') + '.json',
      //     content: JSON.stringify(Bot.sessions),
      //     contentType: 'application/json'
      //   }],

      //   from: process.env.EMAIL_ID,
      //   to: process.env.EMAIL_ID,
      //   subject: 'BACKUP', // Subject line
      //   text: (new Date()).toLocaleString() + '\nChatbot backup of ' + Object.keys(Bot.sessions).length + ' users.\n\n' + JSON.stringify(Bot.sessions)
      // };

      // transporter.sendMail(mailOptions, (error, info) => {
      //   if (error) {
      //     console.log(error);
      //     console.log(error.message);
      //     res.json({response: error, message: error.message});
      //   } else {
      //     console.log('Message sent: ' + info.response);
      //     res.json({response: info.response});
      //   }
      // });
    }
    res.json({response: 'Not created'});
  });

  // Index page
  app.get('/', (req, res) => {
    res.send(
      '
Version ' + require('./package.json').version + '
' +
      '
nodejs server, index page .... hello world i am a chat bot'
    );
  });

  // app.get('/reminders/:time_of_day/:secret', (req, res) => {
  //   if (req.params.secret === process.env.CRON_SECRET) {

  //     let quickReplyActions = [
  //       'Completed Habit',
  //       'Not Today',
  //       'Snooze'
  //     ];

  //     // Validate the time of day
  //     let message = 'Wow this is a strange time.'; // Default
  //     if (req.params.time_of_day === 'morning') {
  //       message = 'Good morning';
  //     } else if (req.params.time_of_day === 'afternoon') {
  //       message = 'Afternoon';
  //     } else if (req.params.time_of_day === 'evening') {
  //       message = 'Good evening';
  //     } else if (req.params.time_of_day === 'night') {
  //       message = 'You\'re up late';
  //       // Remove snooze if its the night
  //       quickReplyActions =  [
  //         'Completed Habit',
  //         'Not Today'
  //       ];
  //     } else {
  //       res.send('Wow you should not be here. [' + req.params.time_of_day + ']');
  //       throw new Error('Reached invalid timeofday call');
  //     }

  //     // Debug Logging!
  //     console.log('We got a reminder at time: ' + req.params.time_of_day);

  //     const time = req.params.time_of_day.toUpperCase();

  //     // Get all users based on time
  //     // Setup online database, airtable
  //     const base = require('airtable').base('app5u2vOBmkjxLp4M');

  //     let i = 0;
  //     base('Users').select({
  //       filterByFormula: '({reminderTime} = "' + time + '")'
  //     }).eachPage(function page(records, fetchNextPage) {

  //       records.forEach(record => {
  //         console.log('Found user ' + record.get('fbid'));

  //         FB.newMessage(record.get('fbid'),
  //           Bot.createQuickReply(
  //             'Hey, have you completed your daily ' + Bot.convertToFriendlyName(record.get('habit')) + '?',
  //             quickReplyActions
  //           )
  //         );
  //         i++;
  //         console.log('Looking for next page');
  //         fetchNextPage();
  //       });

  //     }, function done(err) {
  //       if (err) {
  //         console.error(err);
  //         throw new Error(err);
  //       }
  //       console.log('Sent ' + time + ' reminders to ' + i + ' users.');
  //       res.send('Sent ' + time + ' reminders to ' + i + ' users.');
  //     });

  //   } else {
  //     res.send('Secret invalid.');
  //   }
  // });

  // For facebook to verify
  app.get('/webhooks', (req, res) => {
    if (req.query['hub.verify_token'] === process.env.FB_VERIFY_TOKEN) {
      res.status(200).send(req.query['hub.challenge']);
    } else {
      console.log('Error wrong fb verify token, make sure validation tokens match.');
      res.status(403).send('> Error, wrong fb verify token');
    }
  });

  /**
   * Facebook Messenger webhook, to receive messages via Messenger.
   */
  app.post('/webhooks', (req, res) => {
    console.log('> Receiving Message');

    // Extract message content
    const entry = FB.getMessageEntry(req.body);
    console.log(entry);

    // If the message is valid
    if (entry && entry.message) {
      if (entry.message && entry.message.quick_reply) {
        console.log('QR> ' + entry.message.quick_reply.payload);
      } else {
        console.log('MSG> ' + entry.message.text);
      }

      // Process the message and decide on the response
      Bot.read(entry.sender.id, entry.message, (senderFbid, reply, anotherReply) => {
        console.log('-- from bot to user vv --');
        console.log(JSON.stringify(reply));

        // Send message to that user
        FB.newMessage(senderFbid, reply, (msg, data) => {
          if (data.error) {
            console.log('Error sending new fb message');
            console.log(msg); // Log received info
            console.log(data); // Log recieved info
          } else if (typeof anotherReply !== 'undefined' && anotherReply !== null) {
            // Check if we want to double message the user
            setTimeout(() => {
              FB.newMessage(senderFbid, anotherReply, (msg, data) => {
                if (data.error) {
                  console.log('Error sending new second reply fb message');
                  console.log(msg); // Log received info
                  console.log(data); // Log recieved info
                }
              });
            }, 2000); // 2 second gap between messages
          }
        });
      });
    } else {
      console.log('Invalid entry/message or attachment found.');
      console.log(JSON.stringify(entry));
      console.log(JSON.stringify(req.body));
      // process.exit(1);
    }

    res.sendStatus(200);
  });

  module.exports = {
    shutdown() {
      console.log('Server shutting down');
      serverInstance.close();
    }
  };
})();
<|endoftext|>


<commit_before>'use strict';

var spawn = require("cross-spawn");
var _ = require("lodash");
var compilerBinaryName = "elm-make";
var fs = require("fs");
var path = require("path");
var temp = require("temp");

var defaultOptions     = {
  emitWarning: console.warn,
  spawn:      spawn,
  cwd:        undefined,
  pathToMake: undefined,
  yes:        undefined,
  help:       undefined,
  output:     undefined,
  report:     undefined,
  warn:       undefined,
  verbose:    false,
  processOpts: undefined,
};

var supportedOptions = _.keys(defaultOptions);

function compile(sources, options) {
  if (typeof sources === "string") {
    sources = [sources];
  }

  if (!(sources instanceof Array)) {
    throw "compile() received neither an Array nor a String for its sources argument."
  }

  options = _.defaults({}, options, defaultOptions);

  if (typeof options.spawn !== "function") {
    throw "options.spawn was a(n) " + (typeof options.spawn) + " instead of a function."
  }

  var compilerArgs = compilerArgsFromOptions(options, options.emitWarning);
  var processArgs  = sources ? sources.concat(compilerArgs) : compilerArgs;
  var env = _.merge({LANG: 'en_US.UTF-8'}, process.env);
  var processOpts = _.merge({ env: env, stdio: "inherit", cwd: options.cwd }, options.processOpts);
  var pathToMake = options.pathToMake || compilerBinaryName;
  var verbose = options.verbose;

  try {
    if (verbose) {
      console.log(["Running", pathToMake].concat(processArgs || []).join(" "));
    }

    return options.spawn(pathToMake, processArgs, processOpts)
      .on('error', function(err) {
        handleError(pathToMake, err);

        process.exit(1)
      });
  } catch (err) {
    if ((typeof err === "object") && (typeof err.code === "string")) {
      handleError(pathToMake, err);
    } else {
      console.error("Exception thrown when attempting to run Elm compiler " + JSON.stringify(pathToMake) + ":\n" + err);
    }

    process.exit(1)
  }
}

// Returns a Promise that returns a flat list of all the Elm files the given
// Elm file depends on, based on the modules it loads via `import`.
function findAllDependencies(file, knownDependencies, baseDir) {
  if (!knownDependencies) {
    knownDependencies = [];
  }

  if (!baseDir) {
    baseDir = path.dirname(file);
  }

  return new Promise(function(resolve, reject) {

    fs.readFile(file, {encoding: "utf8"}, function(err, lines) {
      if (err) {
        reject(err);
      } else {
        // Turn e.g. ~/code/elm-css/src/Css.elm
        // into just ~/code/elm-css/src/
        var newImports = _.compact(lines.split("\n").map(function(line) {
          var matches = line.match(/^import\s+([^\s]+)/);

          if (matches) {
            // e.g. Css.Declarations
            var moduleName = matches[1];

            // e.g. Css/Declarations
            var dependencyLogicalName = moduleName.replace(/\./g, "/");

            // e.g. ~/code/elm-css/src/Css/Declarations.elm
            var result = path.join(baseDir, dependencyLogicalName)

            return _.contains(knownDependencies, result) ? null : result;
          } else {
            return null;
          }
        }));

        var promises = newImports.map(function(newImport) {
          var elmFile = newImport + ".elm";

          return new Promise(function(resolve, reject) {
            return checkIsFile(newImport + ".elm").then(resolve).catch(function(firstErr) {
              if (firstErr.code === "ENOENT") {
                // If we couldn't find the import as a .elm file, try as .js
                checkIsFile(newImport + ".js").then(resolve).catch(function(secondErr) {
                  if (secondErr.code === "ENOENT") {
                    // If we don't find the dependency in our filesystem, assume it's because
                    // it comes in through a third-party package rather than our sources.
                    resolve([]);
                  } else {
                    reject(secondErr);
                  }
                })
              } else {
                reject(firstErr);
              }
            });
          });
        });

        Promise.all(promises).then(function(nestedValidDependencies) {
          var validDependencies = _.flatten(nestedValidDependencies);
          var newDependencies = knownDependencies.concat(validDependencies);
          var recursePromises = _.compact(validDependencies.map(function(dependency) {
            return path.extname(dependency) === ".elm" ?
              findAllDependencies(dependency, newDependencies, baseDir) : null;
          }));

          Promise.all(recursePromises).then(function(extraDependencies) {
            resolve(_.uniq(_.flatten(newDependencies.concat(extraDependencies))));
          }).catch(reject);
        }).catch(reject);
      }
    });
  });
}

// write compiled Elm to a string output
// returns a Promise which will contain a Buffer of the text
// If you want html instead of js, use options object to set
// output to a html file instead
// creates a temp file and deletes it after reading
function compileToString(sources, options){
  if (typeof options.output === "undefined"){
    options.output = '.js';
  }

  return new Promise(function(resolve, reject){
    temp.open({ suffix: options.output }, function(err, info){
      if (err){
        return reject(err);
      }

      options.output = info.path;
      options.processOpts = { stdio: 'pipe' }

      var compiler = compile(sources, options);

      var output = '';
      compiler.stdout.on('data', function(chunk) {
        output += chunk;
      });
      compiler.stderr.on('data', function(chunk) {
        output += chunk;
      });

      compiler.on("close", function(exitCode) {
          if (exitCode !== 0) {
            temp.cleanupSync();
            return reject(new Error('Compilation failed\n' + output));
          }

          fs.readFile(info.path, function(err, data){
            temp.cleanupSync();
            return err ? reject(err) : resolve(data);
          });
        });
    });
  });
}

function checkIsFile(file) {
  return new Promise(function(resolve, reject) {
    fs.stat(file, function(err, stats) {
      if (err) {
        reject(err);
      } else if (stats.isFile()) {
        resolve([file]);
      } else {
        resolve([]);
      }
    });
  });
}

function handleError(pathToMake, err) {
  if (err.code === "ENOENT") {
    console.error("Could not find Elm compiler \"" + pathToMake + "\". Is it installed?")
  } else if (err.code === "EACCES") {
    console.error("Elm compiler \"" + pathToMake + "\" did not have permission to run. Do you need to give it executable permissions?");
  } else {
    console.error("Error attempting to run Elm compiler \"" + pathToMake + "\":\n" + err);
  }
}

function escapePath(pathStr) {
  return pathStr.replace(/ /g, "\\ ");
}

// Converts an object of key/value pairs to an array of arguments suitable
// to be passed to child_process.spawn for elm-make.
function compilerArgsFromOptions(options, emitWarning) {
  return _.flatten(_.map(options, function(value, opt) {
    if (value) {
      switch(opt) {
        case "yes":    return ["--yes"];
        case "help":   return ["--help"];
        case "output": return ["--output", escapePath(value)];
        case "report": return ["--report", value];
        case "warn":   return ["--warn"];
        default:
          if (supportedOptions.indexOf(opt) === -1) {
            emitWarning('Unknown Elm compiler option: ' + opt);
          }

          return [];
      }
    } else {
      return [];
    }
  }));
}

module.exports = {
  compile: compile,
  compileWorker: require("./worker.js")(compile),
  compileToString: compileToString,
  findAllDependencies: findAllDependencies
};
<commit_msg>Use _.includes instead of _.contains<commit_after>'use strict';

var spawn = require("cross-spawn");
var _ = require("lodash");
var compilerBinaryName = "elm-make";
var fs = require("fs");
var path = require("path");
var temp = require("temp");

var defaultOptions     = {
  emitWarning: console.warn,
  spawn:      spawn,
  cwd:        undefined,
  pathToMake: undefined,
  yes:        undefined,
  help:       undefined,
  output:     undefined,
  report:     undefined,
  warn:       undefined,
  verbose:    false,
  processOpts: undefined,
};

var supportedOptions = _.keys(defaultOptions);

function compile(sources, options) {
  if (typeof sources === "string") {
    sources = [sources];
  }

  if (!(sources instanceof Array)) {
    throw "compile() received neither an Array nor a String for its sources argument."
  }

  options = _.defaults({}, options, defaultOptions);

  if (typeof options.spawn !== "function") {
    throw "options.spawn was a(n) " + (typeof options.spawn) + " instead of a function."
  }

  var compilerArgs = compilerArgsFromOptions(options, options.emitWarning);
  var processArgs  = sources ? sources.concat(compilerArgs) : compilerArgs;
  var env = _.merge({LANG: 'en_US.UTF-8'}, process.env);
  var processOpts = _.merge({ env: env, stdio: "inherit", cwd: options.cwd }, options.processOpts);
  var pathToMake = options.pathToMake || compilerBinaryName;
  var verbose = options.verbose;

  try {
    if (verbose) {
      console.log(["Running", pathToMake].concat(processArgs || []).join(" "));
    }

    return options.spawn(pathToMake, processArgs, processOpts)
      .on('error', function(err) {
        handleError(pathToMake, err);

        process.exit(1)
      });
  } catch (err) {
    if ((typeof err === "object") && (typeof err.code === "string")) {
      handleError(pathToMake, err);
    } else {
      console.error("Exception thrown when attempting to run Elm compiler " + JSON.stringify(pathToMake) + ":\n" + err);
    }

    process.exit(1)
  }
}

// Returns a Promise that returns a flat list of all the Elm files the given
// Elm file depends on, based on the modules it loads via `import`.
function findAllDependencies(file, knownDependencies, baseDir) {
  if (!knownDependencies) {
    knownDependencies = [];
  }

  if (!baseDir) {
    baseDir = path.dirname(file);
  }

  return new Promise(function(resolve, reject) {

    fs.readFile(file, {encoding: "utf8"}, function(err, lines) {
      if (err) {
        reject(err);
      } else {
        // Turn e.g. ~/code/elm-css/src/Css.elm
        // into just ~/code/elm-css/src/
        var newImports = _.compact(lines.split("\n").map(function(line) {
          var matches = line.match(/^import\s+([^\s]+)/);

          if (matches) {
            // e.g. Css.Declarations
            var moduleName = matches[1];

            // e.g. Css/Declarations
            var dependencyLogicalName = moduleName.replace(/\./g, "/");

            // e.g. ~/code/elm-css/src/Css/Declarations.elm
            var result = path.join(baseDir, dependencyLogicalName)

            return _.includes(knownDependencies, result) ? null : result;
          } else {
            return null;
          }
        }));

        var promises = newImports.map(function(newImport) {
          var elmFile = newImport + ".elm";

          return new Promise(function(resolve, reject) {
            return checkIsFile(newImport + ".elm").then(resolve).catch(function(firstErr) {
              if (firstErr.code === "ENOENT") {
                // If we couldn't find the import as a .elm file, try as .js
                checkIsFile(newImport + ".js").then(resolve).catch(function(secondErr) {
                  if (secondErr.code === "ENOENT") {
                    // If we don't find the dependency in our filesystem, assume it's because
                    // it comes in through a third-party package rather than our sources.
                    resolve([]);
                  } else {
                    reject(secondErr);
                  }
                })
              } else {
                reject(firstErr);
              }
            });
          });
        });

        Promise.all(promises).then(function(nestedValidDependencies) {
          var validDependencies = _.flatten(nestedValidDependencies);
          var newDependencies = knownDependencies.concat(validDependencies);
          var recursePromises = _.compact(validDependencies.map(function(dependency) {
            return path.extname(dependency) === ".elm" ?
              findAllDependencies(dependency, newDependencies, baseDir) : null;
          }));

          Promise.all(recursePromises).then(function(extraDependencies) {
            resolve(_.uniq(_.flatten(newDependencies.concat(extraDependencies))));
          }).catch(reject);
        }).catch(reject);
      }
    });
  });
}

// write compiled Elm to a string output
// returns a Promise which will contain a Buffer of the text
// If you want html instead of js, use options object to set
// output to a html file instead
// creates a temp file and deletes it after reading
function compileToString(sources, options){
  if (typeof options.output === "undefined"){
    options.output = '.js';
  }

  return new Promise(function(resolve, reject){
    temp.open({ suffix: options.output }, function(err, info){
      if (err){
        return reject(err);
      }

      options.output = info.path;
      options.processOpts = { stdio: 'pipe' }

      var compiler = compile(sources, options);

      var output = '';
      compiler.stdout.on('data', function(chunk) {
        output += chunk;
      });
      compiler.stderr.on('data', function(chunk) {
        output += chunk;
      });

      compiler.on("close", function(exitCode) {
          if (exitCode !== 0) {
            temp.cleanupSync();
            return reject(new Error('Compilation failed\n' + output));
          }

          fs.readFile(info.path, function(err, data){
            temp.cleanupSync();
            return err ? reject(err) : resolve(data);
          });
        });
    });
  });
}

function checkIsFile(file) {
  return new Promise(function(resolve, reject) {
    fs.stat(file, function(err, stats) {
      if (err) {
        reject(err);
      } else if (stats.isFile()) {
        resolve([file]);
      } else {
        resolve([]);
      }
    });
  });
}

function handleError(pathToMake, err) {
  if (err.code === "ENOENT") {
    console.error("Could not find Elm compiler \"" + pathToMake + "\". Is it installed?")
  } else if (err.code === "EACCES") {
    console.error("Elm compiler \"" + pathToMake + "\" did not have permission to run. Do you need to give it executable permissions?");
  } else {
    console.error("Error attempting to run Elm compiler \"" + pathToMake + "\":\n" + err);
  }
}

function escapePath(pathStr) {
  return pathStr.replace(/ /g, "\\ ");
}

// Converts an object of key/value pairs to an array of arguments suitable
// to be passed to child_process.spawn for elm-make.
function compilerArgsFromOptions(options, emitWarning) {
  return _.flatten(_.map(options, function(value, opt) {
    if (value) {
      switch(opt) {
        case "yes":    return ["--yes"];
        case "help":   return ["--help"];
        case "output": return ["--output", escapePath(value)];
        case "report": return ["--report", value];
        case "warn":   return ["--warn"];
        default:
          if (supportedOptions.indexOf(opt) === -1) {
            emitWarning('Unknown Elm compiler option: ' + opt);
          }

          return [];
      }
    } else {
      return [];
    }
  }));
}

module.exports = {
  compile: compile,
  compileWorker: require("./worker.js")(compile),
  compileToString: compileToString,
  findAllDependencies: findAllDependencies
};
<|endoftext|>