<commit_before>/**
 * aFrame library
 * http://avoidwork.com/aFrame
 *
 * @author Jason Mulligan <jason.mulligan@avoidwork.com>
 * @version 1.0.beta
 */

/**
 * aFrame singleton.
 */
var aFrame=(aFrame)?aFrame:function()
{
	/**
	 * Helper function.
	 *
	 * @param {mixed} Element ID or array of element IDs.
	 * @returns {mixed} Instance or Array of Instances of elements.
	 * @TODO Test!
	 */
	$=function(args)
	{
		switch(typeof args)
		{
		case "object":
			var instances=[];
			for (id in args)
			{
				instances.push(document.getElementById(id));
			}
			return instances;
		case "string":
		default:
			return document.getElementById(args);
		}
	};

	/**
	 * IE detection
	 */
	ie=(document.all)?true:false;

	/**
	 * This array holds cached URI resources.
	 */
	cache=[];

	/**
	 * CSS3 detection, kinda weak!
	 */
	css3=((!document.all)||(navigator.appVersion.indexOf("MSIE 9")>-1))?true:false;

	/**
	 * Exception handling.
	 *
	 * @TODO Figure out what to do with this!
	 */
	ex=function(args)
	{
		alert(args); // temp!!
	};

	/**
	 * Calendar class will render a calendar, and act as a date picker.
	 */
	calendar=
	{
			clear:false,
			date:new Date(),
			pattern:new String("yyyy/mm/dd") // ISO 8601 standard, change to any localized pattern
	};

	/**
	 * Client class contains methods to retrieve data.
	 * These methods should be executed from a try{} statement.
	 */
	client=
	{

		/**
		 * Receives and caches the URI/xmlHttp response
		 *  set attribute based on typeof maybe?
		 *
		 *  @param {Integer} Target element ID.
		 *  @param {String} Attribute to set with response.
		 *  @param {Object} XMLHttp object.
		 * @TODO add a timestamp for expiration.
		 */
		httpGet:function(obj,attribute,xmlHttp)
		{
			if (xmlHttp.readyState==4)
			{
					if ((xmlHttp.status==200)&&(xmlHttp.responseText!=""))
					{
						if ($(obj))
						{
							eval("cache[\""+obj+"\"]="+xmlHttp.responseText+";");
							element.update($(obj),[[attribute,xmlHttp.responseText]]);
						}
						else
						{
							throw label.exception.err1;
						}
					}
					else
					{
						throw label.exception.err2;
					}
			}
		},

		/**
		 * Creates an xmlHttp request for a URI.
		 */
		httpRequest:function(id,attribute,uri,operation)
		{
			var xmlHttp=false;

			if (window.XMLHttpRequest)
			{
				xmlHttp=new XMLHttpRequest();
			}
			else if (window.ActiveXObject)
			{
				try { xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); }
				catch (e)
				{
					try { xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); }
					catch (e)
					{
						ex(e);
					}
				}
			}

			(!xmlHttp)?eval("return false"):void(0);

			try
			{
				switch(operation)
				{
				case "get":
					xmlHttp.onreadystatechange=function() { client.httpGet(id,attribute,xmlHttp); };
					xmlHttp.open("GET",uri,true);
					xmlHttp.send(null);
					break;
				case "post":
					// @TODO finish this!
					break;
				};
			}
			catch(e)
			{
				ex(e);
			}
		},

		/**
		 * Renders a loading (spinning) icon in a target element.
		 */
		icon: function(obj)
		{
			if (!$(obj).id+"_"+label.element.loading)
			{
				var args=
				[
				      [["alt",label.element.loading]],
				      [["id",$(obj).id+"_"+label.element.loading]],
				      [["src",window["aFrame.icon"].src]],
				      [["class","loading"]]
				];

				try
				{
					element.create("img",args,obj);
				}
				catch(e)
				{
					ex(e);
				}
			}
		}
	};

	/**
	 * Exposed to the client.
	 * @constructor
	 */
	constructor=
	{
		/**
		 * Classes
		 */
		calendar:this.parent.calendar,
		client:this.parent.client, // add client height/width properties
		element:this.parent.element,
		fx:this.parent.fx,
		label:this.parent.label,
		validate:this.parent.validate,

		/**
		 * Methods
		 */
		$:this.parent.$,
		position:null, //find the position; maybe put this in the element class?

		/**
		 * Properties
		 */
		ie:this.parent.ie,
		css3:this.parent.css3
	};

	/**
	 * Element class provides CRUD methods.
	 */
	element=
	{
		/**
		 * Creates an element.
		 *
		 * @param {String} Type of element to create.
		 * @param {Array} Literal array of attributes for the new element.
		 * @param {String} Optional target element ID.
		 */
		create:function(element,args,target)
		{
			if (typeof args=="object")
			{
				var loop=args.length;
				var obj=document.createElement(element);

				for (i=0;i<loop;i++)
				{
					switch(args[i][0])
					{
					case "class":
						(this.ie)?obj.setAttribute("className",args[i][1]):href.setAttribute("class",args[i][1]);
						break;
					case "innerHTML":
					case "type":
					case "src":
						obj.args[i][0]=args[i][1];
						break;
					default:
						obj.setAttribute(args[i][0],args[i][1]);
						break;
					};
				}

				((target==undefined)||(!$(target)))?document.body.appendChild(obj):target.appendChild(obj);
			}
			else
			{
				throw label.exception.err3;
			}
		},

		/**
		 * Destroys an element if it exists.
		 *
		 * @param {Integer} Target element ID.
		 */
		destroy:function(id)
		{
			if ($(id)) { document.body.removeChild($(id)); }
		},

		/**
		 * Resets an element.
		 *
		 * @param {Integer} Target element ID.
		 */
		reset:function(id)
		{
			if ($(id))
			{
				var element=$(id);
				switch(typeof element)
				{
					case "object":
						element.update(id,[["innerHTML",""]]);
						break;
					case "form":
						$(id).reset();
						break;
				}
			}
			else
			{
				throw label.exception.err1;
			}
		},

		/**
		 * Updates an element.
		 *
		 * @param {Integer} Target element ID.
		 * @param {Array} Literal array of attributes and values.
		 */
		update:function(id,args)
		{
			if ($(id))
			{
				if (typeof args=="object")
				{
					var loop=args.length;
					var obj=$(id);
					for (i=0;i<loop;i++)
					{
						switch(args[i][0])
						{
						case "class":
							(this.ie)?obj.setAttribute("className",args[i][1]):href.setAttribute("class",args[i][1]);
							break;
						case "innerHTML":
						case "type":
						case "src":
							obj.args[i][0]=args[i][1];
							break;
						default:
							obj.setAttribute(args[i][0],args[i][1]);
							break;
						};
					}
				}
				else
				{
					throw label.exception.err3;
				}
			}
			else
			{
				throw label.exception.err1;
			}
		}
	};

	/**
	 * GUI effects
	 */
	fx=
	{
		/**
		 * Changes an element's opacity to the supplied value.
		 */
		opacity:function(opacity,id)
		{
			if ($(id))
			{
				var style=$(id).style;
				style.opacity=(opacity/100);
				style.MozOpacity=(opacity/100);
				style.KhtmlOpacity=(opacity/100);
				style.filter="alpha(opacity="+opacity+")";
			}
		},

		/**
		 * Changes an element's opacity to the supplied value, spanning a supplied timeframe.
		 *
		 * @TODO replace aFrame hook with a reference to the parent object/id/name
		 */
		opacityChange:function(id,start,end,ms)
		{
			var speed=Math.round(ms/100);
			var timer=0;

			if (start>end)
			{
				for (i=start;i>=end;i--)
				{
					setTimeout("aFrame.fx.opacity("+i+",'"+id+"')",(timer*speed));
					timer++;
				}
			}
			else if (start<end)
			{
				for (i=start;i<=end;i++)
				{
					setTimeout("aFrame.fx.opacity("+i+",'"+id+"')",(timer*speed));
					timer++;
				}
			}
		},

		/**
		 * Shifts an element's opacity, spanning a supplied timeframe.
		 */
		opacityShift:function(id,ms)
		{
			($(id).style.opacity==0)?opacityChange(id,0,100,ms):opacityChange(id,100,0,ms);
		}
	};

	/**
	 * Label collection / language pack.
	 * Overload this to change languages..
	 */
	var label=
	{
		exception:
		{
			err1:"Couldn't find target element.",
			err2:"A server error has occurred.",
			err3:"Expected an array."
		},

		element:
		{
			back:"Back",
			cancel:"Cancel",
			cont:"Continue",
			del:"Delete",
			edit:"Edit",
			loading:"Loading",
			next:"Next",
			login:"Login",
			save:"Save",
			submit:"Submit"
		},

		month:
		{
			1:"January",
			2:"February",
			3:"March",
			4:"April",
			5:"May",
			6:"June",
			7:"July",
			8:"August",
			9:"September",
			10:"October",
			11:"November",
			12:"December"
		}
	};

	/**
	 * Form validation.
	 */
	var validate=
	{
	};

	return constructor;
}();<commit_msg>Misc revisions, still not done cleaning up. The calendar will go in soon, and then form validation.<commit_after>/**
 * aFrame library
 * http://avoidwork.com/aFrame
 *
 * @author Jason Mulligan <jason.mulligan@avoidwork.com>
 * @version 1.0.beta
 */

/**
 * aFrame singleton.
 */
