<commit_before>'use strict';

const Utils = require('./../utils');
const _ = require('lodash');
const HasOne = require('./has-one');
const HasMany = require('./has-many');
const BelongsToMany = require('./belongs-to-many');
const BelongsTo = require('./belongs-to');

const Mixin = {
  hasMany(target, options) { // testhint options:none
    if (!target.prototype || !(target.prototype instanceof this.sequelize.Model)) {
      throw new Error(this.name + '.hasMany called with something that\'s not a subclass of Sequelize.Model');
    }

    const source = this;

    // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
    options = options || {};
    options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
    options.useHooks = options.hooks;

    options = _.extend(options, _.omit(source.options, ['hooks']));

    // the id is in the foreign table or in a connecting table
    const association = new HasMany(source, target, options);
    source.associations[association.associationAccessor] = association;

    association.injectAttributes();
    association.mixin(source.prototype);

    return association;
  },

  belongsToMany(targetModel, options) { // testhint options:none
    if (!targetModel.prototype || !(targetModel.prototype instanceof this.sequelize.Model)) {
      throw new Error(this.name + '.belongsToMany called with something that\'s not a subclass of Sequelize.Model');
    }

    const sourceModel = this;

    // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
    options = options || {};
    options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
    options.useHooks = options.hooks;
    options.timestamps = options.timestamps === undefined ? this.sequelize.options.timestamps : options.timestamps;
    options = _.extend(options, _.omit(sourceModel.options, ['hooks', 'timestamps', 'scopes', 'defaultScope']));

    // the id is in the foreign table or in a connecting table
    const association = new BelongsToMany(sourceModel, targetModel, options);
    sourceModel.associations[association.associationAccessor] = association;

    association.injectAttributes();
    association.mixin(sourceModel.prototype);

    return association;
  },

  getAssociations(target) {
    return _.values(this.associations).filter(association => association.target.name === target.name);
  },

  getAssociationForAlias(target, alias) {
    // Two associations cannot have the same alias, so we can use find instead of filter
    return this.getAssociations(target).find(association => this.verifyAssociationAlias(association, alias)) || null;
  },

  verifyAssociationAlias(association, alias) {
    if (alias) {
      return association.as === alias;
    } else {
      return !association.isAliased;
    }
  }
};

// The logic for hasOne and belongsTo is exactly the same
function singleLinked(Type) {
  return function(target, options) { // testhint options:none
    if (!target.prototype || !(target.prototype instanceof this.sequelize.Model)) {
      throw new Error(this.name + '.' + Utils.lowercaseFirst(Type.toString()) + ' called with something that\'s not a subclass of Sequelize.Model');
    }

    const source = this;

    // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
    options = options || {};
    options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
    options.useHooks = options.hooks;

    // the id is in the foreign table
    const association = new Type(source, target, _.extend(options, source.options));
    source.associations[association.associationAccessor] = association;

    association.injectAttributes();
    association.mixin(source.prototype);

    return association;
  };
}

Mixin.hasOne = singleLinked(HasOne);

Mixin.belongsTo = singleLinked(BelongsTo);

module.exports = Mixin;
module.exports.Mixin = Mixin;
module.exports.default = Mixin;
<commit_msg>fix(associations): throw informational message when try to associate undefined (#8106)<commit_after>'use strict';

const Utils = require('./../utils');
const _ = require('lodash');
const HasOne = require('./has-one');
const HasMany = require('./has-many');
const BelongsToMany = require('./belongs-to-many');
const BelongsTo = require('./belongs-to');

const Mixin = {
  hasMany(target, options) { // testhint options:none
    if (!target || !target.prototype || !(target.prototype instanceof this.sequelize.Model)) {
      throw new Error(this.name + '.hasMany called with something that\'s not a subclass of Sequelize.Model');
    }

    const source = this;

    // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
    options = options || {};
    options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
    options.useHooks = options.hooks;

    options = _.extend(options, _.omit(source.options, ['hooks']));

    // the id is in the foreign table or in a connecting table
    const association = new HasMany(source, target, options);
    source.associations[association.associationAccessor] = association;

    association.injectAttributes();
    association.mixin(source.prototype);

    return association;
  },

  belongsToMany(targetModel, options) { // testhint options:none
    if (!targetModel || !targetModel.prototype || !(targetModel.prototype instanceof this.sequelize.Model)) {
      throw new Error(this.name + '.belongsToMany called with something that\'s not a subclass of Sequelize.Model');
    }

    const sourceModel = this;

    // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
    options = options || {};
    options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
    options.useHooks = options.hooks;
    options.timestamps = options.timestamps === undefined ? this.sequelize.options.timestamps : options.timestamps;
    options = _.extend(options, _.omit(sourceModel.options, ['hooks', 'timestamps', 'scopes', 'defaultScope']));

    // the id is in the foreign table or in a connecting table
    const association = new BelongsToMany(sourceModel, targetModel, options);
    sourceModel.associations[association.associationAccessor] = association;

    association.injectAttributes();
    association.mixin(sourceModel.prototype);

    return association;
  },

  getAssociations(target) {
    return _.values(this.associations).filter(association => association.target.name === target.name);
  },

  getAssociationForAlias(target, alias) {
    // Two associations cannot have the same alias, so we can use find instead of filter
    return this.getAssociations(target).find(association => this.verifyAssociationAlias(association, alias)) || null;
  },

  verifyAssociationAlias(association, alias) {
    if (alias) {
      return association.as === alias;
    } else {
      return !association.isAliased;
    }
  }
};

// The logic for hasOne and belongsTo is exactly the same
function singleLinked(Type) {
  return function(target, options) { // testhint options:none
    if (!target || !target.prototype || !(target.prototype instanceof this.sequelize.Model)) {
      throw new Error(this.name + '.' + Utils.lowercaseFirst(Type.toString()) + ' called with something that\'s not a subclass of Sequelize.Model');
    }

    const source = this;

    // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
    options = options || {};
    options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
    options.useHooks = options.hooks;

    // the id is in the foreign table
    const association = new Type(source, target, _.extend(options, source.options));
    source.associations[association.associationAccessor] = association;

    association.injectAttributes();
    association.mixin(source.prototype);

    return association;
  };
}

Mixin.hasOne = singleLinked(HasOne);

Mixin.belongsTo = singleLinked(BelongsTo);

module.exports = Mixin;
module.exports.Mixin = Mixin;
module.exports.default = Mixin;
<|endoftext|>


<commit_before>( function( window, atom, require, module, undefined ) {
	'use strict';

	// Imports
	var atomUtils = require( 'atom' );
	var spawn = require( 'win-spawn' );

	// Instance
	var commands = {
		subscriptions: new atomUtils.CompositeDisposable(),
		panel: null,
		config: require( './config' )
	}

	commands.activate = function( state ) {

		// Define and update a command list from the user config
		var registeredAtomCommands = [];

		atom.config.observe( 'atom-user-commands', {}, function( value ) {

			// Dispose old commands
			registeredAtomCommands.forEach( function( disposable ) {

				// Remove it from subscriptions and...
				commands.subscriptions.remove( disposable );

				// ... dispose it manually
				disposable.dispose();
			} );

			registeredAtomCommands = [];

			// Register new commands
			value.commands.forEach( function( command ) {

				// Create an atom command for each entry
				var commandName = 'atom-user-commands:' + command.name;
				var atomCommand = atom.commands.add( command.selector, commandName, function() {

					execute( command.command, command.arguments, command.options );
				} )

				// Create a menu entry for each command
				var menuEntry = atom.menu.add( [ {
					label : 'Packages',
					submenu: [ {
						label: 'Atom User Commands',
						submenu: [
							{
								label: command.name,
								command: commandName
							}
						]
					} ]
				} ] );

				// Register it in the subscriptions;
				registeredAtomCommands.push( atomCommand );
				registeredAtomCommands.push( menuEntry );

				commands.subscriptions.add( atomCommand );
				commands.subscriptions.add( menuEntry );
			} );
		} );
	}

	commands.deactivate = function() {
		commands.panel.destroy();
		commands.subscriptions.dispose();
	}

	commands.serialize = function() {
		return {};
	}

	// Create a bottom panel for the output
	function showConsole() {

		if ( !commands.panel ) {

			commands.panel = atom.workspace.addBottomPanel( {
				item: buildPanel(),
				visible: true,
				priority: 100
			} )
		};
	}

	// Destroy de bottom panel
	function hideConsole() {

		if ( commands.panel ) {
			commands.panel.destroy();
			commands.panel = null;
		}
	}

	// Execute an OS command
	function execute( command, args, options ) {

		// Open the panel if it is not
		if ( !commands.panel ) {
			showConsole();
		}

		// Cancel the close console listener
		if (commands.closeConsoleDisposable) {
			commands.closeConsoleDisposable.dispose();
			commands.subscriptions.remove(commands.closeConsoleDisposable);
		}

		var env = getEnv();

		command = replace( command || '', env );
		args = replace( args || [], env );
		options = replace( options || {}, env );

		// Announcing launch
		commands.panel.item.appendChild( window.document.createElement( 'br' ) );
		var span = window.document.createElement( 'span' );
		span.classList.add( 'echo' );
		span.textContent = "> " + command + ' ' + JSON.stringify( args ) + ' ' + JSON.stringify( options );
		commands.panel.item.appendChild( span );
		commands.panel.item.appendChild( window.document.createElement( 'br' ) );
		commands.panel.item.appendChild( window.document.createElement( 'br' ) );

		// Run the spawn, we pass args.slice() to make a shallow copy of the array because spawn will modify it.
		var proc = spawn( command, args.slice(), options );

		// Update console panel on data
		proc.stdout.on( 'data', function( data ) {

			var div = commands.panel.item;
			var line = window.document.createElement( 'div' );
			line.classList.add( 'stdout' );
			line.textContent = data.toString();
			div.appendChild( line );
			div.scrollTop = div.scrollHeight;

		} );

		// Update console panel on error data
		proc.stderr.on( 'data', function( data ) {

			var div = commands.panel.item;
			var line = window.document.createElement( 'div' );
			line.classList.add( 'stderr' );
			line.textContent = data.toString();
			div.appendChild( line );
			div.scrollTop = div.scrollHeight;
		} );

		proc.stdout.on( 'close', function( code, signal ) {
			// console.info('command closed', code, signal);
		} );

		// Register code for termination
		proc.stderr.on( 'close', function( code, signal ) {

			// Register an action for panel destruction
			commands.closeConsoleDisposable = atom.commands.add( 'atom-workspace', 'core:cancel', function() {
				hideConsole()
				commands.closeConsoleDisposable.dispose();
				commands.subscriptions.remove( commands.closeConsoleDisposable );
			} )

			commands.subscriptions.add( commands.closeConsoleDisposable );
		} );
	}

	// Set up the HTML element
	function buildPanel() {

		var mainDiv = window.document.createElement( 'div' );
		mainDiv.classList.add( 'atom-user-commands-console' );

		//var input = window.document.createElement( 'textarea' );
		//input.classList.add( 'console-input' );
		//mainDiv.appendChild(input);

		return mainDiv;

	}

	// Generate Environment variables
	function getEnv() {

		var paths = atom.project.relativizePath( atom.workspace.getActiveTextEditor().getPath() );
		var env = {
			project: paths[ 0 ],
			path: paths[ 1 ],
			absPath: atom.workspace.getActiveTextEditor().getPath()
		};

		return env;
	}

	// Replace members with env variables.
	function replace( input, vars ) {

		// Dispatch input type
		if ( !input ) {
			return;
		} else if ( typeof input == 'string' ) {
			return replaceString( input, vars );
		} else if ( Array.isArray( input ) ) {
			return replaceArray( input, vars );
		} else if ( typeof input == 'object' ) {
			return replaceObject( input, vars );
		} else {
			return input;
		}
	}

	// replace a string with vars.
	function replaceString( input, vars ) {

		var rgx = /{({?)(\w+)(}?)}/g

		var match = null;
		while ( match = rgx.exec( input ) ) {

			// {? + variable + }? so {{arg}} -> {arg}
			var key = match[ 1 ] + match[ 2 ] + match[ 3 ];
			var replacement = vars[ key ] || key;

			// Replace the result only on the remaining string
			input = input.substring( 0, match.index ) + input.substring( match.index ).replace( match[ 0 ], replacement );
			rgx.lastIndex = match.index + replacement.length;

			//console.info('replace', match[0], 'for', replacement, 'at', match.index, 'in', match.input, 'next on', rgx.lastIndex );
		}

		return input;
	}

	// Replace array string elements with variables
	function replaceArray( input, vars ) {

		for ( var i = 0; i < input.length; i++ ) {
			input[ i ] = replace( input[ i ], vars );
		}

		return input;

	}

	// Replaces oboject string members with variables
	function replaceObject( input, vars ) {

		var keys = Object.keys( input );
		keys.forEach( function( key ) {
			input[ key ] = replace( input[ key ], vars );
		} );

		return input;
	}

	// TODO: Register active processes for killing;

	// Publishing a reference
	module.exports = commands;

} )( window, atom, require, module );
<commit_msg>fixed: replace will destroy original command/args<commit_after>( function( window, atom, require, module, undefined ) {
	'use strict';

	// Imports
	var atomUtils = require( 'atom' );
	var spawn = require( 'win-spawn' );

	// Instance
	var commands = {
		subscriptions: new atomUtils.CompositeDisposable(),
		panel: null,
		config: require( './config' )
	}

	commands.activate = function( state ) {

		// Define and update a command list from the user config
		var registeredAtomCommands = [];

		atom.config.observe( 'atom-user-commands', {}, function( value ) {

			// Dispose old commands
			registeredAtomCommands.forEach( function( disposable ) {

				// Remove it from subscriptions and...
				commands.subscriptions.remove( disposable );

				// ... dispose it manually
				disposable.dispose();
			} );

			registeredAtomCommands = [];

			// Register new commands
			value.commands.forEach( function( command ) {

				// Create an atom command for each entry
				var commandName = 'atom-user-commands:' + command.name;
				var atomCommand = atom.commands.add( command.selector, commandName, function() {

					execute( command.command, command.arguments, command.options );
				} )

				// Create a menu entry for each command
				var menuEntry = atom.menu.add( [ {
					label : 'Packages',
					submenu: [ {
						label: 'Atom User Commands',
						submenu: [
							{
								label: command.name,
								command: commandName
							}
						]
					} ]
				} ] );

				// Register it in the subscriptions;
				registeredAtomCommands.push( atomCommand );
				registeredAtomCommands.push( menuEntry );

				commands.subscriptions.add( atomCommand );
				commands.subscriptions.add( menuEntry );
			} );
		} );
	}

	commands.deactivate = function() {
		commands.panel.destroy();
		commands.subscriptions.dispose();
	}

	commands.serialize = function() {
		return {};
	}

	// Create a bottom panel for the output
	function showConsole() {

		if ( !commands.panel ) {

			commands.panel = atom.workspace.addBottomPanel( {
				item: buildPanel(),
				visible: true,
				priority: 100
			} )
		};
	}

	// Destroy de bottom panel
	function hideConsole() {

		if ( commands.panel ) {
			commands.panel.destroy();
			commands.panel = null;
		}
	}

	// Clear de bottom panel
	function clearConsole() {
		if (commands.panel) {
			while (commands.panel.item.lastChild) {
				var child = commands.panel.item.lastChild;
				commands.panel.item.removeChild(child);
			}
		}
	}

	// Output to de bottom panel
	function writeConsole( text, style ) {
		var div = commands.panel.item;
		var line = window.document.createElement( 'div' );
		line.classList.add( style );
		line.textContent = text;
		div.appendChild( line );
		div.scrollTop = div.scrollHeight;
	}

	// Execute an OS command
	function execute( command, args, options ) {

		clearConsole();

		// Open the panel if it is not
		if ( !commands.panel ) {
			showConsole();
		}

		// Cancel the close console listener
		if (commands.closeConsoleDisposable) {
			commands.closeConsoleDisposable.dispose();
			commands.subscriptions.remove(commands.closeConsoleDisposable);
		}

		//writeConsole('? ' + JSON.stringify(args), 'echo');

		var env = getEnv();
		var command = replace( command || '', env );
		var args = replace( args || [], env );
		var options = replace( options || {}, env );
		//writeConsole('? ' + JSON.stringify(args), 'echo');
		//writeConsole("> " + JSON.stringify( env), "echo");

		// Announcing launch
		commands.panel.item.appendChild( window.document.createElement( 'br' ) );
		var span = window.document.createElement( 'span' );
		span.classList.add( 'echo' );
		span.textContent = "> " + command + ' ' + JSON.stringify( args ) + ' ' + JSON.stringify( options );
		commands.panel.item.appendChild( span );
		commands.panel.item.appendChild( window.document.createElement( 'br' ) );
		//commands.panel.item.appendChild( window.document.createElement( 'br' ) );

		// Run the spawn, we pass args.slice() to make a shallow copy of the array because spawn will modify it.
		var proc = spawn( command, args.slice(), options );

		// Update console panel on data
		proc.stdout.on( 'data', function( data ) {
			writeConsole(data.toString(), 'stdout');
		} );

		// Update console panel on error data
		proc.stderr.on( 'data', function( data ) {
			writeConsole(data.toString(), 'stderr');
		} );

		proc.stdout.on( 'close', function( code, signal ) {
			// console.info('command closed', code, signal);
			writeConsole('[Finished]', 'stdout');
		} );

		// Register code for termination
		proc.stderr.on( 'close', function( code, signal ) {

			// Register an action for panel destruction
			commands.closeConsoleDisposable = atom.commands.add( 'atom-workspace', 'core:cancel', function() {
				hideConsole()
				commands.closeConsoleDisposable.dispose();
				commands.subscriptions.remove( commands.closeConsoleDisposable );
			} )

			commands.subscriptions.add( commands.closeConsoleDisposable );
		} );
	}

	// Set up the HTML element
	function buildPanel() {

		var mainDiv = window.document.createElement( 'div' );
		if (true) {
			mainDiv.classList.add( 'atom-user-commands-console' );
		}	else {
			var input = window.document.createElement( 'textarea' );
		  input.classList.add( 'console-input' );
			mainDiv.appendChild(input);
		}
		return mainDiv;

	}

	// Generate Environment variables
	function getEnv() {

		var paths = atom.project.relativizePath( atom.workspace.getActiveTextEditor().getPath() );
		var env = {
			project: paths[ 0 ],
			path: paths[ 1 ],
			absPath: atom.workspace.getActiveTextEditor().getPath()
		};

		return env;
	}

	// Replace members with env variables.
	function replace( input, vars ) {

		// Dispatch input type
		if ( !input ) {
			return;
		} else if ( typeof input == 'string' ) {
			return replaceString( input, vars );
		} else if ( Array.isArray( input ) ) {
			return replaceArray( input, vars );
		} else if ( typeof input == 'object' ) {
			return replaceObject( input, vars );
		} else {
			return input;
		}
	}

	// replace a string with vars.
	function replaceString( input, vars ) {

		var rgx = /{({?)(\w+)(}?)}/g

		var match = null;
		while ( match = rgx.exec( input ) ) {

			// {? + variable + }? so {{arg}} -> {arg}
			var key = match[ 1 ] + match[ 2 ] + match[ 3 ];
			var replacement = vars[ key ] || key;

			// Replace the result only on the remaining string
			input = input.substring( 0, match.index ) + input.substring( match.index ).replace( match[ 0 ], replacement );
			rgx.lastIndex = match.index + replacement.length;

			//console.info('replace', match[0], 'for', replacement, 'at', match.index, 'in', match.input, 'next on', rgx.lastIndex );
		}

		return input;
	}

	// Replace array string elements with variables
	function replaceArray( input, vars ) {
		var output = new Array(input.length);
		for ( var i = 0; i < input.length; i++ ) {
			output[ i ] = replace( input[ i ], vars );
		}
		return output;
	}

	// Replaces oboject string members with variables
	function replaceObject( input, vars ) {
		var output = {};
		var keys = Object.keys( input );
		keys.forEach( function( key ) {
			output[ key ] = replace( input[ key ], vars );
		} );
		return output;
	}

	// TODO: Register active processes for killing;

	// Publishing a reference
	module.exports = commands;

} )( window, atom, require, module );
<|endoftext|>


<commit_before>/* global require, module */
'use strict';

var fs = require('fs');
var p  = require('../preprocessors');

var Project      = require('../models/project');
var cleanBaseURL = require('../utilities/clean-base-url');

var preprocessJs  = p.preprocessJs;
var preprocessCss = p.preprocessCss;
var isType        = p.isType;

var preprocessTemplates = p.preprocessTemplates;

var preprocessMinifyCss = p.preprocessMinifyCss;

var replace     = require('broccoli-replace');
var compileES6  = require('broccoli-es6-concatenator');
//var validateES6 = require('broccoli-es6-import-validate');
var pickFiles   = require('broccoli-static-compiler');
var jshintTrees = require('broccoli-jshint');
var concatFiles = require('broccoli-concat');
var moveFile    = require('broccoli-file-mover');
var assetRev    = require('broccoli-asset-rev');

var upstreamMergeTrees  = require('broccoli-merge-trees');

var unwatchedTree    = require('broccoli-unwatched-tree');
var uglifyJavaScript = require('broccoli-uglify-js');

var memoize    = require('lodash-node/modern/functions').memoize;
var assign     = require('lodash-node/modern/objects/assign');
var defaults   = require('lodash-node/modern/objects/defaults');
var path       = require('path');
var ES3SafeFilter = require('broccoli-es3-safe-recast');

function mergeTrees(inputTree, options) {
  var tree = upstreamMergeTrees(inputTree, options);

  tree.description = options && options.description;

  return tree;
}

module.exports = EmberApp;

function EmberApp(options) {
  options = options || {};

  this.project = options.project || Project.closestSync(process.cwd());

  this.env  = process.env.EMBER_ENV || 'development';
  this.name = options.name || this.project.name();

  this.registry = options.registry || p.setupRegistry(this);

  var isProduction = this.env === 'production';

  this.tests   = options.hasOwnProperty('tests')   ? options.tests   : !isProduction;
  this.hinting = options.hasOwnProperty('hinting') ? options.hinting : !isProduction;

  this.options = defaults(options, {
    es3Safe: true,
    wrapInEval: !isProduction,
    minifyCSS: {
      enabled: true,
      options: {}
    },
    minifyJS: {
      enabled: true,
      options: {
        mangle: true,
        compress: true
      }
    },
    fingerprint: {
      enabled: isProduction,
      exclude: [],
      extensions: ['js', 'css', 'png', 'jpg', 'gif'],
      prepend: '',
      replaceExtensions: ['html', 'css', 'js']
    },
    loader: 'vendor/loader/loader.js',
    trees: {},
    getEnvJSON: this.project.require('./config/environment')
  });

  this.options.trees = defaults(this.options.trees, {
    app:    'app',
    styles: 'app/styles',
    tests:  'tests',
    vendor: unwatchedTree('vendor'),
  });

  this.importWhitelist     = {};
  this.legacyFilesToAppend = [];
  this.vendorStaticStyles  = [];
  this.otherAssetTrees     = [];
  this._importTrees        = [];

  this.trees = this.options.trees;

  this.populateLegacyFiles();
  this._notifyAddonIncluded();
}

EmberApp.prototype._notifyAddonIncluded = function() {
  this.project.initializeAddons();
  this.project.addons.forEach(function(addon) {
    if (typeof addon.included !== 'function') {
      throw new Error('The `' + addon.constructor.name + '` addon must implement the `included` hook.');
    }
    addon.included(this);
  }, this);
};

EmberApp.prototype.addonTreesFor = function(type) {
  var addonTrees = this.project.addons.map(function(addon) {
    if (typeof addon.treeFor !== 'function') {
      throw new Error('The `' + addon.constructor.name + '` addon must implement the `treeFor` hook.');
    }

    return addon.treeFor(type);
  }, this);

  return addonTrees.filter(function(tree) {
    return !!tree;
  });
};

EmberApp.prototype.populateLegacyFiles = function () {
  this.import('vendor/loader/loader.js');

  this.import('vendor/jquery/dist/jquery.js');

  this.import({
    development: 'vendor/handlebars/handlebars.js',
    production:  'vendor/handlebars/handlebars.runtime.js'
  });

  this.import({
    development: 'vendor/ember/ember.js',
    production:  'vendor/ember/ember.prod.js'
  });

  this.import('vendor/ember-cli-shims/app-shims.js', {
    availableModules: {
      ember: ['default']
    }
  });

  this.import('vendor/ember-resolver/dist/modules/ember-resolver.js', {
    availableModules: {
      'ember/resolver': ['default']
    }
  });

  this.import('vendor/ember-load-initializers/ember-load-initializers.js', {
    availableModules: {
      'ember/load-initializers': ['default']
    }
  });
};

EmberApp.prototype.index = memoize(function() {
  var files = [
    'index.html'
  ];

  var index = pickFiles(this.trees.app, {
    srcDir: '/',
    files: files,
    destDir: '/'
  });

  return injectENVJson(this.options.getEnvJSON, this.env, index, files);
});

EmberApp.prototype.testIndex = memoize(function() {
  var index = pickFiles(this.trees.tests, {
    srcDir: '/',
    files: ['index.html'],
    destDir: '/tests'
  });

  return injectENVJson(this.options.getEnvJSON, this.env, index, [
    'tests/index.html'
  ]);
});

EmberApp.prototype.publicFolder = function() {
  return 'public';
};

EmberApp.prototype._processedAppTree = memoize(function() {
  var addonTrees = this.addonTreesFor('app');
  var mergedApp  = mergeTrees(addonTrees.concat(this.trees.app), {
    overwrite: true,
    description: 'TreeMerger (app)'
  });

  return pickFiles(mergedApp, {
    srcDir: '/',
    destDir: this.name
  });
});

EmberApp.prototype._processedTestsTree = memoize(function() {
  return pickFiles(this.trees.tests, {
    srcDir: '/',
    destDir: this.name + '/tests'
  });
});

EmberApp.prototype._processedVendorTree = memoize(function() {
  var addonTrees   = this.addonTreesFor('vendor');
  var mergedVendor = mergeTrees(this._importTrees.concat(addonTrees, this.trees.vendor), {
    overwrite: true,
    description: 'TreeMerger (vendor)'
  });

  return pickFiles(mergedVendor, {
    srcDir: '/',
    destDir: 'vendor/'
  });
});

EmberApp.prototype.appAndDependencies = memoize(function() {
  var app    = this._processedAppTree();
  if (this.options.es3Safe) {
    app = new ES3SafeFilter(app);
  }

  var vendor = this._processedVendorTree();

  var appAndTemplates = preprocessTemplates(app);

  var sourceTrees = [ vendor ];

  if (this.tests) {
    var tests  = this._processedTestsTree();
    var preprocessedTests = preprocessJs(tests, '/tests', this.name);

    sourceTrees.push(preprocessedTests);

    if (this.hinting) {
      var jshintedApp = jshintTrees(app, {
        jshintrcPath: this.project.root,
        description: 'JSHint - App'
      });
      var jshintedTests = jshintTrees(tests, {
        jshintrcPath: path.join(this.project.root,  'tests'),
        description: 'JSHint - Tests'
      });

      jshintedApp = pickFiles(jshintedApp, {
        srcDir: '/',
        destDir: this.name + '/tests/'
      });

      jshintedTests = pickFiles(jshintedTests, {
        srcDir: '/',
        destDir: this.name + '/tests/'
      });

      sourceTrees.push(jshintedApp);
      sourceTrees.push(jshintedTests);
    }
  }

  // js hint should be applied before preprocessing, validateES6 after
  var preprocessedApp = preprocessJs(appAndTemplates, '/', this.name);
  sourceTrees.unshift(preprocessedApp);

  if (this.hinting) {
    //var applicationJs = validateES6(preprocessedApp, {
    //  whitelist: this.importWhitelist
    //});
    sourceTrees.push(preprocessedApp);
  }

  var emberCLITree = pickFiles(unwatchedTree(__dirname), {
    files: ['loader.js'],
    srcDir: '/',
    destDir: '/assets/ember-cli/'
  });

  sourceTrees.push(emberCLITree);

  return mergeTrees(sourceTrees, {
    overwrite: true,
    description: 'TreeMerger (appAndDependencies)'
  });
});

EmberApp.prototype.javascript = memoize(function() {
  var applicationJs       = this.appAndDependencies();
  var legacyFilesToAppend = this.legacyFilesToAppend;

  if (this.env !== 'production') {
    this.import('vendor/ember-qunit/dist/named-amd/main.js', {
      availableModules: {
        'ember-qunit': [
          'globalize',
          'moduleFor',
          'moduleForComponent',
          'moduleForModel',
          'test',
          'setResolver'
        ]
      }
    });

    this.import('vendor/ember-cli-shims/test-shims.js', {
      availableModules: {
        'qunit': ['default']
      }
    });
  }

  var es6 = compileES6(applicationJs, {
    loaderFile: 'assets/ember-cli/loader.js',
    ignoredModules: Object.keys(this.importWhitelist),
    inputFiles: [
      this.name + '/**/*.js'
    ],
    wrapInEval: this.options.wrapInEval,
    outputFile: '/assets/' + this.name + '.js',
  });

  var vendor = concatFiles(applicationJs, {
    inputFiles: legacyFilesToAppend,
    outputFile: '/assets/vendor.js',
    separator: '\n;'
  });

  var vendorAndApp = mergeTrees([vendor, es6]);

  if (this.env === 'production' && this.options.minifyJS.enabled === true) {
    var options = this.options.minifyJS.options || {};
    return uglifyJavaScript(vendorAndApp, options);
  } else {
    return vendorAndApp;
  }
});

EmberApp.prototype.styles = memoize(function() {
  var addonTrees = this.addonTreesFor('styles');
  var vendor = this._processedVendorTree();
  var styles = pickFiles(this.trees.styles, {
    srcDir: '/',
    destDir: '/app/styles'
  });

  var trees = [vendor];
  trees.concat(addonTrees);
  trees.push(styles);

  var stylesAndVendor = mergeTrees(trees, {
    description: 'TreeMerger (stylesAndVendor)'
  });

  var processedStyles = preprocessCss(stylesAndVendor, '/app/styles', '/assets');
  var vendorStyles    = concatFiles(stylesAndVendor, {
    inputFiles: this.vendorStaticStyles,
    outputFile: '/assets/vendor.css',
    description: 'concatFiles - vendorStyles'
  });

  if (this.env === 'production' && this.options.minifyCSS.enabled === true) {
    var options = this.options.minifyCSS.options || {};
    processedStyles = preprocessMinifyCss(processedStyles, options);
    vendorStyles    = preprocessMinifyCss(vendorStyles, options);
  }

  return mergeTrees([
      processedStyles,
      vendorStyles
    ], {
      description: 'styles'
    });
});

EmberApp.prototype.testFiles = memoize(function() {
  var qunitFiles = pickFiles(this.trees.vendor, {
      srcDir: '/qunit/qunit',
      files: [
        'qunit.css', 'qunit.js'
      ],
      destDir: '/assets/'
    });

  var notificationsFile = moveFile(this.trees.vendor, {
    srcFile: '/qunit-notifications/index.js',
    destFile: '/assets/qunit-notifications.js',
    duplicate: false,
    description: 'moveFile - notificationsFile'
  });

  qunitFiles = mergeTrees([
    qunitFiles,
    notificationsFile
  ], {
    overwrite: true,
    description: 'TreeMerger (qunitFiles)'
  });

  var testemPath = path.join(__dirname, 'testem');
  testemPath = path.dirname(testemPath);

  var testemTree = pickFiles(unwatchedTree(testemPath), {
      files: ['testem.js'],
      srcDir: '/',
      destDir: '/'
    });

  var iconsPath = 'vendor/ember-qunit-notifications';

  var iconsTree = pickFiles(iconsPath, {
    files: ['passed.png', 'failed.png'],
    srcDir: '/',
    destDir: '/assets/'
  });

  var testLoader = pickFiles('vendor/ember-cli-test-loader', {
    files: ['test-loader.js'],
    srcDir: '/',
    destDir: '/assets/'
  });

  var sourceTrees = [
    qunitFiles,
    testemTree,
    iconsTree,
    testLoader
  ];

  return mergeTrees(sourceTrees, {
      overwrite: true,
      description: 'TreeMerger (testFiles)'
    });
});

EmberApp.prototype.otherAssets = memoize(function() {
  return mergeTrees(this.otherAssetTrees, {
    description: 'TreeMerger (otherAssetTrees)'
  });
});

EmberApp.prototype.import = function(asset, options) {
  var assetPath;
  options = options || {};

  if (typeof asset === 'object') {
    assetPath = asset[this.env];
  } else {
    assetPath = asset;
  }

  if (assetPath === null) {
    return;
  }

  if (/[\*\,]/.test(assetPath)) {
    throw new Error('You must pass a file path (without glob pattern) to `app.import`.  path was: `' + assetPath + '`');
  }

  var directory = path.dirname(assetPath);
  var subdirectory = directory.replace(/^vendor\//, '');
  var extension = path.extname(assetPath);
  var basename  = path.basename(assetPath);

  if (!extension) {
    throw new Error('You must pass a file to `app.import`. For directories specify them to the constructor under the `trees` option.');
  }

  if (fs.existsSync(directory)) {
    var assetTree = pickFiles(directory, {
      srcDir: '/',
      destDir: subdirectory
    });

    this._importTrees.push(assetTree);
  }

  if (isType(assetPath, 'js')) {
    this.legacyFilesToAppend.push(assetPath);

    this.importWhitelist = assign(this.importWhitelist, options.availableModules || {});
  } else if (extension === '.css') {
    this.vendorStaticStyles.push(assetPath);
  } else {
    var otherAssetTree = pickFiles(this.trees.vendor, {
      srcDir: subdirectory,
      files: [basename],
      destDir: subdirectory
    });

    this.otherAssetTrees.push(otherAssetTree);
  }
};

EmberApp.prototype.fingerprint = function(tree) {
  return assetRev(tree, {
    fingerprintExtensions: this.options.fingerprint.extensions,
    fingerprintExclude:    this.options.fingerprint.exclude,
    replaceExtensions:     this.options.fingerprint.replaceExtensions,
    prependPath:           this.options.fingerprint.prepend,
    customHash:            this.options.fingerprint.customHash
  });
};

EmberApp.prototype.toArray = function() {
  var sourceTrees = [
    this.index(),
    this.javascript(),
    this.publicFolder(),
    this.styles(),
    this.otherAssets()
  ];

  if (this.tests) {
    sourceTrees = sourceTrees.concat(this.testIndex(), this.testFiles());
  }

  return sourceTrees;
};

EmberApp.prototype.toTree = memoize(function() {
  var tree = mergeTrees(this.toArray(), {
    overwrite: true,
    description: 'TreeMerger (allTrees)'
  });

  if (this.options.fingerprint.enabled === true) {
    tree = this.fingerprint(tree);
  }

  return tree;
});

function injectENVJson(fn, env, tree, files) {
  // TODO: real templating
  var envJsonString = function(){
    return JSON.stringify(fn(env));
  };

  var baseTag = function(){
    var envJSON      = fn(env);
    var baseURL      = cleanBaseURL(envJSON.baseURL);
    var locationType = envJSON.locationType;

    if (locationType === 'hash' || locationType === 'none') {
      return '';
    }

    if (baseURL) {
      return '<base href="' + baseURL + '" />';
    } else {
      return '';
    }
  };

  return replace(tree, {
    files: files,
    patterns: [{
      match: /\{\{ENV\}\}/g,
      replacement: envJsonString
    },
    {
      match: /\{\{BASE_TAG\}\}/g,
      replacement: baseTag
    }]
  });
}
<commit_msg>Use exports instead of availableModules.<commit_after>/* global require, module */
'use strict';

var fs = require('fs');
var p  = require('../preprocessors');

var Project      = require('../models/project');
var cleanBaseURL = require('../utilities/clean-base-url');

var preprocessJs  = p.preprocessJs;
var preprocessCss = p.preprocessCss;
var isType        = p.isType;

var preprocessTemplates = p.preprocessTemplates;

var preprocessMinifyCss = p.preprocessMinifyCss;

var replace     = require('broccoli-replace');
var compileES6  = require('broccoli-es6-concatenator');
//var validateES6 = require('broccoli-es6-import-validate');
var pickFiles   = require('broccoli-static-compiler');
var jshintTrees = require('broccoli-jshint');
var concatFiles = require('broccoli-concat');
var moveFile    = require('broccoli-file-mover');
var assetRev    = require('broccoli-asset-rev');

var upstreamMergeTrees  = require('broccoli-merge-trees');

var unwatchedTree    = require('broccoli-unwatched-tree');
var uglifyJavaScript = require('broccoli-uglify-js');

var memoize    = require('lodash-node/modern/functions').memoize;
var assign     = require('lodash-node/modern/objects/assign');
var defaults   = require('lodash-node/modern/objects/defaults');
var path       = require('path');
var ES3SafeFilter = require('broccoli-es3-safe-recast');

function mergeTrees(inputTree, options) {
  var tree = upstreamMergeTrees(inputTree, options);

  tree.description = options && options.description;

  return tree;
}

module.exports = EmberApp;

function EmberApp(options) {
  options = options || {};

  this.project = options.project || Project.closestSync(process.cwd());

  this.env  = process.env.EMBER_ENV || 'development';
  this.name = options.name || this.project.name();

  this.registry = options.registry || p.setupRegistry(this);

  var isProduction = this.env === 'production';

  this.tests   = options.hasOwnProperty('tests')   ? options.tests   : !isProduction;
  this.hinting = options.hasOwnProperty('hinting') ? options.hinting : !isProduction;

  this.options = defaults(options, {
    es3Safe: true,
    wrapInEval: !isProduction,
    minifyCSS: {
      enabled: true,
      options: {}
    },
    minifyJS: {
      enabled: true,
      options: {
        mangle: true,
        compress: true
      }
    },
    fingerprint: {
      enabled: isProduction,
      exclude: [],
      extensions: ['js', 'css', 'png', 'jpg', 'gif'],
      prepend: '',
      replaceExtensions: ['html', 'css', 'js']
    },
    loader: 'vendor/loader/loader.js',
    trees: {},
    getEnvJSON: this.project.require('./config/environment')
  });

  this.options.trees = defaults(this.options.trees, {
    app:    'app',
    styles: 'app/styles',
    tests:  'tests',
    vendor: unwatchedTree('vendor'),
  });

  this.importWhitelist     = {};
  this.legacyFilesToAppend = [];
  this.vendorStaticStyles  = [];
  this.otherAssetTrees     = [];
  this._importTrees        = [];

  this.trees = this.options.trees;

  this.populateLegacyFiles();
  this._notifyAddonIncluded();
}

EmberApp.prototype._notifyAddonIncluded = function() {
  this.project.initializeAddons();
  this.project.addons.forEach(function(addon) {
    if (typeof addon.included !== 'function') {
      throw new Error('The `' + addon.constructor.name + '` addon must implement the `included` hook.');
    }
    addon.included(this);
  }, this);
};

EmberApp.prototype.addonTreesFor = function(type) {
  var addonTrees = this.project.addons.map(function(addon) {
    if (typeof addon.treeFor !== 'function') {
      throw new Error('The `' + addon.constructor.name + '` addon must implement the `treeFor` hook.');
    }

    return addon.treeFor(type);
  }, this);

  return addonTrees.filter(function(tree) {
    return !!tree;
  });
};

EmberApp.prototype.populateLegacyFiles = function () {
  this.import('vendor/loader/loader.js');

  this.import('vendor/jquery/dist/jquery.js');

  this.import({
    development: 'vendor/handlebars/handlebars.js',
    production:  'vendor/handlebars/handlebars.runtime.js'
  });

  this.import({
    development: 'vendor/ember/ember.js',
    production:  'vendor/ember/ember.prod.js'
  });

  this.import('vendor/ember-cli-shims/app-shims.js', {
    exports: {
      ember: ['default']
    }
  });

  this.import('vendor/ember-resolver/dist/modules/ember-resolver.js', {
    exports: {
      'ember/resolver': ['default']
    }
  });

  this.import('vendor/ember-load-initializers/ember-load-initializers.js', {
    exports: {
      'ember/load-initializers': ['default']
    }
  });
};

EmberApp.prototype.index = memoize(function() {
  var files = [
    'index.html'
  ];

  var index = pickFiles(this.trees.app, {
    srcDir: '/',
    files: files,
    destDir: '/'
  });

  return injectENVJson(this.options.getEnvJSON, this.env, index, files);
});

EmberApp.prototype.testIndex = memoize(function() {
  var index = pickFiles(this.trees.tests, {
    srcDir: '/',
    files: ['index.html'],
    destDir: '/tests'
  });

  return injectENVJson(this.options.getEnvJSON, this.env, index, [
    'tests/index.html'
  ]);
});

EmberApp.prototype.publicFolder = function() {
  return 'public';
};

EmberApp.prototype._processedAppTree = memoize(function() {
  var addonTrees = this.addonTreesFor('app');
  var mergedApp  = mergeTrees(addonTrees.concat(this.trees.app), {
    overwrite: true,
    description: 'TreeMerger (app)'
  });

  return pickFiles(mergedApp, {
    srcDir: '/',
    destDir: this.name
  });
});

EmberApp.prototype._processedTestsTree = memoize(function() {
  return pickFiles(this.trees.tests, {
    srcDir: '/',
    destDir: this.name + '/tests'
  });
});

EmberApp.prototype._processedVendorTree = memoize(function() {
  var addonTrees   = this.addonTreesFor('vendor');
  var mergedVendor = mergeTrees(this._importTrees.concat(addonTrees, this.trees.vendor), {
    overwrite: true,
    description: 'TreeMerger (vendor)'
  });

  return pickFiles(mergedVendor, {
    srcDir: '/',
    destDir: 'vendor/'
  });
});

EmberApp.prototype.appAndDependencies = memoize(function() {
  var app    = this._processedAppTree();
  if (this.options.es3Safe) {
    app = new ES3SafeFilter(app);
  }

  var vendor = this._processedVendorTree();

  var appAndTemplates = preprocessTemplates(app);

  var sourceTrees = [ vendor ];

  if (this.tests) {
    var tests  = this._processedTestsTree();
    var preprocessedTests = preprocessJs(tests, '/tests', this.name);

    sourceTrees.push(preprocessedTests);

    if (this.hinting) {
      var jshintedApp = jshintTrees(app, {
        jshintrcPath: this.project.root,
        description: 'JSHint - App'
      });
      var jshintedTests = jshintTrees(tests, {
        jshintrcPath: path.join(this.project.root,  'tests'),
        description: 'JSHint - Tests'
      });

      jshintedApp = pickFiles(jshintedApp, {
        srcDir: '/',
        destDir: this.name + '/tests/'
      });

      jshintedTests = pickFiles(jshintedTests, {
        srcDir: '/',
        destDir: this.name + '/tests/'
      });

      sourceTrees.push(jshintedApp);
      sourceTrees.push(jshintedTests);
    }
  }

  // js hint should be applied before preprocessing, validateES6 after
  var preprocessedApp = preprocessJs(appAndTemplates, '/', this.name);
  sourceTrees.unshift(preprocessedApp);

  if (this.hinting) {
    //var applicationJs = validateES6(preprocessedApp, {
    //  whitelist: this.importWhitelist
    //});
    sourceTrees.push(preprocessedApp);
  }

  var emberCLITree = pickFiles(unwatchedTree(__dirname), {
    files: ['loader.js'],
    srcDir: '/',
    destDir: '/assets/ember-cli/'
  });

  sourceTrees.push(emberCLITree);

  return mergeTrees(sourceTrees, {
    overwrite: true,
    description: 'TreeMerger (appAndDependencies)'
  });
});

EmberApp.prototype.javascript = memoize(function() {
  var applicationJs       = this.appAndDependencies();
  var legacyFilesToAppend = this.legacyFilesToAppend;

  if (this.env !== 'production') {
    this.import('vendor/ember-qunit/dist/named-amd/main.js', {
      exports: {
        'ember-qunit': [
          'globalize',
          'moduleFor',
          'moduleForComponent',
          'moduleForModel',
          'test',
          'setResolver'
        ]
      }
    });

    this.import('vendor/ember-cli-shims/test-shims.js', {
      exports: {
        'qunit': ['default']
      }
    });
  }

  var es6 = compileES6(applicationJs, {
    loaderFile: 'assets/ember-cli/loader.js',
    ignoredModules: Object.keys(this.importWhitelist),
    inputFiles: [
      this.name + '/**/*.js'
    ],
    wrapInEval: this.options.wrapInEval,
    outputFile: '/assets/' + this.name + '.js',
  });

  var vendor = concatFiles(applicationJs, {
    inputFiles: legacyFilesToAppend,
    outputFile: '/assets/vendor.js',
    separator: '\n;'
  });

  var vendorAndApp = mergeTrees([vendor, es6]);

  if (this.env === 'production' && this.options.minifyJS.enabled === true) {
    var options = this.options.minifyJS.options || {};
    return uglifyJavaScript(vendorAndApp, options);
  } else {
    return vendorAndApp;
  }
});

EmberApp.prototype.styles = memoize(function() {
  var addonTrees = this.addonTreesFor('styles');
  var vendor = this._processedVendorTree();
  var styles = pickFiles(this.trees.styles, {
    srcDir: '/',
    destDir: '/app/styles'
  });

  var trees = [vendor];
  trees.concat(addonTrees);
  trees.push(styles);

  var stylesAndVendor = mergeTrees(trees, {
    description: 'TreeMerger (stylesAndVendor)'
  });

  var processedStyles = preprocessCss(stylesAndVendor, '/app/styles', '/assets');
  var vendorStyles    = concatFiles(stylesAndVendor, {
    inputFiles: this.vendorStaticStyles,
    outputFile: '/assets/vendor.css',
    description: 'concatFiles - vendorStyles'
  });

  if (this.env === 'production' && this.options.minifyCSS.enabled === true) {
    var options = this.options.minifyCSS.options || {};
    processedStyles = preprocessMinifyCss(processedStyles, options);
    vendorStyles    = preprocessMinifyCss(vendorStyles, options);
  }

  return mergeTrees([
      processedStyles,
      vendorStyles
    ], {
      description: 'styles'
    });
});

EmberApp.prototype.testFiles = memoize(function() {
  var qunitFiles = pickFiles(this.trees.vendor, {
      srcDir: '/qunit/qunit',
      files: [
        'qunit.css', 'qunit.js'
      ],
      destDir: '/assets/'
    });

  var notificationsFile = moveFile(this.trees.vendor, {
    srcFile: '/qunit-notifications/index.js',
    destFile: '/assets/qunit-notifications.js',
    duplicate: false,
    description: 'moveFile - notificationsFile'
  });

  qunitFiles = mergeTrees([
    qunitFiles,
    notificationsFile
  ], {
    overwrite: true,
    description: 'TreeMerger (qunitFiles)'
  });

  var testemPath = path.join(__dirname, 'testem');
  testemPath = path.dirname(testemPath);

  var testemTree = pickFiles(unwatchedTree(testemPath), {
      files: ['testem.js'],
      srcDir: '/',
      destDir: '/'
    });

  var iconsPath = 'vendor/ember-qunit-notifications';

  var iconsTree = pickFiles(iconsPath, {
    files: ['passed.png', 'failed.png'],
    srcDir: '/',
    destDir: '/assets/'
  });

  var testLoader = pickFiles('vendor/ember-cli-test-loader', {
    files: ['test-loader.js'],
    srcDir: '/',
    destDir: '/assets/'
  });

  var sourceTrees = [
    qunitFiles,
    testemTree,
    iconsTree,
    testLoader
  ];

  return mergeTrees(sourceTrees, {
      overwrite: true,
      description: 'TreeMerger (testFiles)'
    });
});

EmberApp.prototype.otherAssets = memoize(function() {
  return mergeTrees(this.otherAssetTrees, {
    description: 'TreeMerger (otherAssetTrees)'
  });
});

EmberApp.prototype.import = function(asset, options) {
  var assetPath;
  options = options || {};

  if (typeof asset === 'object') {
    assetPath = asset[this.env];
  } else {
    assetPath = asset;
  }

  if (assetPath === null) {
    return;
  }

  if (/[\*\,]/.test(assetPath)) {
    throw new Error('You must pass a file path (without glob pattern) to `app.import`.  path was: `' + assetPath + '`');
  }

  var directory = path.dirname(assetPath);
  var subdirectory = directory.replace(/^vendor\//, '');
  var extension = path.extname(assetPath);
  var basename  = path.basename(assetPath);

  if (!extension) {
    throw new Error('You must pass a file to `app.import`. For directories specify them to the constructor under the `trees` option.');
  }

  if (fs.existsSync(directory)) {
    var assetTree = pickFiles(directory, {
      srcDir: '/',
      destDir: subdirectory
    });

    this._importTrees.push(assetTree);
  }

  if (isType(assetPath, 'js')) {
    this.legacyFilesToAppend.push(assetPath);

    this.importWhitelist = assign(this.importWhitelist, options.exports || {});
  } else if (extension === '.css') {
    this.vendorStaticStyles.push(assetPath);
  } else {
    var otherAssetTree = pickFiles(this.trees.vendor, {
      srcDir: subdirectory,
      files: [basename],
      destDir: subdirectory
    });

    this.otherAssetTrees.push(otherAssetTree);
  }
};

EmberApp.prototype.fingerprint = function(tree) {
  return assetRev(tree, {
    fingerprintExtensions: this.options.fingerprint.extensions,
    fingerprintExclude:    this.options.fingerprint.exclude,
    replaceExtensions:     this.options.fingerprint.replaceExtensions,
    prependPath:           this.options.fingerprint.prepend,
    customHash:            this.options.fingerprint.customHash
  });
};

EmberApp.prototype.toArray = function() {
  var sourceTrees = [
    this.index(),
    this.javascript(),
    this.publicFolder(),
    this.styles(),
    this.otherAssets()
  ];

  if (this.tests) {
    sourceTrees = sourceTrees.concat(this.testIndex(), this.testFiles());
  }

  return sourceTrees;
};

EmberApp.prototype.toTree = memoize(function() {
  var tree = mergeTrees(this.toArray(), {
    overwrite: true,
    description: 'TreeMerger (allTrees)'
  });

  if (this.options.fingerprint.enabled === true) {
    tree = this.fingerprint(tree);
  }

  return tree;
});

function injectENVJson(fn, env, tree, files) {
  // TODO: real templating
  var envJsonString = function(){
    return JSON.stringify(fn(env));
  };

  var baseTag = function(){
    var envJSON      = fn(env);
    var baseURL      = cleanBaseURL(envJSON.baseURL);
    var locationType = envJSON.locationType;

    if (locationType === 'hash' || locationType === 'none') {
      return '';
    }

    if (baseURL) {
      return '<base href="' + baseURL + '" />';
    } else {
      return '';
    }
  };

  return replace(tree, {
    files: files,
    patterns: [{
      match: /\{\{ENV\}\}/g,
      replacement: envJsonString
    },
    {
      match: /\{\{BASE_TAG\}\}/g,
      replacement: baseTag
    }]
  });
}
<|endoftext|>


<commit_before>'use strict'

const {Buffer} = require('buffer')
const electron = require('electron')
const v8Util = process.atomBinding('v8_util')
const {ipcMain, isPromise, webContents} = electron

const fs = require('fs')

const objectsRegistry = require('./objects-registry')

const hasProp = {}.hasOwnProperty

// The internal properties of Function.
const FUNCTION_PROPERTIES = [
  'length', 'name', 'arguments', 'caller', 'prototype'
]

// The remote functions in renderer processes.
// id => Function
let rendererFunctions = v8Util.createDoubleIDWeakMap()

// Return the description of object's members:
let getObjectMembers = function (object) {
  let names = Object.getOwnPropertyNames(object)
  // For Function, we should not override following properties even though they
  // are "own" properties.
  if (typeof object === 'function') {
    names = names.filter((name) => {
      return !FUNCTION_PROPERTIES.includes(name)
    })
  }
  // Map properties to descriptors.
  return names.map((name) => {
    let descriptor = Object.getOwnPropertyDescriptor(object, name)
    let member = {name, enumerable: descriptor.enumerable, writable: false}
    if (descriptor.get === undefined && typeof object[name] === 'function') {
      member.type = 'method'
    } else {
      if (descriptor.set || descriptor.writable) member.writable = true
      member.type = 'get'
    }
    return member
  })
}

// Return the description of object's prototype.
let getObjectPrototype = function (object) {
  let proto = Object.getPrototypeOf(object)
  if (proto === null || proto === Object.prototype) return null
  return {
    members: getObjectMembers(proto),
    proto: getObjectPrototype(proto)
  }
}

// Convert a real value into meta data.
let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
  // Determine the type of value.
  const meta = { type: typeof value }
  if (meta.type === 'object') {
    // Recognize certain types of objects.
    if (value === null) {
      meta.type = 'value'
    } else if (ArrayBuffer.isView(value)) {
      meta.type = 'buffer'
    } else if (Array.isArray(value)) {
      meta.type = 'array'
    } else if (value instanceof Error) {
      meta.type = 'error'
    } else if (value instanceof Date) {
      meta.type = 'date'
    } else if (isPromise(value)) {
      meta.type = 'promise'
    } else if (hasProp.call(value, 'callee') && value.length != null) {
      // Treat the arguments object as array.
      meta.type = 'array'
    } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
      // Treat simple objects as value.
      meta.type = 'value'
    }
  }

  // Fill the meta object according to value's type.
  if (meta.type === 'array') {
    meta.members = value.map((el) => valueToMeta(sender, el))
  } else if (meta.type === 'object' || meta.type === 'function') {
    meta.name = value.constructor ? value.constructor.name : ''

    // Reference the original value if it's an object, because when it's
    // passed to renderer we would assume the renderer keeps a reference of
    // it.
    meta.id = objectsRegistry.add(sender, value)
    meta.members = getObjectMembers(value)
    meta.proto = getObjectPrototype(value)
  } else if (meta.type === 'buffer') {
    meta.value = Buffer.from(value)
  } else if (meta.type === 'promise') {
    // Add default handler to prevent unhandled rejections in main process
    // Instead they should appear in the renderer process
    value.then(function () {}, function () {})

    meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
      value.then(onFulfilled, onRejected)
    })
  } else if (meta.type === 'error') {
    meta.members = plainObjectToMeta(value)

    // Error.name is not part of own properties.
    meta.members.push({
      name: 'name',
      value: value.name
    })
  } else if (meta.type === 'date') {
    meta.value = value.getTime()
  } else {
    meta.type = 'value'
    meta.value = value
  }
  return meta
}