var aFrame=(aFrame)?aFrame:function()
{
	/**
	 * Helper function.
	 *
	 * @param {mixed} Element ID or array of element IDs.
	 * @returns {mixed} Instance or Array of Instances of elements.
	 * @TODO Test!
	 */
	$=function(args)
	{
		switch(typeof args)
		{
		case "object":
			var instances=[];
			for (id in args)
			{
				instances.push(document.getElementById(id));
			}
			return instances;
		case "string":
		default:
			return document.getElementById(args);
		}
	};

	/**
	 * IE detection
	 */
	ie=(document.all)?true:false;

	/**
	 * This array holds cached URI resources.
	 */
	cache=[];

	/**
	 * CSS3 detection, kinda weak!
	 */
	css3=((!document.all)||(navigator.appVersion.indexOf("MSIE 9")>-1))?true:false;

	/**
	 * Exception handling.
	 *
	 * @TODO Figure out what to do with this!
	 */
	ex=function(args)
	{
		alert(args); // temp!!
	};

	/**
	 * Calendar class will render a calendar, and act as a date picker.
	 */
	calendar=
	{
			clear:false,
			date:new Date(),
			pattern:new String("yyyy/mm/dd") // ISO 8601 standard, change to any localized pattern
	};

	/**
	 * Client class contains methods to retrieve data.
	 * These methods should be executed from a try{} statement.
	 */
	client=
	{

		/**
		 * Receives and caches the URI/xmlHttp response
		 *  set attribute based on typeof maybe?
		 *
		 *  @param {Integer} Target element ID.
		 *  @param {String} Attribute to set with response.
		 *  @param {Object} XMLHttp object.
		 * @TODO add a timestamp for expiration.
		 */
		httpGet:function(target,xmlHttp)
		{
			if (xmlHttp.readyState==4)
			{
					if ((xmlHttp.status==200)&&(xmlHttp.responseText!=""))
					{
						if ($(obj))
						{
							eval("cache[\""+target+"\"]="+xmlHttp.responseText+";");
							element.update($(target),[["innerHTML",xmlHttp.responseText]]);
						}
						else
						{
							throw label.exception.err1;
						}
					}
					else
					{
						throw label.exception.err2;
					}
			}
		},

		/**
		 * Creates an xmlHttp request for a URI.
		 */
		httpRequest:function(target,uri,operation)
		{
			var xmlHttp=false;

			if (window.XMLHttpRequest)
			{
				xmlHttp=new XMLHttpRequest();
			}
			else if (window.ActiveXObject)
			{
				try { xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); }
				catch (e)
				{
					try { xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); }
					catch (e)
					{
						ex(e);
					}
				}
			}

			(!xmlHttp)?eval("return false"):void(0);

			try
			{
				switch(operation)
				{
				case "get":
					xmlHttp.onreadystatechange=function() { client.httpGet(target,xmlHttp); };
					xmlHttp.open("GET",uri,true);
					xmlHttp.send(null);
					break;
				case "post":
					// @TODO finish this!
					break;
				};
			}
			catch(e)
			{
				ex(e);
			}
		},

		/**
		 * Renders a loading (spinning) icon in a target element.
		 */
		icon: function(obj)
		{
			if (!$(obj).id+"_"+label.element.loading)
			{
				var args=
				[
				      [["alt",label.element.loading]],
				      [["id",$(obj).id+"_"+label.element.loading]],
				      [["src",window["aFrame.icon"].src]],
				      [["class","loading"]]
				];

				try
				{
					element.create("img",args,obj);
				}
				catch(e)
				{
					ex(e);
				}
			}
		}
	};

	/**
	 * Exposed to the client.
	 * @constructor
	 */
	constructor=
	{
		/**
		 * Classes
		 */
		calendar:this.parent.calendar,
		client:this.parent.client, // add client height/width properties
		element:this.parent.element,
		fx:this.parent.fx,
		label:this.parent.label,
		validate:this.parent.validate,

		/**
		 * Methods
		 */
		$:this.parent.$,
		position:null, //find the position; maybe put this in the element class?

		/**
		 * Properties
		 */
		ie:this.parent.ie,
		css3:this.parent.css3
	};

	/**
	 * Element class provides CRUD methods.
	 */
	element=
	{
		/**
		 * Creates an element.
		 *
		 * @param {String} Type of element to create.
		 * @param {Array} Literal array of attributes for the new element.
		 * @param {String} Optional target element ID.
		 */
		create:function(element,args,target)
		{
			if (typeof args=="object")
			{
				var obj=document.createElement(element);
				for (attribute in args)
				{
					switch(attribute[0])
					{
					case "class":
						(this.ie)?obj.setAttribute("className",attribute[1]):href.setAttribute("class",attribute[1]);
						break;
					case "innerHTML":
					case "type":
					case "src":
						obj.attribute[0]=attribute[1];
						break;
					default:
						obj.setAttribute(attribute[0],attribute[1]);
						break;
					};
				}
				((target==undefined)||(!$(target)))?document.body.appendChild(obj):target.appendChild(obj);
			}
			else
			{
				throw label.exception.err3;
			}
		},

		/**
		 * Destroys an element if it exists.
		 *
		 * @param {Integer} Target element ID.
		 */
		destroy:function(id)
		{
			if ($(id)) { document.body.removeChild($(id)); }
		},

		/**
		 * Resets an element.
		 *
		 * @param {Integer} Target element ID.
		 */
		reset:function(id)
		{
			if ($(id))
			{
				var element=$(id);
				switch(typeof element)
				{
				case "form":
					$(id).reset();
					break;
				case "object":
				default:
					element.update(id,[["innerHTML",""]]);
					break;
				}
			}
			else
			{
				throw label.exception.err1;
			}
		},

		/**
		 * Updates an element.
		 *
		 * @param {Integer} Target element ID.
		 * @param {Array} Literal array of attributes and values.
		 */
		update:function(id,args)
		{
			if ($(id))
			{
				if (typeof args=="object")
				{
					var obj=$(id);
					for (attribute in args)
					{
						switch(attribute[0])
						{
						case "class":
							(this.ie)?obj.setAttribute("className",attribute[1]):href.setAttribute("class",attribute[1]);
							break;
						case "innerHTML":
						case "type":
						case "src":
							obj.attribute[0]=attribute[1];
							break;
						default:
							obj.setAttribute(attribute[0],attribute[1]);
							break;
						};
					}
				}
				else
				{
					throw label.exception.err3;
				}
			}
			else
			{
				throw label.exception.err1;
			}
		}
	};

	/**
	 * GUI effects
	 */
	fx=
	{
		/**
		 * Changes an element's opacity to the supplied value.
		 */
		opacity:function(id,opacity)
		{
			if ($(id))
			{
				var style=$(id).style;
				style.opacity=(opacity/100);
				style.MozOpacity=(opacity/100);
				style.KhtmlOpacity=(opacity/100);
				style.filter="alpha(opacity="+opacity+")";
			}
		},

		/**
		 * Changes an element's opacity to the supplied value, spanning a supplied timeframe.
		 *
		 * @TODO replace aFrame hook with a reference to the parent object/id/name
		 */
		opacityChange:function(id,start,end,ms)
		{
			var speed=Math.round(ms/100);
			var timer=0;

			if (start>end)
			{
				for (i=start;i>=end;i--)
				{
					setTimeout("aFrame.fx.opacity('"+id+"',"+i+")",(timer*speed));
					timer++;
				}
			}
			else if (start<end)
			{
				for (i=start;i<=end;i++)
				{
					setTimeout("aFrame.fx.opacity('"+id+"',"+i+")",(timer*speed));
					timer++;
				}
			}
		},

		/**
		 * Shifts an element's opacity, spanning a supplied timeframe.
		 */
		opacityShift:function(id,ms)
		{
			($(id).style.opacity==0)?opacityChange(id,0,100,ms):opacityChange(id,100,0,ms);
		}
	};

	/**
	 * Label collection / language pack.
	 * Overload this to change languages..
	 */
	var label=
	{
		exception:
		{
			err1:"Couldn't find target element.",
			err2:"A server error has occurred.",
			err3:"Expected an array."
		},

		element:
		{
			back:"Back",
			cancel:"Cancel",
			cont:"Continue",
			del:"Delete",
			edit:"Edit",
			loading:"Loading",
			next:"Next",
			login:"Login",
			save:"Save",
			submit:"Submit"
		},

		month:
		{
			1:"January",
			2:"February",
			3:"March",
			4:"April",
			5:"May",
			6:"June",
			7:"July",
			8:"August",
			9:"September",
			10:"October",
			11:"November",
			12:"December"
		}
	};

	/**
	 * Form validation.
	 */
	var validate=
	{
	};

	return constructor;
}();<|endoftext|>


<commit_before>import _ from 'lodash';
import fs from 'fs-extra';
import {exec} from 'child_process';
import {join} from 'path';
import {default as debugLib} from 'debug';

import options from './options';

import {set as trackSet, track} from 'util/track';
import hrMillis from 'util/hrMillis';

function createDebugger(suffix){
  return debugLib(`serverless-plugin-iopipe:${suffix}`);
}