// Convert object to meta by value.
const plainObjectToMeta = function (obj) {
  return Object.getOwnPropertyNames(obj).map(function (name) {
    return {
      name: name,
      value: obj[name]
    }
  })
}

// Convert Error into meta data.
const exceptionToMeta = function (error) {
  return {
    type: 'exception',
    message: error.message,
    stack: error.stack || error
  }
}

const throwRPCError = function (message) {
  const error = new Error(message)
  error.code = 'EBADRPC'
  error.errno = 72
  throw error
}

// Convert array of meta data from renderer into array of real values.
const unwrapArgs = function (sender, args) {
  const metaToValue = function (meta) {
    let i, len, member, ref, returnValue
    switch (meta.type) {
      case 'value':
        return meta.value
      case 'remote-object':
        return objectsRegistry.get(meta.id)
      case 'array':
        return unwrapArgs(sender, meta.value)
      case 'buffer':
        return Buffer.from(meta.value)
      case 'date':
        return new Date(meta.value)
      case 'promise':
        return Promise.resolve({
          then: metaToValue(meta.then)
        })
      case 'object': {
        let ret = {}
        Object.defineProperty(ret.constructor, 'name', { value: meta.name })

        ref = meta.members
        for (i = 0, len = ref.length; i < len; i++) {
          member = ref[i]
          ret[member.name] = metaToValue(member.value)
        }
        return ret
      }
      case 'function-with-return-value':
        returnValue = metaToValue(meta.value)
        return function () {
          return returnValue
        }
      case 'function': {
        // Merge webContentsId and meta.id, since meta.id can be the same in
        // different webContents.
        const webContentsId = sender.getId()
        const objectId = [webContentsId, meta.id]

        // Cache the callbacks in renderer.
        if (rendererFunctions.has(objectId)) {
          return rendererFunctions.get(objectId)
        }

        let callIntoRenderer = function (...args) {
          if (!sender.isDestroyed() && webContentsId === sender.getId()) {
            sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args))
          } else {
            throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`)
          }
        }

        v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender)
        rendererFunctions.set(objectId, callIntoRenderer)
        return callIntoRenderer
      }
      default:
        throw new TypeError(`Unknown type: ${meta.type}`)
    }
  }
  return args.map(metaToValue)
}

// Call a function and send reply asynchronously if it's a an asynchronous
// style function and the caller didn't pass a callback.
const callFunction = function (event, func, caller, args) {
  let funcMarkedAsync, funcName, funcPassedCallback, ref, ret
  funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
  funcPassedCallback = typeof args[args.length - 1] === 'function'
  try {
    if (funcMarkedAsync && !funcPassedCallback) {
      args.push(function (ret) {
        event.returnValue = valueToMeta(event.sender, ret, true)
      })
      func.apply(caller, args)
    } else {
      ret = func.apply(caller, args)
      event.returnValue = valueToMeta(event.sender, ret, true)
    }
  } catch (error) {
    // Catch functions thrown further down in function invocation and wrap
    // them with the function name so it's easier to trace things like
    // `Error processing argument -1.`
    funcName = ((ref = func.name) != null) ? ref : 'anonymous'
    throw new Error(`Could not call remote function '${funcName}'. Check that the function signature is correct. Underlying error: ${error.message}`)
  }
}

ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
  try {
    event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_READ_FILE', function (event, file) {
  fs.readFile(file, (err, data) => {
    if (err) event.returnValue = {err: err.message}
    else event.returnValue = {data: data.toString()}
  })
})

ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
  try {
    event.returnValue = valueToMeta(event.sender, electron[module])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
  try {
    event.returnValue = valueToMeta(event.sender, global[name])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
  try {
    event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) {
  event.returnValue = valueToMeta(event.sender, event.sender)
})

ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let constructor = objectsRegistry.get(id)

    if (constructor == null) {
      throwRPCError(`Cannot call constructor on missing remote object ${id}`)
    }

    // Call new with array of arguments.
    // http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
    let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
    event.returnValue = valueToMeta(event.sender, obj)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let func = objectsRegistry.get(id)

    if (func == null) {
      throwRPCError(`Cannot call function on missing remote object ${id}`)
    }

    callFunction(event, func, global, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let object = objectsRegistry.get(id)

    if (object == null) {
      throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
    }

    // Call new with array of arguments.
    let constructor = object[method]
    let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
    event.returnValue = valueToMeta(event.sender, obj)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let obj = objectsRegistry.get(id)

    if (obj == null) {
      throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
    }

    callFunction(event, obj[method], obj, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, value) {
  try {
    let obj = objectsRegistry.get(id)

    if (obj == null) {
      throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
    }

    obj[name] = value
    event.returnValue = null
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
  try {
    let obj = objectsRegistry.get(id)
    event.returnValue = valueToMeta(event.sender, obj[name])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) {
  objectsRegistry.remove(event.sender.getId(), id)
})

ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) {
  try {
    let guestViewManager = require('./guest-view-manager')
    event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) {
  try {
    let guestViewManager = require('./guest-view-manager')
    let guest = guestViewManager.getGuest(guestInstanceId)
    if (requestId) {
      const responseCallback = function (result) {
        event.sender.send(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, result)
      }
      args.push(responseCallback)
    }
    guest[method].apply(guest, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_SEND_TO', function (event, sendToAll, webContentsId, channel, ...args) {
  let contents = webContents.fromId(webContentsId)
  if (!contents) {
    console.error(`Sending message to WebContents with unknown ID ${webContentsId}`)
    return
  }

  if (sendToAll) {
    contents.sendToAll(channel, ...args)
  } else {
    contents.send(channel, ...args)
  }
})
<commit_msg>Negate code as per node docs<commit_after>'use strict'

const {Buffer} = require('buffer')
const electron = require('electron')
const v8Util = process.atomBinding('v8_util')
const {ipcMain, isPromise, webContents} = electron

const fs = require('fs')

const objectsRegistry = require('./objects-registry')

const hasProp = {}.hasOwnProperty

// The internal properties of Function.
const FUNCTION_PROPERTIES = [
  'length', 'name', 'arguments', 'caller', 'prototype'
]

// The remote functions in renderer processes.
// id => Function
let rendererFunctions = v8Util.createDoubleIDWeakMap()

// Return the description of object's members:
let getObjectMembers = function (object) {
  let names = Object.getOwnPropertyNames(object)
  // For Function, we should not override following properties even though they
  // are "own" properties.
  if (typeof object === 'function') {
    names = names.filter((name) => {
      return !FUNCTION_PROPERTIES.includes(name)
    })
  }
  // Map properties to descriptors.
  return names.map((name) => {
    let descriptor = Object.getOwnPropertyDescriptor(object, name)
    let member = {name, enumerable: descriptor.enumerable, writable: false}
    if (descriptor.get === undefined && typeof object[name] === 'function') {
      member.type = 'method'
    } else {
      if (descriptor.set || descriptor.writable) member.writable = true
      member.type = 'get'
    }
    return member
  })
}

// Return the description of object's prototype.
let getObjectPrototype = function (object) {
  let proto = Object.getPrototypeOf(object)
  if (proto === null || proto === Object.prototype) return null
  return {
    members: getObjectMembers(proto),
    proto: getObjectPrototype(proto)
  }
}

// Convert a real value into meta data.
let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
  // Determine the type of value.
  const meta = { type: typeof value }
  if (meta.type === 'object') {
    // Recognize certain types of objects.
    if (value === null) {
      meta.type = 'value'
    } else if (ArrayBuffer.isView(value)) {
      meta.type = 'buffer'
    } else if (Array.isArray(value)) {
      meta.type = 'array'
    } else if (value instanceof Error) {
      meta.type = 'error'
    } else if (value instanceof Date) {
      meta.type = 'date'
    } else if (isPromise(value)) {
      meta.type = 'promise'
    } else if (hasProp.call(value, 'callee') && value.length != null) {
      // Treat the arguments object as array.
      meta.type = 'array'
    } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
      // Treat simple objects as value.
      meta.type = 'value'
    }
  }

  // Fill the meta object according to value's type.
  if (meta.type === 'array') {
    meta.members = value.map((el) => valueToMeta(sender, el))
  } else if (meta.type === 'object' || meta.type === 'function') {
    meta.name = value.constructor ? value.constructor.name : ''

    // Reference the original value if it's an object, because when it's
    // passed to renderer we would assume the renderer keeps a reference of
    // it.
    meta.id = objectsRegistry.add(sender, value)
    meta.members = getObjectMembers(value)
    meta.proto = getObjectPrototype(value)
  } else if (meta.type === 'buffer') {
    meta.value = Buffer.from(value)
  } else if (meta.type === 'promise') {
    // Add default handler to prevent unhandled rejections in main process
    // Instead they should appear in the renderer process
    value.then(function () {}, function () {})

    meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
      value.then(onFulfilled, onRejected)
    })
  } else if (meta.type === 'error') {
    meta.members = plainObjectToMeta(value)

    // Error.name is not part of own properties.
    meta.members.push({
      name: 'name',
      value: value.name
    })
  } else if (meta.type === 'date') {
    meta.value = value.getTime()
  } else {
    meta.type = 'value'
    meta.value = value
  }
  return meta
}

// Convert object to meta by value.
const plainObjectToMeta = function (obj) {
  return Object.getOwnPropertyNames(obj).map(function (name) {
    return {
      name: name,
      value: obj[name]
    }
  })
}

// Convert Error into meta data.
const exceptionToMeta = function (error) {
  return {
    type: 'exception',
    message: error.message,
    stack: error.stack || error
  }
}

const throwRPCError = function (message) {
  const error = new Error(message)
  error.code = 'EBADRPC'
  error.errno = -72
  throw error
}

// Convert array of meta data from renderer into array of real values.
const unwrapArgs = function (sender, args) {
  const metaToValue = function (meta) {
    let i, len, member, ref, returnValue
    switch (meta.type) {
      case 'value':
        return meta.value
      case 'remote-object':
        return objectsRegistry.get(meta.id)
      case 'array':
        return unwrapArgs(sender, meta.value)
      case 'buffer':
        return Buffer.from(meta.value)
      case 'date':
        return new Date(meta.value)
      case 'promise':
        return Promise.resolve({
          then: metaToValue(meta.then)
        })
      case 'object': {
        let ret = {}
        Object.defineProperty(ret.constructor, 'name', { value: meta.name })

        ref = meta.members
        for (i = 0, len = ref.length; i < len; i++) {
          member = ref[i]
          ret[member.name] = metaToValue(member.value)
        }
        return ret
      }
      case 'function-with-return-value':
        returnValue = metaToValue(meta.value)
        return function () {
          return returnValue
        }
      case 'function': {
        // Merge webContentsId and meta.id, since meta.id can be the same in
        // different webContents.
        const webContentsId = sender.getId()
        const objectId = [webContentsId, meta.id]

        // Cache the callbacks in renderer.
        if (rendererFunctions.has(objectId)) {
          return rendererFunctions.get(objectId)
        }

        let callIntoRenderer = function (...args) {
          if (!sender.isDestroyed() && webContentsId === sender.getId()) {
            sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args))
          } else {
            throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`)
          }
        }

        v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender)
        rendererFunctions.set(objectId, callIntoRenderer)
        return callIntoRenderer
      }
      default:
        throw new TypeError(`Unknown type: ${meta.type}`)
    }
  }
  return args.map(metaToValue)
}

// Call a function and send reply asynchronously if it's a an asynchronous
// style function and the caller didn't pass a callback.
const callFunction = function (event, func, caller, args) {
  let funcMarkedAsync, funcName, funcPassedCallback, ref, ret
  funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
  funcPassedCallback = typeof args[args.length - 1] === 'function'
  try {
    if (funcMarkedAsync && !funcPassedCallback) {
      args.push(function (ret) {
        event.returnValue = valueToMeta(event.sender, ret, true)
      })
      func.apply(caller, args)
    } else {
      ret = func.apply(caller, args)
      event.returnValue = valueToMeta(event.sender, ret, true)
    }
  } catch (error) {
    // Catch functions thrown further down in function invocation and wrap
    // them with the function name so it's easier to trace things like
    // `Error processing argument -1.`
    funcName = ((ref = func.name) != null) ? ref : 'anonymous'
    throw new Error(`Could not call remote function '${funcName}'. Check that the function signature is correct. Underlying error: ${error.message}`)
  }
}

ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
  try {
    event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_READ_FILE', function (event, file) {
  fs.readFile(file, (err, data) => {
    if (err) event.returnValue = {err: err.message}
    else event.returnValue = {data: data.toString()}
  })
})

ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
  try {
    event.returnValue = valueToMeta(event.sender, electron[module])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
  try {
    event.returnValue = valueToMeta(event.sender, global[name])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
  try {
    event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) {
  event.returnValue = valueToMeta(event.sender, event.sender)
})

ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let constructor = objectsRegistry.get(id)

    if (constructor == null) {
      throwRPCError(`Cannot call constructor on missing remote object ${id}`)
    }

    // Call new with array of arguments.
    // http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
    let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
    event.returnValue = valueToMeta(event.sender, obj)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let func = objectsRegistry.get(id)

    if (func == null) {
      throwRPCError(`Cannot call function on missing remote object ${id}`)
    }

    callFunction(event, func, global, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let object = objectsRegistry.get(id)

    if (object == null) {
      throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
    }

    // Call new with array of arguments.
    let constructor = object[method]
    let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
    event.returnValue = valueToMeta(event.sender, obj)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let obj = objectsRegistry.get(id)

    if (obj == null) {
      throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
    }

    callFunction(event, obj[method], obj, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, value) {
  try {
    let obj = objectsRegistry.get(id)

    if (obj == null) {
      throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
    }

    obj[name] = value
    event.returnValue = null
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
  try {
    let obj = objectsRegistry.get(id)
    event.returnValue = valueToMeta(event.sender, obj[name])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) {
  objectsRegistry.remove(event.sender.getId(), id)
})

ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) {
  try {
    let guestViewManager = require('./guest-view-manager')
    event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) {
  try {
    let guestViewManager = require('./guest-view-manager')
    let guest = guestViewManager.getGuest(guestInstanceId)
    if (requestId) {
      const responseCallback = function (result) {
        event.sender.send(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, result)
      }
      args.push(responseCallback)
    }
    guest[method].apply(guest, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_SEND_TO', function (event, sendToAll, webContentsId, channel, ...args) {
  let contents = webContents.fromId(webContentsId)
  if (!contents) {
    console.error(`Sending message to WebContents with unknown ID ${webContentsId}`)
    return
  }

  if (sendToAll) {
    contents.sendToAll(channel, ...args)
  } else {
    contents.send(channel, ...args)
  }
})
<|endoftext|>


<commit_before>const _ = require('lodash/fp')
const pgp = require('pg-promise')()
const pEachSeries = require('p-each-series')

const blacklist = require('../blacklist')
const db = require('../db')
const plugins = require('../plugins')
const logger = require('../logger')
const settingsLoader = require('../new-settings-loader')
const configManager = require('../new-config-manager')
const notifier = require('../notifier')

const cashInAtomic = require('./cash-in-atomic')
const cashInLow = require('./cash-in-low')

const PENDING_INTERVAL = '60 minutes'
const MAX_PENDING = 10

const TRANSACTION_STATES = `
case
  when operator_completed and error = 'Operator cancel' then 'Cancelled'
  when error is not null then 'Error'
  when send_confirmed then 'Sent'
  when ((not send_confirmed) and (created <= now() - interval '${PENDING_INTERVAL}')) then 'Expired'
  else 'Pending'
end`

module.exports = {post, monitorPending, cancel, PENDING_INTERVAL, TRANSACTION_STATES}

function post (machineTx, pi) {
  logger.silly('Updating cashin tx:', machineTx)
  return cashInAtomic.atomic(machineTx, pi)
    .then(r => {
      const updatedTx = r.tx
      let blacklisted = false
      let addressReuse = false
      let walletScore = {}

      return Promise.all([settingsLoader.loadLatest(), checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), getWalletScore(updatedTx, pi)])
        .then(([{ config }, blacklistItems, isReusedAddress, fetchedWalletScore]) => {
          const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse

          walletScore = fetchedWalletScore

          if (_.some(it => it.address === updatedTx.toAddress)(blacklistItems)) {
            blacklisted = true
            notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, false)
          } else if (isReusedAddress && rejectAddressReuse) {
            notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, true)
            addressReuse = true
          }
          return postProcess(r, pi, blacklisted, addressReuse, walletScore)
        })
        .then(changes => cashInLow.update(db, updatedTx, changes))
        .then(tx => _.set('bills', machineTx.bills, tx))
        .then(tx => _.set('blacklisted', blacklisted, tx))
        .then(tx => _.set('addressReuse', addressReuse, tx))
        .then(tx => _.set('validWalletScore', _.isNil(walletScore) ? true : walletScore.isValid, tx))
        .then(tx => _.set('walletScore', _.isNil(walletScore) ? null : walletScore.score, tx))
    })
}

function registerTrades (pi, r) {
  _.forEach(bill => pi.buy(bill, r.tx), r.newBills)
}

function logAction (rec, tx) {
  const action = {
    tx_id: tx.id,
    action: rec.action || (rec.sendConfirmed ? 'sendCoins' : 'sendCoinsError'),
    error: rec.error,
    error_code: rec.errorCode,
    tx_hash: rec.txHash
  }

  const sql = pgp.helpers.insert(action, null, 'cash_in_actions')

  return db.none(sql)
    .then(_.constant(rec))
}

function logActionById (action, _rec, txId) {
  const rec = _.assign(_rec, { action, tx_id: txId })
  const sql = pgp.helpers.insert(rec, null, 'cash_in_actions')

  return db.none(sql)
}

function checkForBlacklisted (tx) {
  // Check only on addressScan and avoid testing for blacklist on every bill inserted
  if (!tx.fiat || tx.fiat.isZero()) {
    return blacklist.blocked(tx.toAddress, tx.cryptoCode)
  }
  return Promise.resolve(false)
}

function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
  if (addressReuse) {
    return Promise.resolve({
      operatorCompleted: true,
      error: 'Address Reused'
    })
  }

  if (isBlacklisted) {
    return Promise.resolve({
      operatorCompleted: true,
      error: 'Blacklisted Address'
    })
  }

  if (!_.isNil(walletScore) && !walletScore.isValid) {
    return Promise.resolve({
      walletScore: walletScore.score,
      operatorCompleted: true,
      error: 'Ciphertrace score is above defined threshold'
    })
  }

  registerTrades(pi, r)

  if (!cashInLow.isClearToSend(r.dbTx, r.tx)) return Promise.resolve({})

  return pi.sendCoins(r.tx)
    .then(txObj => {
      if (txObj.batched) {
        return {
          batched: true,
          batchTime: 'now()^',
          sendPending: true,
          error: null,
          errorCode: null
        }
      }

      return {
        txHash: txObj.txid,
        fee: txObj.fee,
        sendConfirmed: true,
        sendTime: 'now()^',
        sendPending: false,
        error: null,
        errorCode: null
      }
    })
    .catch(err => {
    // Important: We don't know what kind of error this is
    // so not safe to assume that funds weren't sent.
    // Therefore, don't set sendPending to false except for
    // errors (like InsufficientFundsError) that are guaranteed
    // not to send.
      const sendPending = err.name !== 'InsufficientFundsError'

      return {
        sendTime: 'now()^',
        error: err.message,
        errorCode: err.name,
        sendPending
      }
    })
    .then(sendRec => {
      pi.notifyOperator(r.tx, sendRec)
        .catch((err) => logger.error('Failure sending transaction notification', err))
      return logAction(sendRec, r.tx)
    })
}