class ServerlessIOpipePlugin {
  constructor(sls = {}, opts) {
    this.sls = sls;
    this.setOptions(opts);
    this.prefix = opts.prefix || this.sls.config.servicePath || process.env.npm_config_prefix;
    trackSet(this);
    this.package = {};
    this.funcs = [];
    this.originalServicePath = this.sls.config.servicePath;
    this.handlerFileName = 'iopipe-handlers';
    this.commands = {
      iopipe: {
        usage: 'Automatically wraps your function handlers in IOpipe, so you don\'t have to.',
        lifecycleEvents: [
          'run'
        ]
      }
    };
    this.hooks = {
      'before:package:createDeploymentArtifacts': this.run.bind(this),
      'after:package:createDeploymentArtifacts': this.finish.bind(this),
      'iopipe:run': this.greeting.bind(this)
    };
  }
  log(arg1, ...rest){
    //sls doesn't actually support multiple args to log?
    const logger = this.sls.cli.log || console.log;
    logger.call(this.sls.cli, `serverless-plugin-iopipe: ${arg1}`, ...rest);
  }
  greeting(){
    this.log('Welcome to the IOpipe Serverless plugin.');
    const {token} = options();
    if (token){
      this.log('You have your token specified, so you are all set! Just run sls deploy for the magic.');
    } else {
      this.log('Whoops! You are missing the iopipeToken custom variable in your serverless.yml');
    }
  }
  async run(){
    const start = process.hrtime();
    track({
      action: 'run-start'
    });
    this.log('Wrapping your functions with IO|...');
    this.setPackage();
    this.checkForLib();
    this.checkToken();
    this.upgradeLib();
    this.getFuncs();
    this.createFile();
    this.assignHandlers();
    track({
      action: 'run-finish',
      value: hrMillis(start)
    });
  }
  setPackage(){
    try {
      this.package = fs.readJsonSync(join(this.prefix, 'package.json'));
    } catch (err){
      this.package = {};
    }
  }
  setOptions(opts){
    const debug = createDebugger('setOptions');
    const custom = _.chain(this.sls)
      .get('service.custom')
      .pickBy((val, key) => key.match(/^iopipe/))
      .mapKeys((val, key) => _.camelCase(key.replace(/^iopipe/, '')))
      .mapValues((val, key) => {
        if (key === 'exclude' && _.isString(val)){
          return val.split(',');
        }
        return val;
      })
      .value();
    const envVars = _.chain(process.env)
      .pickBy((val, key) => key.match(/^IOPIPE/))
      .mapKeys((val, key) => _.camelCase(key.replace(/^IOPIPE/, '')))
      .value();
    const val = _.defaults(opts, custom, envVars);
    debug('Options object:', val);
    track({
      action: 'options-set',
      value: val
    });
    options(val);
  }
  checkForLib(pack = this.package){
    const {dependencies} = pack;
    if (_.isEmpty(pack) || !_.isPlainObject(pack)){
      track({
        action: 'no-package-skip-lib-check'
      });
      this.log('No package.json found, skipping lib check.');
      return 'no-package-skip';
    } else if (_.isPlainObject(pack) && !dependencies.iopipe){
      if (options().noVerify){
        this.log('Skipping iopipe module installation check.');
        return 'no-verify-skip';
      }
      track({
        action: 'lib-not-found'
      });
      throw new Error('IOpipe module not found in package.json. Make sure to install it via npm or yarn, or use the --noVerify option for serverless-plugin-iopipe.');
    }
    return true;
  }
  checkToken(){
    const token = options().token;
    if (!token){
      track({
        action: 'token-missing'
      });
      throw new Error('No iopipe token found. Specify "iopipeToken" in the "custom" object in serverless.yml.');
    }
    return true;
  }
  upgradeLib(targetVerison, preCmd = 'echo Installing.'){
    if (options().noUpgrade){
      return 'no-upgrade';
    }
    const debug = createDebugger('upgrade');
    let wantedVersion = targetVerison;
    const files = fs.readdirSync(this.prefix);
    const useYarn = _.includes(files, 'yarn.lock');
    const packageManager = useYarn ? 'yarn' : 'npm';
    debug(`Using pkg manager: ${packageManager}`);
    track({
      action: 'lib-upgrade',
      label: packageManager
    });
    return new Promise((resolve, reject) => {
      exec(`${packageManager} outdated iopipe`, (err1, stdout1 = '', stderr1 = '') => {
        if (stderr1 || err1){
          this.log('Could not finish upgrading IOpipe automatically.');
          debug(`Err from ${packageManager} outdated:`, stderr1 || err1);
          track({
            action: `${packageManager}-outdated-error`,
            value: stderr1 || err1
          });
          return reject('err-npm-outdated');
        }
        const arr = stdout1.split('\n');
        debug(`From ${packageManager} outdated command: `, arr);
        const line2Arr = _.compact((arr[packageManager === 'yarn' ? 2 : 1] || '').split(' '));
        const libName = line2Arr[0];
        wantedVersion = targetVerison || line2Arr[2];
        if ((libName === 'iopipe' && wantedVersion) || targetVerison){
          // set version to newer
          debug(`Attempting to upgrade to ${wantedVersion}`);
          this.package.dependencies.iopipe = wantedVersion;
          // write package.json to file
          fs.writeFileSync(join(this.prefix, 'package.json'), JSON.stringify(this.package, null, '  '));
          debug(`Executing ${packageManager} install`);
          return exec(`${preCmd} && ${packageManager} install && echo $?`, (err2, stdout2 = '', stderr2 = '') => {
            if (err2){
              console.log(err2);
              return reject(err2);
            }
            const exitCode = _.chain(stdout2)
              .defaultTo('')
              .split('\n')
              .compact()
              .last()
              .value();
            if (exitCode === '0'){
              track({
                action: 'lib-upgrade-success',
                value: stderr1 || err1
              });
              this.log(`Upgraded IOpipe to ${wantedVersion} automatically. 💪`);
              return resolve(`success-upgrade-${packageManager}-${wantedVersion}`);
            }
            this.log('Ran into an error attempting to upgrade IOpipe automatically.');
            const err = stderr2 || stderr1 || 'Unknown error.';
            this.log(err);
            track({
              action: 'lib-upgrade-error',
              value: err
            });
            return reject(`err-install-${packageManager}`);
          });
        } else if (!libName){
          this.log('You have the latest IOpipe library. Nice work!');
          return resolve(`success-no-upgrade-${packageManager}`);
        }
        this.log('Something went wrong trying to upgrade IOpipe automatically.');
        track({
          action: 'lib-upgrade-error',
          value: 'unknown'
        });
        return reject('err-unknown');
      });
    });
  }
  getFuncs(){
    try {
      const {servicePath} = this.sls.config;
      this.funcs = _.chain(this.sls.service.functions)
        .omit(options().exclude)
        .toPairs()
        //filter out functions that are not Node.js
        .reject(arr => {
          const key = arr[0];
          const obj = arr[1];
          if (_.isString(obj.runtime) && !obj.runtime.match('node')){
            this.log(`Function "${key}" is not Node.js. Currently the plugin only supports Node.js functions. Skipping ${key}.`);
            return true;
          }
          return false;
        })
        .map(arr => {
          const key = arr[0];
          const obj = arr[1];
          const handlerArr = _.isString(obj.handler) ? obj.handler.split('.') : [];
          const relativePath = handlerArr.slice(0, -1).join('.');
          const path = `${servicePath}/${relativePath}.js`;
          return _.assign({}, obj, {
            method: _.last(handlerArr),
            path,
            name: key,
            relativePath
          });
        })
        .value();
      track({
        action: 'funcs-count',
        value: this.funcs.length
      });
    } catch (err){
      track({
        action: 'get-funcs-fail',
        value: err
      });
      console.error('Failed to read functions from serverless.yml.');
      throw new Error(err);
    }
  }
  createFile(){
    const debug = createDebugger('createFile');
    debug('Creating file');
    const iopipeInclude = `const iopipe = require('iopipe')({token: '${options().token}'});`;
    const funcContents = _.chain(this.funcs)
      .map(obj => {
        return `exports['${obj.name}'] = iopipe(require('./${obj.relativePath}').${obj.method});`;
      })
      .join('\n')
      .value();
    const contents = `${iopipeInclude}\n\n${funcContents}`;
    fs.writeFileSync(join(this.originalServicePath, `${this.handlerFileName}.js`), contents);
  }
  assignHandlers(){
    const debug = createDebugger('assignHandlers');
    debug('Assigning iopipe handlers to sls service');
    this.funcs.forEach(obj => {
      _.set(this.sls.service.functions, `${obj.name}.handler`, `${this.handlerFileName}.${obj.name}`);
    });
  }
  finish(){
    const debug = createDebugger('finish');
    this.log('Successfully wrapped Node.js functions with IOpipe, cleaning up.');
    debug(`Removing ${this.handlerFileName}.js`);
    fs.removeSync(join(this.originalServicePath, `${this.handlerFileName}.js`));
    track({
      action: 'finish'
    }).then(_.noop).catch(debug);
  }
}

module.exports = ServerlessIOpipePlugin;
<commit_msg>allow hanlders like this `module.exports = () => {}`<commit_after>import _ from 'lodash';
import fs from 'fs-extra';
import {exec} from 'child_process';
import {join} from 'path';
import {default as debugLib} from 'debug';

import options from './options';

import {set as trackSet, track} from 'util/track';
import hrMillis from 'util/hrMillis';

function createDebugger(suffix){
  return debugLib(`serverless-plugin-iopipe:${suffix}`);
}