function doesTxReuseAddress (tx) {
  if (!tx.fiat || tx.fiat.isZero()) {
    const sql = `SELECT EXISTS (SELECT DISTINCT to_address FROM cash_in_txs WHERE to_address = $1)`
    return db.any(sql, [tx.toAddress])
  }
  return Promise.resolve(false)
}

function getWalletScore (tx, pi) {
  if (!tx.fiat || tx.fiat.isZero()) {
    return pi.rateWallet(tx.cryptoCode, tx.toAddress)
  }
  // Passthrough the previous result
  return pi.isValidWalletScore(tx.walletScore)
    .then(isValid => ({
      address: tx.toAddress,
      score: tx.walletScore,
      isValid
    }))
}

function monitorPending (settings) {
  const sql = `select * from cash_in_txs
  where created > now() - interval $1
  and send
  and not send_confirmed
  and not send_pending
  and not operator_completed
  order by created
  limit $2`

  const processPending = row => {
    const tx = cashInLow.toObj(row)
    const pi = plugins(settings, tx.deviceId)

    return post(tx, pi)
      .catch(logger.error)
  }

  return db.any(sql, [PENDING_INTERVAL, MAX_PENDING])
    .then(rows => pEachSeries(rows, row => processPending(row)))
    .catch(logger.error)
}