class ServerlessIOpipePlugin {
  constructor(sls = {}, opts) {
    this.sls = sls;
    this.setOptions(opts);
    this.prefix = opts.prefix || this.sls.config.servicePath || process.env.npm_config_prefix;
    trackSet(this);
    this.package = {};
    this.funcs = [];
    this.originalServicePath = this.sls.config.servicePath;
    this.handlerFileName = 'iopipe-handlers';
    this.commands = {
      iopipe: {
        usage: 'Automatically wraps your function handlers in IOpipe, so you don\'t have to.',
        lifecycleEvents: [
          'run'
        ]
      }
    };
    this.hooks = {
      'before:package:createDeploymentArtifacts': this.run.bind(this),
      'after:package:createDeploymentArtifacts': this.finish.bind(this),
      'iopipe:run': this.greeting.bind(this)
    };
  }
  log(arg1, ...rest){
    //sls doesn't actually support multiple args to log?
    const logger = this.sls.cli.log || console.log;
    logger.call(this.sls.cli, `serverless-plugin-iopipe: ${arg1}`, ...rest);
  }
  greeting(){
    this.log('Welcome to the IOpipe Serverless plugin.');
    const {token} = options();
    if (token){
      this.log('You have your token specified, so you are all set! Just run sls deploy for the magic.');
    } else {
      this.log('Whoops! You are missing the iopipeToken custom variable in your serverless.yml');
    }
  }
  async run(){
    const start = process.hrtime();
    track({
      action: 'run-start'
    });
    this.log('Wrapping your functions with IO|...');
    this.setPackage();
    this.checkForLib();
    this.checkToken();
    this.upgradeLib();
    this.getFuncs();
    this.createFile();
    this.assignHandlers();
    track({
      action: 'run-finish',
      value: hrMillis(start)
    });
  }
  setPackage(){
    try {
      this.package = fs.readJsonSync(join(this.prefix, 'package.json'));
    } catch (err){
      this.package = {};
    }
  }
  setOptions(opts){
    const debug = createDebugger('setOptions');
    const custom = _.chain(this.sls)
      .get('service.custom')
      .pickBy((val, key) => key.match(/^iopipe/))
      .mapKeys((val, key) => _.camelCase(key.replace(/^iopipe/, '')))
      .mapValues((val, key) => {
        if (key === 'exclude' && _.isString(val)){
          return val.split(',');
        }
        return val;
      })
      .value();
    const envVars = _.chain(process.env)
      .pickBy((val, key) => key.match(/^IOPIPE/))
      .mapKeys((val, key) => _.camelCase(key.replace(/^IOPIPE/, '')))
      .value();
    const val = _.defaults(opts, custom, envVars);
    debug('Options object:', val);
    track({
      action: 'options-set',
      value: val
    });
    options(val);
  }
  checkForLib(pack = this.package){
    const {dependencies} = pack;
    if (_.isEmpty(pack) || !_.isPlainObject(pack)){
      track({
        action: 'no-package-skip-lib-check'
      });
      this.log('No package.json found, skipping lib check.');
      return 'no-package-skip';
    } else if (_.isPlainObject(pack) && !dependencies.iopipe){
      if (options().noVerify){
        this.log('Skipping iopipe module installation check.');
        return 'no-verify-skip';
      }
      track({
        action: 'lib-not-found'
      });
      throw new Error('IOpipe module not found in package.json. Make sure to install it via npm or yarn, or use the --noVerify option for serverless-plugin-iopipe.');
    }
    return true;
  }
  checkToken(){
    const token = options().token;
    if (!token){
      track({
        action: 'token-missing'
      });
      throw new Error('No iopipe token found. Specify "iopipeToken" in the "custom" object in serverless.yml.');
    }
    return true;
  }
  upgradeLib(targetVerison, preCmd = 'echo Installing.'){
    if (options().noUpgrade){
      return 'no-upgrade';
    }
    const debug = createDebugger('upgrade');
    let wantedVersion = targetVerison;
    const files = fs.readdirSync(this.prefix);
    const useYarn = _.includes(files, 'yarn.lock');
    const packageManager = useYarn ? 'yarn' : 'npm';
    debug(`Using pkg manager: ${packageManager}`);
    track({
      action: 'lib-upgrade',
      label: packageManager
    });
    return new Promise((resolve, reject) => {
      exec(`${packageManager} outdated iopipe`, (err1, stdout1 = '', stderr1 = '') => {
        if (stderr1 || err1){
          this.log('Could not finish upgrading IOpipe automatically.');
          debug(`Err from ${packageManager} outdated:`, stderr1 || err1);
          track({
            action: `${packageManager}-outdated-error`,
            value: stderr1 || err1
          });
          return reject('err-npm-outdated');
        }
        const arr = stdout1.split('\n');
        debug(`From ${packageManager} outdated command: `, arr);
        const line2Arr = _.compact((arr[packageManager === 'yarn' ? 2 : 1] || '').split(' '));
        const libName = line2Arr[0];
        wantedVersion = targetVerison || line2Arr[2];
        if ((libName === 'iopipe' && wantedVersion) || targetVerison){
          // set version to newer
          debug(`Attempting to upgrade to ${wantedVersion}`);
          this.package.dependencies.iopipe = wantedVersion;
          // write package.json to file
          fs.writeFileSync(join(this.prefix, 'package.json'), JSON.stringify(this.package, null, '  '));
          debug(`Executing ${packageManager} install`);
          return exec(`${preCmd} && ${packageManager} install && echo $?`, (err2, stdout2 = '', stderr2 = '') => {
            if (err2){
              console.log(err2);
              return reject(err2);
            }
            const exitCode = _.chain(stdout2)
              .defaultTo('')
              .split('\n')
              .compact()
              .last()
              .value();
            if (exitCode === '0'){
              track({
                action: 'lib-upgrade-success',
                value: stderr1 || err1
              });
              this.log(`Upgraded IOpipe to ${wantedVersion} automatically. 💪`);
              return resolve(`success-upgrade-${packageManager}-${wantedVersion}`);
            }
            this.log('Ran into an error attempting to upgrade IOpipe automatically.');
            const err = stderr2 || stderr1 || 'Unknown error.';
            this.log(err);
            track({
              action: 'lib-upgrade-error',
              value: err
            });
            return reject(`err-install-${packageManager}`);
          });
        } else if (!libName){
          this.log('You have the latest IOpipe library. Nice work!');
          return resolve(`success-no-upgrade-${packageManager}`);
        }
        this.log('Something went wrong trying to upgrade IOpipe automatically.');
        track({
          action: 'lib-upgrade-error',
          value: 'unknown'
        });
        return reject('err-unknown');
      });
    });
  }
  getFuncs(){
    try {
      const {servicePath} = this.sls.config;
      this.funcs = _.chain(this.sls.service.functions)
        .omit(options().exclude)
        .toPairs()
        //filter out functions that are not Node.js
        .reject(arr => {
          const key = arr[0];
          const obj = arr[1];
          if (_.isString(obj.runtime) && !obj.runtime.match('node')){
            this.log(`Function "${key}" is not Node.js. Currently the plugin only supports Node.js functions. Skipping ${key}.`);
            return true;
          }
          return false;
        })
        .map(arr => {
          const key = arr[0];
          const obj = arr[1];
          const handlerArr = _.isString(obj.handler) ? obj.handler.split('.') : [];
          const relativePath = handlerArr.length > 1 ? handlerArr.slice(0, -1).join('.') : handlerArr[0];
          const path = `${servicePath}/${relativePath}.js`;
          return _.assign({}, obj, {
            method: _.last(handlerArr),
            path,
            name: key,
            relativePath
          });
        })
        .value();
      track({
        action: 'funcs-count',
        value: this.funcs.length
      });
    } catch (err){
      track({
        action: 'get-funcs-fail',
        value: err
      });
      console.error('Failed to read functions from serverless.yml.');
      throw new Error(err);
    }
  }
  createFile(){
    const debug = createDebugger('createFile');
    debug('Creating file');
    const iopipeInclude = `const iopipe = require('iopipe')({token: '${options().token}'});`;
    const funcContents = _.chain(this.funcs)
      .map(obj => {
        return `exports['${obj.name}'] = iopipe(require('./${obj.relativePath}').${obj.method});`;
      })
      .join('\n')
      .value();
    const contents = `${iopipeInclude}\n\n${funcContents}`;
    fs.writeFileSync(join(this.originalServicePath, `${this.handlerFileName}.js`), contents);
  }
  assignHandlers(){
    const debug = createDebugger('assignHandlers');
    debug('Assigning iopipe handlers to sls service');
    this.funcs.forEach(obj => {
      _.set(this.sls.service.functions, `${obj.name}.handler`, `${this.handlerFileName}.${obj.name}`);
    });
  }
  finish(){
    const debug = createDebugger('finish');
    this.log('Successfully wrapped Node.js functions with IOpipe, cleaning up.');
    debug(`Removing ${this.handlerFileName}.js`);
    fs.removeSync(join(this.originalServicePath, `${this.handlerFileName}.js`));
    track({
      action: 'finish'
    }).then(_.noop).catch(debug);
  }
}

module.exports = ServerlessIOpipePlugin;
<|endoftext|>


<commit_before>'use strict';
var React = require('react');
var _ = require('underscore');
var globals = require('../globals');
var form = require('../../libs/bootstrap/form');
var Form = form.Form;
var Input = form.Input;
var FormMixin = form.FormMixin;

var queryKeyValue = globals.queryKeyValue;
var editQueryValue = globals.editQueryValue;

// Import individual tab components
var CurationInterpretationCriteria = require('./interpretation/criteria').CurationInterpretationCriteria;
var CurationInterpretationBasicInfo = require('./interpretation/basic_info').CurationInterpretationBasicInfo;
var CurationInterpretationPopulation = require('./interpretation/population').CurationInterpretationPopulation;
var CurationInterpretationComputational = require('./interpretation/computational').CurationInterpretationComputational;
var CurationInterpretationFunctional = require('./interpretation/functional').CurationInterpretationFunctional;
var CurationInterpretationSegregation = require('./interpretation/segregation').CurationInterpretationSegregation;
var CurationInterpretationGeneSpecific = require('./interpretation/gene_specific').CurationInterpretationGeneSpecific;

// Import pathogenicity calculator
var calculator = require('./interpretation/shared/calculator');
var PathogenicityCalculator = calculator.PathogenicityCalculator;

var validTabs = ['basic-info', 'population', 'predictors', 'experimental', 'segregation-case', 'gene-centric'];