function cancel (txId) {
  const updateRec = {
    error: 'Operator cancel',
    error_code: 'operatorCancel',
    operator_completed: true
  }

  return Promise.resolve()
    .then(() => {
      return pgp.helpers.update(updateRec, null, 'cash_in_txs') +
      pgp.as.format(' where id=$1', [txId])
    })
    .then(sql => db.result(sql, false))
    .then(res => {
      if (res.rowCount !== 1) throw new Error('No such tx-id')
    })
    .then(() => logActionById('operatorCompleted', {}, txId))
}
<commit_msg>fix: remove cash-in transaction retry<commit_after>const _ = require('lodash/fp')
const pgp = require('pg-promise')()
const pEachSeries = require('p-each-series')

const blacklist = require('../blacklist')
const db = require('../db')
const plugins = require('../plugins')
const logger = require('../logger')
const settingsLoader = require('../new-settings-loader')
const configManager = require('../new-config-manager')
const notifier = require('../notifier')

const cashInAtomic = require('./cash-in-atomic')
const cashInLow = require('./cash-in-low')

const PENDING_INTERVAL = '60 minutes'
const MAX_PENDING = 10

const TRANSACTION_STATES = `
case
  when operator_completed and error = 'Operator cancel' then 'Cancelled'
  when error is not null then 'Error'
  when send_confirmed then 'Sent'
  when ((not send_confirmed) and (created <= now() - interval '${PENDING_INTERVAL}')) then 'Expired'
  else 'Pending'
end`