// Curation data header for Gene:Disease
var VariantCurationInterpretation = module.exports.VariantCurationInterpretation = React.createClass({
    propTypes: {
        variantData: React.PropTypes.object, // ClinVar data payload
        interpretation: React.PropTypes.object,
        ext_myGeneInfo: React.PropTypes.object,
        href_url: React.PropTypes.object,
        updateInterpretationObj: React.PropTypes.func,
        ext_myVariantInfo: React.PropTypes.object,
        ext_bustamante: React.PropTypes.object,
        ext_ensemblVEP: React.PropTypes.array,
        ext_ensemblVariation: React.PropTypes.object,
        ext_ensemblHgvsVEP: React.PropTypes.array,
        ext_clinvarEutils: React.PropTypes.object,
        ext_clinVarEsearch: React.PropTypes.object,
        ext_clinVarRCV: React.PropTypes.array,
        ext_ensemblGeneId: React.PropTypes.string,
        ext_geneSynonyms: React.PropTypes.array,
        isClinVarLoading: React.PropTypes.bool
    },

    getInitialState: function() {
        return {
            interpretation: this.props.interpretation,
            ext_myGeneInfo: this.props.ext_myGeneInfo,
            ext_myVariantInfo: this.props.ext_myVariantInfo,
            ext_bustamante: this.props.ext_bustamante,
            ext_ensemblVEP: this.props.ext_ensemblVEP,
            ext_ensemblVariation: this.props.ext_ensemblVariation,
            ext_ensemblHgvsVEP: this.props.ext_ensemblHgvsVEP,
            ext_clinvarEutils: this.props.ext_clinvarEutils,
            ext_clinVarEsearch: this.props.ext_clinVarEsearch,
            ext_clinVarRCV: this.props.ext_clinVarRCV,
            ext_ensemblGeneId: this.props.ext_ensemblGeneId,
            ext_geneSynonyms: this.props.ext_geneSynonyms,
            isClinVarLoading: this.props.isClinVarLoading,
            //remember current tab/subtab so user will land on that tab when interpretation starts
            selectedTab: (this.props.href_url.href ? (queryKeyValue('tab', this.props.href_url.href) ? (validTabs.indexOf(queryKeyValue('tab', this.props.href_url.href)) > -1 ? queryKeyValue('tab', this.props.href_url.href) : 'basic-info') : 'basic-info')  : 'basic-info'),
        };
    },

    componentWillReceiveProps: function(nextProps) {
        // this block is for handling props and states when props (external data) is updated after the initial load/rendering
        // when props are updated, update the parent interpreatation object, if applicable
        if (typeof nextProps.interpretation !== undefined && !_.isEqual(nextProps.interpretation, this.props.interpretation)) {
            this.setState({interpretation: nextProps.interpretation});
        }
        if (nextProps.ext_myGeneInfo) {
            this.setState({ext_myGeneInfo: nextProps.ext_myGeneInfo});
        }
        if (nextProps.ext_myVariantInfo) {
            this.setState({ext_myVariantInfo: nextProps.ext_myVariantInfo});
        }
        if (nextProps.ext_bustamante) {
            this.setState({ext_bustamante: nextProps.ext_bustamante});
        }
        if (nextProps.ext_ensemblVEP) {
            this.setState({ext_ensemblVEP: nextProps.ext_ensemblVEP});
        }
        if (nextProps.ext_ensemblVariation) {
            this.setState({ext_ensemblVariation: nextProps.ext_ensemblVariation});
        }
        if (nextProps.ext_ensemblHgvsVEP) {
            this.setState({ext_ensemblHgvsVEP: nextProps.ext_ensemblHgvsVEP});
        }
        if (nextProps.ext_clinvarEutils) {
            this.setState({ext_clinvarEutils: nextProps.ext_clinvarEutils});
        }
        if (nextProps.ext_clinVarEsearch) {
            this.setState({ext_clinVarEsearch: nextProps.ext_clinVarEsearch});
        }
        if (nextProps.ext_clinVarRCV) {
            this.setState({ext_clinVarRCV: nextProps.ext_clinVarRCV, isClinVarLoading: nextProps.isClinVarLoading});
        }
        if (nextProps.ext_ensemblGeneId) {
            this.setState({ext_ensemblGeneId: nextProps.ext_ensemblGeneId});
        }
        if (nextProps.ext_geneSynonyms) {
            this.setState({ext_geneSynonyms: nextProps.ext_geneSynonyms});
        }
    },

    // set selectedTab to whichever tab the user switches to, and update the address accordingly
    handleSelect: function (tab) {
        if (tab == 'basic-info' || validTabs.indexOf(tab) == -1) {
            this.setState({selectedTab: 'basic-info'});
            window.history.replaceState(window.state, '', editQueryValue(window.location.href, 'tab', null));
        } else {
            this.setState({selectedTab: tab});
            window.history.replaceState(window.state, '', editQueryValue(window.location.href, 'tab', tab));
        }
    },

    render: function() {
        var variant = this.props.variantData;
        var interpretation = this.state.interpretation;
        var completedSections = this.state.interpretation && this.state.interpretation.completed_sections ? this.state.interpretation.completed_sections : [];
        var populationTabChecked = false;

        // The ordering of TabPanels are corresponding to that of tabs
        // Adding or deleting a tab also requires its corresponding TabPanel to be added/deleted
        return (

                <PathogenicityCalculator interpretation={interpretation} />

 this.handleSelect('basic-info')} aria-selected={this.state.selectedTab == 'basic-info'}>Basic Information

 this.handleSelect('population')} aria-selected={this.state.selectedTab == 'population'}>Population {completedSections.indexOf('population') > -1 ? ✓ : null}

 this.handleSelect('predictors')} aria-selected={this.state.selectedTab == 'predictors'}>Predictors {completedSections.indexOf('predictors') > -1 ? ✓ : null}

 this.handleSelect('experimental')} aria-selected={this.state.selectedTab == 'experimental'}>Experimental {completedSections.indexOf('experimental') > -1 ? ✓ : null}

 this.handleSelect('segregation-case')} aria-selected={this.state.selectedTab == 'segregation-case'}>Segregation/Case {completedSections.indexOf('segregation-case') > -1 ? ✓ : null}

 this.handleSelect('gene-centric')} aria-selected={this.state.selectedTab == 'gene-centric'}>Gene-centric {completedSections.indexOf('gene-centric') > -1 ? ✓ : null}

                    {this.state.selectedTab == '' || this.state.selectedTab == 'basic-info' ?

                        <CurationInterpretationBasicInfo data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_clinvarEutils={this.state.ext_clinvarEutils}
                            ext_ensemblHgvsVEP={this.state.ext_ensemblHgvsVEP}
                            ext_clinVarRCV={this.state.ext_clinVarRCV}
                            isClinVarLoading={this.state.isClinVarLoading} />

                    : null}
                    {this.state.selectedTab == 'population' ?

                        <CurationInterpretationPopulation data={variant}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_myVariantInfo={this.state.ext_myVariantInfo}
                            ext_ensemblVEP={this.state.ext_ensemblVEP}
                            ext_ensemblVariation={this.state.ext_ensemblVariation} />

                    : null}
                    {this.state.selectedTab == 'predictors' ?

                        <CurationInterpretationComputational data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_myVariantInfo={this.state.ext_myVariantInfo}
                            ext_bustamante={this.state.ext_bustamante}
                            ext_clinvarEutils={this.state.ext_clinvarEutils}
                            ext_clinVarEsearch={this.state.ext_clinVarEsearch} />

                    : null}
                    {this.state.selectedTab == 'experimental' ?

                        <CurationInterpretationFunctional data={variant} data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj} />

                    : null}
                    {this.state.selectedTab == 'segregation-case' ?

                        <CurationInterpretationSegregation data={variant} data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj} />

                    : null}
                    {this.state.selectedTab == 'gene-centric' ?

                        <CurationInterpretationGeneSpecific data={variant} data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_myGeneInfo={this.state.ext_myGeneInfo}
                            ext_ensemblGeneId={this.state.ext_ensemblGeneId}
                            ext_geneSynonyms={this.state.ext_geneSynonyms} />

                    : null}

        );
    },
});
<commit_msg>Added setState when setting state for nextProps.ext_ensemblHgvsVEP<commit_after>'use strict';
var React = require('react');
var _ = require('underscore');
var globals = require('../globals');
var form = require('../../libs/bootstrap/form');
var Form = form.Form;
var Input = form.Input;
var FormMixin = form.FormMixin;

var queryKeyValue = globals.queryKeyValue;
var editQueryValue = globals.editQueryValue;

// Import individual tab components
var CurationInterpretationCriteria = require('./interpretation/criteria').CurationInterpretationCriteria;
var CurationInterpretationBasicInfo = require('./interpretation/basic_info').CurationInterpretationBasicInfo;
var CurationInterpretationPopulation = require('./interpretation/population').CurationInterpretationPopulation;
var CurationInterpretationComputational = require('./interpretation/computational').CurationInterpretationComputational;
var CurationInterpretationFunctional = require('./interpretation/functional').CurationInterpretationFunctional;
var CurationInterpretationSegregation = require('./interpretation/segregation').CurationInterpretationSegregation;
var CurationInterpretationGeneSpecific = require('./interpretation/gene_specific').CurationInterpretationGeneSpecific;

// Import pathogenicity calculator
var calculator = require('./interpretation/shared/calculator');
var PathogenicityCalculator = calculator.PathogenicityCalculator;

var validTabs = ['basic-info', 'population', 'predictors', 'experimental', 'segregation-case', 'gene-centric'];