module.exports = {post, monitorPending, cancel, PENDING_INTERVAL, TRANSACTION_STATES}

function post (machineTx, pi) {
  logger.silly('Updating cashin tx:', machineTx)
  return cashInAtomic.atomic(machineTx, pi)
    .then(r => {
      const updatedTx = r.tx
      let blacklisted = false
      let addressReuse = false
      let walletScore = {}

      return Promise.all([settingsLoader.loadLatest(), checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), getWalletScore(updatedTx, pi)])
        .then(([{ config }, blacklistItems, isReusedAddress, fetchedWalletScore]) => {
          const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse

          walletScore = fetchedWalletScore

          if (_.some(it => it.address === updatedTx.toAddress)(blacklistItems)) {
            blacklisted = true
            notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, false)
          } else if (isReusedAddress && rejectAddressReuse) {
            notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, true)
            addressReuse = true
          }
          return postProcess(r, pi, blacklisted, addressReuse, walletScore)
        })
        .then(changes => cashInLow.update(db, updatedTx, changes))
        .then(tx => _.set('bills', machineTx.bills, tx))
        .then(tx => _.set('blacklisted', blacklisted, tx))
        .then(tx => _.set('addressReuse', addressReuse, tx))
        .then(tx => _.set('validWalletScore', _.isNil(walletScore) ? true : walletScore.isValid, tx))
        .then(tx => _.set('walletScore', _.isNil(walletScore) ? null : walletScore.score, tx))
    })
}

function registerTrades (pi, r) {
  _.forEach(bill => pi.buy(bill, r.tx), r.newBills)
}

function logAction (rec, tx) {
  const action = {
    tx_id: tx.id,
    action: rec.action || (rec.sendConfirmed ? 'sendCoins' : 'sendCoinsError'),
    error: rec.error,
    error_code: rec.errorCode,
    tx_hash: rec.txHash
  }

  const sql = pgp.helpers.insert(action, null, 'cash_in_actions')

  return db.none(sql)
    .then(_.constant(rec))
}

function logActionById (action, _rec, txId) {
  const rec = _.assign(_rec, { action, tx_id: txId })
  const sql = pgp.helpers.insert(rec, null, 'cash_in_actions')

  return db.none(sql)
}

function checkForBlacklisted (tx) {
  // Check only on addressScan and avoid testing for blacklist on every bill inserted
  if (!tx.fiat || tx.fiat.isZero()) {
    return blacklist.blocked(tx.toAddress, tx.cryptoCode)
  }
  return Promise.resolve(false)
}

function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
  if (addressReuse) {
    return Promise.resolve({
      operatorCompleted: true,
      error: 'Address Reused'
    })
  }

  if (isBlacklisted) {
    return Promise.resolve({
      operatorCompleted: true,
      error: 'Blacklisted Address'
    })
  }

  if (!_.isNil(walletScore) && !walletScore.isValid) {
    return Promise.resolve({
      walletScore: walletScore.score,
      operatorCompleted: true,
      error: 'Ciphertrace score is above defined threshold'
    })
  }

  registerTrades(pi, r)

  if (!cashInLow.isClearToSend(r.dbTx, r.tx)) return Promise.resolve({})

  return pi.sendCoins(r.tx)
    .then(txObj => {
      if (txObj.batched) {
        return {
          batched: true,
          batchTime: 'now()^',
          sendPending: true,
          error: null,
          errorCode: null
        }
      }

      return {
        txHash: txObj.txid,
        fee: txObj.fee,
        sendConfirmed: true,
        sendTime: 'now()^',
        sendPending: false,
        error: null,
        errorCode: null
      }
    })
    .catch(err => {
      // Important: We don't know what kind of error this is
      // so not safe to assume that funds weren't sent.

      // Setting sendPending to true ensures that the transaction gets
      // silently terminated and no retries are done

      return {
        sendTime: 'now()^',
        error: err.message,
        errorCode: err.name,
        sendPending: true
      }
    })
    .then(sendRec => {
      pi.notifyOperator(r.tx, sendRec)
        .catch((err) => logger.error('Failure sending transaction notification', err))
      return logAction(sendRec, r.tx)
    })
}

function doesTxReuseAddress (tx) {
  if (!tx.fiat || tx.fiat.isZero()) {
    const sql = `SELECT EXISTS (SELECT DISTINCT to_address FROM cash_in_txs WHERE to_address = $1)`
    return db.any(sql, [tx.toAddress])
  }
  return Promise.resolve(false)
}

function getWalletScore (tx, pi) {
  if (!tx.fiat || tx.fiat.isZero()) {
    return pi.rateWallet(tx.cryptoCode, tx.toAddress)
  }
  // Passthrough the previous result
  return pi.isValidWalletScore(tx.walletScore)
    .then(isValid => ({
      address: tx.toAddress,
      score: tx.walletScore,
      isValid
    }))
}

function monitorPending (settings) {
  const sql = `select * from cash_in_txs
  where created > now() - interval $1
  and send
  and not send_confirmed
  and not send_pending
  and not operator_completed
  order by created
  limit $2`

  const processPending = row => {
    const tx = cashInLow.toObj(row)
    const pi = plugins(settings, tx.deviceId)

    return post(tx, pi)
      .catch(logger.error)
  }

  return db.any(sql, [PENDING_INTERVAL, MAX_PENDING])
    .then(rows => pEachSeries(rows, row => processPending(row)))
    .catch(logger.error)
}

function cancel (txId) {
  const updateRec = {
    error: 'Operator cancel',
    error_code: 'operatorCancel',
    operator_completed: true
  }

  return Promise.resolve()
    .then(() => {
      return pgp.helpers.update(updateRec, null, 'cash_in_txs') +
      pgp.as.format(' where id=$1', [txId])
    })
    .then(sql => db.result(sql, false))
    .then(res => {
      if (res.rowCount !== 1) throw new Error('No such tx-id')
    })
    .then(() => logActionById('operatorCompleted', {}, txId))
}
<|endoftext|>