// Curation data header for Gene:Disease
var VariantCurationInterpretation = module.exports.VariantCurationInterpretation = React.createClass({
    propTypes: {
        variantData: React.PropTypes.object, // ClinVar data payload
        interpretation: React.PropTypes.object,
        ext_myGeneInfo: React.PropTypes.object,
        href_url: React.PropTypes.object,
        updateInterpretationObj: React.PropTypes.func,
        ext_myVariantInfo: React.PropTypes.object,
        ext_bustamante: React.PropTypes.object,
        ext_ensemblVEP: React.PropTypes.array,
        ext_ensemblVariation: React.PropTypes.object,
        ext_ensemblHgvsVEP: React.PropTypes.array,
        ext_clinvarEutils: React.PropTypes.object,
        ext_clinVarEsearch: React.PropTypes.object,
        ext_clinVarRCV: React.PropTypes.array,
        ext_ensemblGeneId: React.PropTypes.string,
        ext_geneSynonyms: React.PropTypes.array,
        isClinVarLoading: React.PropTypes.bool
    },

    getInitialState: function() {
        return {
            interpretation: this.props.interpretation,
            ext_myGeneInfo: this.props.ext_myGeneInfo,
            ext_myVariantInfo: this.props.ext_myVariantInfo,
            ext_bustamante: this.props.ext_bustamante,
            ext_ensemblVEP: this.props.ext_ensemblVEP,
            ext_ensemblVariation: this.props.ext_ensemblVariation,
            ext_ensemblHgvsVEP: this.props.ext_ensemblHgvsVEP,
            ext_clinvarEutils: this.props.ext_clinvarEutils,
            ext_clinVarEsearch: this.props.ext_clinVarEsearch,
            ext_clinVarRCV: this.props.ext_clinVarRCV,
            ext_ensemblGeneId: this.props.ext_ensemblGeneId,
            ext_geneSynonyms: this.props.ext_geneSynonyms,
            isClinVarLoading: this.props.isClinVarLoading,
            //remember current tab/subtab so user will land on that tab when interpretation starts
            selectedTab: (this.props.href_url.href ? (queryKeyValue('tab', this.props.href_url.href) ? (validTabs.indexOf(queryKeyValue('tab', this.props.href_url.href)) > -1 ? queryKeyValue('tab', this.props.href_url.href) : 'basic-info') : 'basic-info')  : 'basic-info'),
        };
    },

    componentWillReceiveProps: function(nextProps) {
        // this block is for handling props and states when props (external data) is updated after the initial load/rendering
        // when props are updated, update the parent interpreatation object, if applicable
        if (typeof nextProps.interpretation !== undefined && !_.isEqual(nextProps.interpretation, this.props.interpretation)) {
            this.setState({interpretation: nextProps.interpretation});
        }
        if (nextProps.ext_myGeneInfo) {
            this.setState({ext_myGeneInfo: nextProps.ext_myGeneInfo});
        }
        if (nextProps.ext_myVariantInfo) {
            this.setState({ext_myVariantInfo: nextProps.ext_myVariantInfo});
        }
        if (nextProps.ext_bustamante) {
            this.setState({ext_bustamante: nextProps.ext_bustamante});
        }
        if (nextProps.ext_ensemblVEP) {
            this.setState({ext_ensemblVEP: nextProps.ext_ensemblVEP});
        }
        if (nextProps.ext_ensemblVariation) {
            this.setState({ext_ensemblVariation: nextProps.ext_ensemblVariation});
        }
        if (nextProps.ext_ensemblHgvsVEP) {
            this.setState({ext_ensemblHgvsVEP: nextProps.ext_ensemblHgvsVEP});
            if (!nextProps.ext_clinVarRCV) {
                this.setState({isClinVarLoading: nextProps.isClinVarLoading});
            }
        }
        if (nextProps.ext_clinvarEutils) {
            this.setState({ext_clinvarEutils: nextProps.ext_clinvarEutils});
        }
        if (nextProps.ext_clinVarEsearch) {
            this.setState({ext_clinVarEsearch: nextProps.ext_clinVarEsearch});
        }
        if (nextProps.ext_clinVarRCV) {
            this.setState({ext_clinVarRCV: nextProps.ext_clinVarRCV, isClinVarLoading: nextProps.isClinVarLoading});
        }
        if (nextProps.ext_ensemblGeneId) {
            this.setState({ext_ensemblGeneId: nextProps.ext_ensemblGeneId});
        }
        if (nextProps.ext_geneSynonyms) {
            this.setState({ext_geneSynonyms: nextProps.ext_geneSynonyms});
        }
    },

    // set selectedTab to whichever tab the user switches to, and update the address accordingly
    handleSelect: function (tab) {
        if (tab == 'basic-info' || validTabs.indexOf(tab) == -1) {
            this.setState({selectedTab: 'basic-info'});
            window.history.replaceState(window.state, '', editQueryValue(window.location.href, 'tab', null));
        } else {
            this.setState({selectedTab: tab});
            window.history.replaceState(window.state, '', editQueryValue(window.location.href, 'tab', tab));
        }
    },

    render: function() {
        var variant = this.props.variantData;
        var interpretation = this.state.interpretation;
        var completedSections = this.state.interpretation && this.state.interpretation.completed_sections ? this.state.interpretation.completed_sections : [];
        var populationTabChecked = false;

        // The ordering of TabPanels are corresponding to that of tabs
        // Adding or deleting a tab also requires its corresponding TabPanel to be added/deleted
        return (

                <PathogenicityCalculator interpretation={interpretation} />

 this.handleSelect('basic-info')} aria-selected={this.state.selectedTab == 'basic-info'}>Basic Information

 this.handleSelect('population')} aria-selected={this.state.selectedTab == 'population'}>Population {completedSections.indexOf('population') > -1 ? ✓ : null}

 this.handleSelect('predictors')} aria-selected={this.state.selectedTab == 'predictors'}>Predictors {completedSections.indexOf('predictors') > -1 ? ✓ : null}

 this.handleSelect('experimental')} aria-selected={this.state.selectedTab == 'experimental'}>Experimental {completedSections.indexOf('experimental') > -1 ? ✓ : null}

 this.handleSelect('segregation-case')} aria-selected={this.state.selectedTab == 'segregation-case'}>Segregation/Case {completedSections.indexOf('segregation-case') > -1 ? ✓ : null}

 this.handleSelect('gene-centric')} aria-selected={this.state.selectedTab == 'gene-centric'}>Gene-centric {completedSections.indexOf('gene-centric') > -1 ? ✓ : null}

                    {this.state.selectedTab == '' || this.state.selectedTab == 'basic-info' ?

                        <CurationInterpretationBasicInfo data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_clinvarEutils={this.state.ext_clinvarEutils}
                            ext_ensemblHgvsVEP={this.state.ext_ensemblHgvsVEP}
                            ext_clinVarRCV={this.state.ext_clinVarRCV}
                            isClinVarLoading={this.state.isClinVarLoading} />

                    : null}
                    {this.state.selectedTab == 'population' ?

                        <CurationInterpretationPopulation data={variant}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_myVariantInfo={this.state.ext_myVariantInfo}
                            ext_ensemblVEP={this.state.ext_ensemblVEP}
                            ext_ensemblVariation={this.state.ext_ensemblVariation} />

                    : null}
                    {this.state.selectedTab == 'predictors' ?

                        <CurationInterpretationComputational data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_myVariantInfo={this.state.ext_myVariantInfo}
                            ext_bustamante={this.state.ext_bustamante}
                            ext_clinvarEutils={this.state.ext_clinvarEutils}
                            ext_clinVarEsearch={this.state.ext_clinVarEsearch} />

                    : null}
                    {this.state.selectedTab == 'experimental' ?

                        <CurationInterpretationFunctional data={variant} data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj} />

                    : null}
                    {this.state.selectedTab == 'segregation-case' ?

                        <CurationInterpretationSegregation data={variant} data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj} />

                    : null}
                    {this.state.selectedTab == 'gene-centric' ?

                        <CurationInterpretationGeneSpecific data={variant} data={variant} href_url={this.props.href_url}
                            interpretation={interpretation} updateInterpretationObj={this.props.updateInterpretationObj}
                            ext_myGeneInfo={this.state.ext_myGeneInfo}
                            ext_ensemblGeneId={this.state.ext_ensemblGeneId}
                            ext_geneSynonyms={this.state.ext_geneSynonyms} />

                    : null}

        );
    },
});
<|endoftext|>


<commit_before>// This file defines a plugin that handles the drawing of cell
// selections and the basic user interactions for creating and working
// with such selections. It also makes sure that, after each
// transaction, the shapes of tables are normalized to be rectangular
// and not contain overlapping cells.

import {Plugin} from "prosemirror-state"

import {handleTripleClick, handleKeyDown, handlePaste, handleMouseDown} from "./input"
import {key, cellAround, inSameTable} from "./util"
import {drawCellSelection, normalizeSelection} from "./cellselection"
import {fixTables} from "./fixtables"

// :: () → Plugin
//
// Creates a [plugin](http://prosemirror.net/docs/ref/#state.Plugin)
// that, when added to an editor, enables cell-selection, handles
// cell-based copy/paste, and makes sure tables stay well-formed (each
// row has the same width, and cells don't overlap).
//
// You should probably put this plugin near the end of your array of
// plugins, since it handles mouse and arrow key events in tables
// rather broadly, and other plugins, like the gap cursor or the
// column-width dragging plugin, might want to get a turn first to
// perform more specific behavior.
export function tableEditing() {
  return new Plugin({
    key,

    // This piece of state is used to remember when a mouse-drag
    // cell-selection is happening, so that it can continue even as
    // transactions (which might move its anchor cell) come in.
    state: {
      init() { return null },
      apply(tr, cur) {
        let set = tr.getMeta(key)
        if (set != null) return set == -1 ? null : set
        if (cur == null || !tr.docChanged) return cur
        let {deleted, pos} = tr.mapping.mapResult(cur)
        return deleted ? null : pos
      }
    },

    props: {
      decorations: drawCellSelection,

      handleDOMEvents: {
        mousedown: handleMouseDown
      },

      createSelectionBetween(view) {
        if (key.getState(view.state) != null) return view.state.selection
      },

      handleTripleClick,

      handleKeyDown,

      handlePaste
    },

    appendTransaction(_, oldState, state) {
      return normalizeSelection(state, fixTables(state, oldState))
    }
  })
}

export {fixTables, handlePaste}
export {cellAround, isInTable, selectionCell, moveCellForward, inSameTable, findCell, colCount, nextCell} from "./util";
export {tableNodes} from "./schema"
export {CellSelection} from "./cellselection"
export {TableMap} from "./tablemap"
export * from "./commands"
export {columnResizing} from "./columnresizing"
export {pastedCells as __pastedCells, insertCells as __insertCells, clipCells as __clipCells} from "./copypaste"
<commit_msg>Remove unnecessary imports<commit_after>// This file defines a plugin that handles the drawing of cell
// selections and the basic user interactions for creating and working
// with such selections. It also makes sure that, after each
// transaction, the shapes of tables are normalized to be rectangular
// and not contain overlapping cells.

import {Plugin} from "prosemirror-state"

import {handleTripleClick, handleKeyDown, handlePaste, handleMouseDown} from "./input"
import {key} from "./util"
import {drawCellSelection, normalizeSelection} from "./cellselection"
import {fixTables} from "./fixtables"

// :: () → Plugin
//
// Creates a [plugin](http://prosemirror.net/docs/ref/#state.Plugin)
// that, when added to an editor, enables cell-selection, handles
// cell-based copy/paste, and makes sure tables stay well-formed (each
// row has the same width, and cells don't overlap).
//
// You should probably put this plugin near the end of your array of
// plugins, since it handles mouse and arrow key events in tables
// rather broadly, and other plugins, like the gap cursor or the
// column-width dragging plugin, might want to get a turn first to
// perform more specific behavior.
export function tableEditing() {
  return new Plugin({
    key,

    // This piece of state is used to remember when a mouse-drag
    // cell-selection is happening, so that it can continue even as
    // transactions (which might move its anchor cell) come in.
    state: {
      init() { return null },
      apply(tr, cur) {
        let set = tr.getMeta(key)
        if (set != null) return set == -1 ? null : set
        if (cur == null || !tr.docChanged) return cur
        let {deleted, pos} = tr.mapping.mapResult(cur)
        return deleted ? null : pos
      }
    },

    props: {
      decorations: drawCellSelection,

      handleDOMEvents: {
        mousedown: handleMouseDown
      },

      createSelectionBetween(view) {
        if (key.getState(view.state) != null) return view.state.selection
      },

      handleTripleClick,

      handleKeyDown,

      handlePaste
    },

    appendTransaction(_, oldState, state) {
      return normalizeSelection(state, fixTables(state, oldState))
    }
  })
}

export {fixTables, handlePaste}
export {cellAround, isInTable, selectionCell, moveCellForward, inSameTable, findCell, colCount, nextCell} from "./util";
export {tableNodes} from "./schema"
export {CellSelection} from "./cellselection"
export {TableMap} from "./tablemap"
export * from "./commands"
export {columnResizing} from "./columnresizing"
export {pastedCells as __pastedCells, insertCells as __insertCells, clipCells as __clipCells} from "./copypaste"
<|endoftext|>


<commit_before>var fs = require('fs')
var _ = require('lodash')
var disk = require('./disk')

function lodashChain(array, cb) {
  var chain = _.chain(array)

  function addCallbackOnValue(c) {
    c.value = _.flow(c.value, function(arg) {
      cb()
      return arg
    })
  }

  addCallbackOnValue(chain)

  _.functions(chain)
    .forEach(function(method) {
      chain[method] = _.flow(chain[method], function(arg) {
        var isChain = _.isObject(arg) && arg.__chain__
        if (isChain) addCallbackOnValue(arg)
        return arg
      })
    })

  return chain
}

function lowChain(array, cb) {
  var chain = _.chain(array)

  _.functions(chain)
    .forEach(function(method) {
      chain[method] = _.flow(chain[method], function(arg) {
        var res = arg.value ? arg.value() : arg
        cb()
        return res
      })
    })

  return chain
}

function low(file, options) {
  var checksum

  options = _.assign({
    autosave: true,
    async: true
  }, options)

  function save() {
    if (file && options.autosave) {
      var str = low.stringify(db.object)
      if (str === checksum) return
      checksum = str
      options.async ? disk.write(file, str) : disk.writeSync(file, str)
    }
  }

  function db(key) {
    if (db.object[key]) {
      var array = db.object[key]
    } else {
      var array = db.object[key] = []
      save()
    }

    var short = lowChain(array, save)
    short.chain = function() {
      return lodashChain(array, save)
    }
    return short
  }

  db.save = function(f) {
    f = f ? f : file
    disk.write(f, low.stringify(db.object))
  }

  db.saveSync = function(f) {
    f = f ? f : file
    disk.writeSync(f, low.stringify(db.object))
  }

  db.object = {}

  if (file) {
    var data = disk.read(file)
    if (data) {
      db.object = low.parse(data)
    } else {
      db.saveSync()
    }
  }

  return db
}

low.mixin = function(arg) {
  _.mixin(arg)
}

low.stringify = function(obj) {
  return JSON.stringify(obj, null, 2)
}

low.parse = function(str) {
  return JSON.parse(str)
}

module.exports = low
<commit_msg>Add file name to JSON parse errors<commit_after>var fs = require('fs')
var _ = require('lodash')
var disk = require('./disk')

function lodashChain(array, cb) {
  var chain = _.chain(array)

  function addCallbackOnValue(c) {
    c.value = _.flow(c.value, function(arg) {
      cb()
      return arg
    })
  }

  addCallbackOnValue(chain)

  _.functions(chain)
    .forEach(function(method) {
      chain[method] = _.flow(chain[method], function(arg) {
        var isChain = _.isObject(arg) && arg.__chain__
        if (isChain) addCallbackOnValue(arg)
        return arg
      })
    })

  return chain
}

function lowChain(array, cb) {
  var chain = _.chain(array)

  _.functions(chain)
    .forEach(function(method) {
      chain[method] = _.flow(chain[method], function(arg) {
        var res = arg.value ? arg.value() : arg
        cb()
        return res
      })
    })

  return chain
}

function low(file, options) {
  var checksum

  options = _.assign({
    autosave: true,
    async: true
  }, options)

  function save() {
    if (file && options.autosave) {
      var str = low.stringify(db.object)
      if (str === checksum) return
      checksum = str
      options.async ? disk.write(file, str) : disk.writeSync(file, str)
    }
  }

  function db(key) {
    if (db.object[key]) {
      var array = db.object[key]
    } else {
      var array = db.object[key] = []
      save()
    }

    var short = lowChain(array, save)
    short.chain = function() {
      return lodashChain(array, save)
    }
    return short
  }

  db.save = function(f) {
    f = f ? f : file
    disk.write(f, low.stringify(db.object))
  }

  db.saveSync = function(f) {
    f = f ? f : file
    disk.writeSync(f, low.stringify(db.object))
  }

  db.object = {}

  if (file) {
    var data = disk.read(file)
    if (data) {
      try {
        db.object = low.parse(data)
      } catch (e) {
        e.message += ' (' + file + ')'
        throw e
      }
    } else {
      db.saveSync()
    }
  }

  return db
}

low.mixin = function(arg) {
  _.mixin(arg)
}

low.stringify = function(obj) {
  return JSON.stringify(obj, null, 2)
}

low.parse = function(str) {
  return JSON.parse(str)
}

module.exports = low
<|endoftext|>


<commit_before>// Import React
import React from "react";

// Import Spectacle Core tags
import {
  // Appear,
  // BlockQuote,
  // Cite,
  // Code,
  CodePane,
  Deck,
  // Fill,
  Heading,
  Image,
  // Layout,
  Link,
  // ListItem,
  // List,
  Markdown,
  // Quote,
  Slide,
  Spectacle,
  Text
} from "spectacle";

// Import image preloader util
import preloader from "spectacle/lib/utils/preloader";

// Import theme
import theme from "../themes/formidable/index";

// Require CSS
require("normalize.css");
require("spectacle/lib/themes/default/index.css");
require("../themes/formidable/index.css");

const images = {
  logo: require("../assets/formidable-logo.svg")
};

preloader(images);

// ----------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------
// A naive, indent preserving strip.
//
// TODO: Extract and open source
// https://github.com/FormidableLabs/gulp-html-extract/blob/master/lib/strip-indent.js
const strip = (val) => {
  // Find first line with text. Capture that indent level.
  let indent = null;

  return val.split("\n")
    .map((line) => {
      // Capture initial indent.
      if (indent === null && /^\s/.test(line)) {
        indent = /^ */.exec(line)[0];
      }

      // If no indent, ignore.
      if (indent === null) {
        return null;
      }

      return line
        .replace(new RegExp(`^${indent}`), "")
        .replace(/\s*$/, "");
    })
    .join("\n")
    .replace(/^\s*|\s$/, "");
};

// Standard Markdown Slide
const markdown = (text, style) => {
  style = Object.assign({width: "75%", margin: "0 auto"}, style || {});
  return (

      <Markdown>
        {strip(text)}
      </Markdown>

  );
};

// ----------------------------------------------------------------------------
// The Deck
// ----------------------------------------------------------------------------
export default class Presentation extends React.Component {
  render() {
    return (
      <Spectacle theme={theme}>
        <Deck progress="bar" transition={["slide"]} transitionDuration={500}>
          {/* -----------------------------------------------------------------
            * Title
            * ----------------------------------------------------------------- */}
          <Slide bgColor="primary">
            <Heading size={1} fit caps lineHeight={1} textColor="black">
              Builder
            </Heading>
            <Heading size={1} fit caps>
              Multi-project npm workflows
            </Heading>
            <Link href="https://github.com/FormidableLabs/builder">
              <Text bold textColor="tertiary" style={{margin: "1em auto"}}>
                github.com/FormidableLabs/builder
              </Text>
            </Link>
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Introduction
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## MODERN JAVASCRIPT
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ![React So Hot](https://camo.githubusercontent.com/a85f7c2c03b36655323ec7a3250057233e82ef55/68747470733a2f2f692e696d6775722e636f6d2f695549497571622e6a7067)
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ### SO MANY TOOLS

              * **Build**: JSX, ES6, Babel, Webpack
              * **Test Tools**: Magellan, Karma, Mocha, Enzyme
              * **Quality**: Eslint, Istanbul
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ### SO MANY WORKFLOWS

              * **Watch**: dev/test JS, Karma
              * **Build**: clean, build, prod/dev
              * **Test**: client, node, coverage
              * **Publish**: git, npm
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## SO MANY PROJECTS

              * **~60** React components
              * **~10** Hapi plugins, helpers
              * **~15+** App servers
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              _welcome to_ ...

              ## [JAVASCRIPT FATIGUE](https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4#.xioz52ymh)
            `)/*eslint-enable max-len*/}
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Builder
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## BUILDER

              Your \`package.json\`, **abstracted** and **managed**
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ### USE CASES

              * **Multiple** projects
              * Nearly **identical**
              * Complex **workflows**
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### GUIDING PRINCIPLES

              * It's "**just npm**"
              * **Avoid** "buying the farm"
              * You can [**give up**](https://github.com/FormidableLabs/builder#i-give-up-how-do-i-abandon-builder)
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ## ECOSYSTEM

              * [**\`builder\`**](https://github.com/FormidableLabs/builder):
                Task runner.
              * [**\`builder-init\`**](https://github.com/FormidableLabs/builder-init):
                Project generator.
              * **\`builder-ARCHETYPE\`**: Project configs, workflows, etc.
            `, { width: "90%" })}
          </Slide>
          <Slide>
            {markdown(`
              ## HOW IT WORKS

              * \`builder\` replaces \`npm run\`
              * Archetype contains \`scripts\` + configs
              * \`builder\` magically _merges_ the archetype into your project
            `, { width: "95%" })}
          </Slide>
          <Slide>
            {markdown(`
              ## GOALS

              * Developers easily **use**
              * Architects centrally **control**
              * Projects can **flexibly** deviate
            `, { width: "80%" })}
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Builder
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## BUILDER

              A replacement for **\`npm run\`** with a _few special extras_
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ## ACTIONS

              * [\`builder run TASK\`](https://github.com/FormidableLabs/builder#builder-run)
              * [\`builder concurrent TASK1 TASK2\`](https://github.com/FormidableLabs/builder#builder-concurrent)
              * [\`builder envs TASK JSON_ARRAY\`](https://github.com/FormidableLabs/builder#builder-envs)
            `, { width: "90%" })/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ## THE "MAGIC"

              * Mutate **\`PATH\`** to include ARCHETYPE **\`.bin\`**
              * Mutate **\`NODE_PATH\`** to include ARCHETYPE **\`node_modules\`**
              * Merge archetype and project **\`package.json:scripts\`**
            `, { width: "90%" })}
          </Slide>
          <Slide>
            {markdown(`
              ## DEMO
            `)}
            <CodePane
              source={strip(`
                \$ builder run down
                \$ builder run fail
                \$ builder run --tries=3 fail

                \$ builder concurrent down down down
                \$ builder envs msg '[{"MSG": "hi"}, {"MSG": "yo"}]'
              `)}
            />
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Archetypes
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## ARCHETYPES

              The "mission control" for a set of identical projects
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## ASSESSMENT

              * Identify project "types"
              * Aim for **one** archetype per project type
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ## EXAMPLES

              * [\`builder-react-component\`](https://github.com/FormidableLabs/builder-react-component)
              * \`builder-react-app\`
              * \`builder-hapi-plugin\`
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ## CONTENTS

              * NPM tasks
              * Configurations
              * Boilerplate files
              * Documentation
              * Templates (_forthcoming_)
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### TASKS

              * [\`formidable-react-component-boilerplate/package.json\`](https://github.com/FormidableLabs/formidable-react-component-boilerplate/package.json)
              * [\`builder-react-component/package.json\`](https://github.com/FormidableLabs/builder-react-component/package.json)
              * **Note**: Special **\`npm:*\`** tasks
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### DEPENDENCIES

              * [\`formidable-react-component-boilerplate/package.json\`](https://github.com/FormidableLabs/formidable-react-component-boilerplate/package.json)
              * [\`builder-react-component/package.json\`](https://github.com/FormidableLabs/builder-react-component/package.json)
              * [\`builder-react-component/dev/package.json\`](https://github.com/FormidableLabs/builder-react-component/package.json)
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ### FILES

              \`\`\`
              DEVELOPMENT.md
              package.json
              dev/package.json

              config/
                babel/
                eslint/
                karma/
                webpack/

              init.js
              init/
              \`\`\`
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## DEMO
            `)}
            <CodePane
              source={strip(`
                # See all tasks
                \$ builder help

                \$ builder run check
                \$ builder run lint

                # Override \`lint-client\`
                \$ builder run lint-client
                \$ builder run lint
                \$ builder run check
              `)}
            />
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Init
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## BUILDER INIT

              A simple project generator from archetype templates.
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## DEMO

              **Note**: Work In Progress
            `)}
          </Slide>

          {/* -----------------------------------------------------------------
            * Thanks
            * ----------------------------------------------------------------- */}
          <Slide bgColor="primary">
            <Heading size={1} caps fit lineHeight={1.5} textColor="secondary">
              Made with love in Seattle by
            </Heading>
            <Link href="http://www.formidable.com">
              <Image width="100%" src={images.logo}/>
            </Link>
          </Slide>
        </Deck>
      </Spectacle>
    );
  }
}
<commit_msg>Add complexities section<commit_after>// Import React
import React from "react";

// Import Spectacle Core tags
import {
  // Appear,
  // BlockQuote,
  // Cite,
  // Code,
  CodePane,
  Deck,
  // Fill,
  Heading,
  Image,
  // Layout,
  Link,
  // ListItem,
  // List,
  Markdown,
  // Quote,
  Slide,
  Spectacle,
  Text
} from "spectacle";

// Import image preloader util
import preloader from "spectacle/lib/utils/preloader";

// Import theme
import theme from "../themes/formidable/index";

// Require CSS
require("normalize.css");
require("spectacle/lib/themes/default/index.css");
require("../themes/formidable/index.css");

const images = {
  logo: require("../assets/formidable-logo.svg")
};

preloader(images);

// ----------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------
// A naive, indent preserving strip.
//
// TODO: Extract and open source
// https://github.com/FormidableLabs/gulp-html-extract/blob/master/lib/strip-indent.js
const strip = (val) => {
  // Find first line with text. Capture that indent level.
  let indent = null;

  return val.split("\n")
    .map((line) => {
      // Capture initial indent.
      if (indent === null && /^\s/.test(line)) {
        indent = /^ */.exec(line)[0];
      }

      // If no indent, ignore.
      if (indent === null) {
        return null;
      }

      return line
        .replace(new RegExp(`^${indent}`), "")
        .replace(/\s*$/, "");
    })
    .join("\n")
    .replace(/^\s*|\s$/, "");
};

// Standard Markdown Slide
const markdown = (text, style) => {
  style = Object.assign({width: "75%", margin: "0 auto"}, style || {});
  return (

      <Markdown>
        {strip(text)}
      </Markdown>

  );
};

// ----------------------------------------------------------------------------
// The Deck
// ----------------------------------------------------------------------------
export default class Presentation extends React.Component {
  render() {
    return (
      <Spectacle theme={theme}>
        <Deck progress="bar" transition={["slide"]} transitionDuration={500}>
          {/* -----------------------------------------------------------------
            * Title
            * ----------------------------------------------------------------- */}
          <Slide bgColor="primary">
            <Heading size={1} fit caps lineHeight={1} textColor="black">
              Builder
            </Heading>
            <Heading size={1} fit caps>
              Multi-project npm workflows
            </Heading>
            <Link href="https://github.com/FormidableLabs/builder">
              <Text bold textColor="tertiary" style={{margin: "1em auto"}}>
                github.com/FormidableLabs/builder
              </Text>
            </Link>
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Introduction
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## MODERN JAVASCRIPT
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ![React So Hot](https://camo.githubusercontent.com/a85f7c2c03b36655323ec7a3250057233e82ef55/68747470733a2f2f692e696d6775722e636f6d2f695549497571622e6a7067)
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ### SO MANY TOOLS

              * **Build**: JSX, ES6, Babel, Webpack
              * **Test Tools**: Magellan, Karma, Mocha, Enzyme
              * **Quality**: Eslint, Istanbul
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ### SO MANY WORKFLOWS

              * **Watch**: dev/test JS, Karma
              * **Build**: clean, build, prod/dev
              * **Test**: client, node, coverage
              * **Publish**: git, npm
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## SO MANY PROJECTS

              * **~60** React components
              * **~10** Hapi plugins, helpers
              * **~15+** App servers
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              _welcome to_ ...

              ## [JAVASCRIPT FATIGUE](https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4#.xioz52ymh)
            `)/*eslint-enable max-len*/}
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Builder
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## BUILDER

              Your \`package.json\`, **abstracted** and **managed**
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ### USE CASES

              * **Multiple** projects
              * Nearly **identical**
              * Complex **workflows**
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### GUIDING PRINCIPLES

              * It's "**just npm**"
              * **Avoid** "buying the farm"
              * You can [**give up**](https://github.com/FormidableLabs/builder#i-give-up-how-do-i-abandon-builder)
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ## ECOSYSTEM

              * [**\`builder\`**](https://github.com/FormidableLabs/builder):
                Task runner.
              * [**\`builder-init\`**](https://github.com/FormidableLabs/builder-init):
                Project generator.
              * **\`builder-ARCHETYPE\`**: Project configs, workflows, etc.
            `, { width: "90%" })}
          </Slide>
          <Slide>
            {markdown(`
              ## HOW IT WORKS

              * \`builder\` replaces \`npm run\`
              * Archetype contains \`scripts\` + configs
              * \`builder\` magically _merges_ the archetype into your project
            `, { width: "95%" })}
          </Slide>
          <Slide>
            {markdown(`
              ## GOALS

              * Developers easily **use**
              * Architects centrally **control**
              * Projects can **flexibly** deviate
            `, { width: "80%" })}
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Builder
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## BUILDER

              A replacement for **\`npm run\`** with a _few special extras_
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ## ACTIONS

              * [\`builder run TASK\`](https://github.com/FormidableLabs/builder#builder-run)
              * [\`builder concurrent TASK1 TASK2\`](https://github.com/FormidableLabs/builder#builder-concurrent)
              * [\`builder envs TASK JSON_ARRAY\`](https://github.com/FormidableLabs/builder#builder-envs)
            `, { width: "90%" })/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ## THE "MAGIC"

              * Mutate **\`PATH\`** to include ARCHETYPE **\`.bin\`**
              * Mutate **\`NODE_PATH\`** to include ARCHETYPE **\`node_modules\`**
              * Merge archetype and project **\`package.json:scripts\`**
            `, { width: "90%" })}
          </Slide>
          <Slide>
            {markdown(`
              ## DEMO
            `)}
            <CodePane
              source={strip(`
                \$ builder run down
                \$ builder run fail
                \$ builder run --tries=3 fail

                \$ builder concurrent down down down
                \$ builder envs msg '[{"MSG": "hi"}, {"MSG": "yo"}]'
              `)}
            />
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Archetypes
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## ARCHETYPES

              The "mission control" for a set of identical projects
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## ASSESSMENT

              * Identify project "types"
              * Aim for **one** archetype per project type
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ## EXAMPLES

              * [\`builder-react-component\`](https://github.com/FormidableLabs/builder-react-component)
              * \`builder-react-app\`
              * \`builder-hapi-plugin\`
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ## CONTENTS

              * NPM tasks
              * Configurations
              * Boilerplate files
              * Documentation
              * Templates (_forthcoming_)
            `)}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### TASKS

              * [\`formidable-react-component-boilerplate/package.json\`](https://github.com/FormidableLabs/formidable-react-component-boilerplate/package.json)
              * [\`builder-react-component/package.json\`](https://github.com/FormidableLabs/builder-react-component/package.json)
              * **Note**: Special **\`npm:*\`** tasks
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### DEPENDENCIES

              * [\`formidable-react-component-boilerplate/package.json\`](https://github.com/FormidableLabs/formidable-react-component-boilerplate/package.json)
              * [\`builder-react-component/package.json\`](https://github.com/FormidableLabs/builder-react-component/package.json)
              * [\`builder-react-component/dev/package.json\`](https://github.com/FormidableLabs/builder-react-component/package.json)
            `)/*eslint-enable max-len*/}
          </Slide>
          <Slide>
            {markdown(`
              ### FILES

              \`\`\`
              DEVELOPMENT.md
              package.json
              dev/package.json

              config/
                babel/
                eslint/
                karma/
                webpack/

              init.js
              init/
              \`\`\`
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## DEMO
            `)}
            <CodePane
              source={strip(`
                # See all tasks
                \$ builder help

                \$ builder run check
                \$ builder run lint

                # Override \`lint-client\`
                \$ builder run lint-client
                \$ builder run lint
                \$ builder run check
              `)}
            />
          </Slide>
          <Slide>
            {/*eslint-disable max-len*/markdown(`
              ### COMPLEXITIES

              Easy for **users**, complex for **architects**

              * [Archetype authoring](https://github.com/FormidableLabs/builder#archetypes)
              * [Tips + Tricks](https://github.com/FormidableLabs/builder#tips-tricks--notes):
                \`process.cwd()\`, \`require.resolve()\`
            `)/*eslint-enable max-len*/}
          </Slide>

          {/* -----------------------------------------------------------------
            * Section: Init
            * ----------------------------------------------------------------- */}
          <Slide>
            {markdown(`
              ## BUILDER INIT

              A simple project generator from archetype templates.
            `)}
          </Slide>
          <Slide>
            {markdown(`
              ## DEMO

              **Note**: Work In Progress
            `)}
          </Slide>

          {/* -----------------------------------------------------------------
            * Thanks
            * ----------------------------------------------------------------- */}
          <Slide bgColor="primary">
            <Heading size={1} caps fit lineHeight={1.5} textColor="secondary">
              Made with love in Seattle by
            </Heading>
            <Link href="http://www.formidable.com">
              <Image width="100%" src={images.logo}/>
            </Link>
          </Slide>
        </Deck>
      </Spectacle>
    );
  }
}
<|endoftext|>