<commit_before>/**
 * Alloy
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * See LICENSE for more information on licensing.
 */
var fs = require('fs'),
	path = require('path')
	program = require('commander'),
	logger = require("./common/logger"),
	U = require('./utils'),
	wrench = require("wrench"),
	colors = require("colors"),
	_ = require("./lib/alloy/underscore")._,
	DOMParser = require("xmldom").DOMParser,
	XMLSerializer = require("xmldom").XMLSerializer,
	jsp = require("./uglify-js/uglify-js").parser,
	pro = require("./uglify-js/uglify-js").uglify,
	generators = require('./generators'),
	pkginfo = require('pkginfo')(module, 'name', 'version');

//
//TODO: we need a much more robust help from command line -- see sort of what i did in titanium
//TODO: handle localization files and merging
//TODO: uglify all files not just our main app.js
//

/**
 * ACTIONS:
 *
 * - new [path] - create a new project
 * - compile - compile the project
 * - generate [type] - generate an object such as a model or controller
 */
		
program
	.version(module.exports.version)
	.description('Alloy command line')
	.usage('ACTION [ARGS] [OPTIONS]')
	.option('-o, --outputPath <outputPath>', 'Output path for generated code')
	.option('-l, --logLevel <logLevel>', 'Log level (default: 3 [DEBUG])')
	.option('-d, --dump','Dump the generated app.js to console')
	.option('-f, --force','Force the command to execute')
	.option('-n, --no-colors','Turn off colors')
	.option('-c, --config <config>','Pass in compiler configuration')
	.parse(process.argv);

var outputPath,
	// ids = {},
	compilerMakeFile,
	alloyUniqueIdPrefix = '__alloyId',
	alloyUniqueIdCounter = 0;
	
function CompilerMakeFile()
{
	var handlers = {};
	
	this.require = require;
	this.process = process;
	
	this.task = function(event, fn)
	{
		logger.debug('adding task: '+event.yellow);
		handlers[event] = fn;
	};
	
	this.trigger = function(event, config)
	{
		logger.debug("compile:trigger-> "+event.yellow);
		var fn = handlers[event];
		if (fn)
		{
			return fn(config,logger);
		}
		return null;
	};
	
	return this;
}	

function generateVarName(id)
{
	return '$.'+id;
}

function die(msg, printUsage) 
{
	printUsage = typeof printUsage === 'undefined' ? false : printUsage;
	logger.error(msg);
	if (printUsage) {
		logger.info(program.helpInformation());
	}
	process.exit(1);
}

function getNodeText(node)
{
	var str = '';
	var serializer = new XMLSerializer();
	for (var c=0;c<node.childNodes.length;c++)
	{
		if (node.childNodes[c].nodeType!=1)
		{
			str += serializer.serializeToString(node.childNodes[c]);
		}
	}
	return str;
}

function loadStyle(p)
{
	if (path.existsSync(p))
	{
		var f = fs.readFileSync(p);
		f = String(f);

		// skip empty files
		if (/^\s*$/.test(f)) {
			return {};
		}

		f = f.replace(/Titanium\./g,"Ti.");
		// fixup constants so we can use them in JSON but then we do magic conversions
		f = f.replace(/Ti\.UI\.FILL/g,'"TI_UI_FILL"');
		f = f.replace(/Ti\.UI\.SIZE/g,'"TI_UI_SIZE"');
		f = f.replace(/Ti\.UI\.TEXT_ALIGNMENT_LEFT/g,'"TI_UI_TEXT_ALIGNMENT_LEFT"')
		f = f.replace(/Ti\.UI\.TEXT_ALIGNMENT_RIGHT/g,'"TI_UI_TEXT_ALIGNMENT_RIGHT"')
		f = f.replace(/Ti\.UI\.TEXT_ALIGNMENT_CENTER/g,'"TI_UI_TEXT_ALIGNMENT_CENTER"')
		try 
		{
			return JSON.parse(f);
		}
		catch(E)
		{
			die("Error parsing style at "+p.yellow+".  Error was: "+String(E).red);
		}
	}
	return {};
}

function banner()
{
	var str = 
	"       .__  .__                \n"+
	"_____  |  | |  |   ____ ___.__.\n"+
	"\\__  \\ |  | |  |  /  _ <   |  |\n"+
	" / __ \\|  |_|  |_(  <_> )___  |\n"+
	"(____  /____/____/\\____// ____|\n"+
	"     \\/                 \\/";
	
	if (!program.dump)
	{
		console.log(logger.stripColors ? str : str.blue);
		var m = "Alloy by Appcelerator. The MVC app framework for Titanium.\n".white;
		console.log(logger.stripColors ? colors.stripColors(m) : m);
	}
}

function compile(args)
{
	var inputPath = args.length > 0 ? args[0] : resolveAppHome();

	if (!path.existsSync(inputPath)) 
	{
		die('inputPath "' + inputPath + '" does not exist');
	}	

	if (!program.outputPath)
	{
		var t = path.join(inputPath,'views','index.xml');
		if (path.existsSync(t))
		{
			outputPath = path.join(inputPath,'..');
		}
	}
	outputPath = outputPath ? outputPath : (program.outputPath || path.join(resolveAppHome(),".."));
	U.ensureDir(outputPath);
	
	if (program.config)
	{
		var pc = {};
		_.each(program.config.split(','),function(v)
		{
			var a = v.split('=');
			pc[a[0]]=a[1];
		});
		program.config = pc;
	}
	
	var alloyConfig = {};
	var alloyCF = path.join(inputPath,'alloy.json');
	if (path.existsSync(alloyCF))
	{
		var c = fs.readFileSync(alloyCF);
		alloyConfig = JSON.parse(c);
		logger.info("found alloy configuration at "+alloyCF.yellow);
	}

	var viewsDir = path.join(inputPath,'views');
	if (!path.existsSync(viewsDir))
	{
		die("Couldn't find expected views directory at '"+viewsDir+"'");
	}
	var stylesDir = path.join(inputPath,'styles');
	var controllersDir = path.join(inputPath,'controllers');
	var widgetsDir = path.join(inputPath,'widgets');
	var modelsDir = path.join(inputPath,'models');
	var migrationsDir = path.join(inputPath,'migrations');
	var configDir = path.join(inputPath,'config');

	var indexView = path.join(viewsDir,"index.xml");
	if (!path.existsSync(indexView))
	{
		die("Couldn't find expected index view at '"+indexView+"'");
	}
	var resourcesDir = path.join(outputPath,"Resources");
	U.ensureDir(resourcesDir);

	var assetsDir = path.join(inputPath,'assets');

	logger.info("Generating to "+resourcesDir.yellow+" from ".cyan + inputPath.yellow);

	// setup the compiler makefile
	compilerMakeFile = new CompilerMakeFile();
	var compileConfig = 
	{
		alloyConfig: alloyConfig,
		home: path.resolve(inputPath),
		projectDir : path.resolve(outputPath),
		viewsDir: path.resolve(viewsDir),
		controllersDir: path.resolve(controllersDir),
		widgetsDir: path.resolve(widgetsDir),
		modelsDir: path.resolve(modelsDir),
		migrationsDir: path.resolve(migrationsDir),
		resourcesDir: path.resolve(resourcesDir),
		assetsDir: path.resolve(assetsDir),
		configDir:path.resolve(configDir)
	};
	
	var alloyJMK = path.resolve(path.normalize(path.join(inputPath,"alloy.jmk")));
	if (path.existsSync(alloyJMK))
	{
		logger.info("Found project specific makefile at " + "app/alloy.jmk".yellow);
		var vm = require('vm'),
			util = require('util');
		var script = vm.createScript(fs.readFileSync(alloyJMK), 'alloy.jmk');
		try
		{
			script.runInNewContext(compilerMakeFile);
		}
		catch(E)
		{
			logger.error("project build at "+alloyJMK.yellow + " generated an error during load: "+E.red);
		}
	}
	
	// trigger our custom compiler makefile
	compilerMakeFile.trigger("pre:compile",_.clone(compileConfig));

	function generateConfig()
	{
		var cf = path.join(configDir,'config.json');
		if (path.existsSync(cf))
		{
			var jf = fs.readFileSync(cf);
			var j = JSON.parse(jf);
			
			var o = j.global || {};
			if (program.config)
			{
				o = _.extend(o, j['env:'+program.config.deploytype]);
				o = _.extend(o, j['os:'+program.config.platform]);
			}
			return "$.CFG = " + JSON.stringify(o) + ";";
		}
		return '';
	}

	function generateSourceCode()
	{
		var appJS = path.join(resourcesDir,"app.js");
		
		if (!program.config)
		{
			program.config = {
				deploytype:"development"
			};
		}
		
		var DEFINES = 
		{
			OS_IOS : program.config.platform == 'ios',
			OS_ANDROID: program.config.platform == 'android',
			OS_MOBILEWEB: program.config.platform == 'mobileweb',
			ENV_DEV: program.config.deploytype == 'development',
			ENV_DEVELOPMENT: program.config.deploytype == 'development',
			ENV_TEST: program.config.deploytype == 'test',
			ENV_PRODUCTION: program.config.deploytype == 'production'
		};
		
		var defines = {};
		for (var k in DEFINES)
		{
			defines[k] = [ "num", DEFINES[k] ? 1 : 0 ];
		}
		
		var cfg = generateConfig();
		var code = _.template(fs.readFileSync(path.join(outputPath,'app','template','app.js'),'utf8'),{config:cfg});
		var beautify = alloyConfig.compiler && typeof alloyConfig.compiler.beautify !== 'undefined' ? alloyConfig.compiler.beautify : program.config.deploytype === 'development' ? true : false;

		var ast = jsp.parse(code); // parse code and get the initial AST
		ast = pro.ast_mangle(ast,{except:['Ti','Titanium'],defines:defines}); // get a new AST with mangled names
		ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
		var final_code = pro.gen_code(ast,{beautify:beautify}); 

		// trigger our custom compiler makefile
		var njs = compilerMakeFile.trigger("compile:app.js",_.extend(_.clone(compileConfig), {"code":final_code, "appJSFile" : path.resolve(appJS)}));
		if (njs)
		{
			final_code = njs;
		}
		
		fs.writeFileSync(appJS,final_code);
		logger.info("compiling alloy to " + appJS.yellow);

		if (program.dump) console.log(final_code.blue);
	}

	function copyAssets()
	{
		if (path.existsSync(assetsDir))
		{
			logger.info('Copying assets from: '+assetsDir.yellow);
			U.copyFilesAndDirs(assetsDir,resourcesDir);
		}
	}

	function copyLibs()
	{
		var lib = path.join(inputPath,'lib');
		if (path.existsSync(lib))
		{
			logger.info('Copying app libs: '+lib.yellow);
			U.copyFilesAndDirs(lib,resourcesDir);
		}
		var vendor = path.join(inputPath,'vendor');
		if (path.existsSync(vendor))
		{
			logger.info('Copying vendor libs: '+vendor.yellow);
			U.copyFilesAndDirs(vendor,path.join(resourcesDir,'vendor'));
		}
	}

	function copyAlloy()
	{
		var lib = path.join(__dirname,'lib');
		U.copyFilesAndDirs(lib,resourcesDir);
	}

	var JSON_NULL = JSON.parse('null');

	function mergeStyles(from,to)
	{
		if (from)
		{
			for (var k in from)
			{
				var v = from[k];
				// for optimization, remove null or undefined values
				if (v == JSON_NULL || typeof(v)==='undefined' || typeof(v)==='null')
				{
					delete to[k];
				}
				else
				{
					to[k] = from[k];
				}
			}
		}
	}

	function properCase (n)
	{
		return n.charAt(0).toUpperCase() + n.substring(1);
	}

	function generateStyleParams(styles,classes,id,className)
	{
		var s = {};
		mergeStyles(styles['View'],s);
		mergeStyles(styles[properCase(className)],s);
		for (var c=0;c<classes.length;c++)
		{
			var clsn = classes[c];
			mergeStyles(styles['.'+clsn],s);
		}
		mergeStyles(styles['#'+id],s);
		if (id) s['id'] = id;
		var str = [];
		
		var constants = {
			'TI_UI_FILL':'TFL$',
			'TI_UI_SIZE':'TSZ$',
			'TI_UI_TEXT_ALIGNMENT_LEFT':'Ti.UI.TEXT_ALIGNMENT_LEFT',
			'TI_UI_TEXT_ALIGNMENT_CENTER':'Ti.UI.TEXT_ALIGNMENT_CENTER',
			'TI_UI_TEXT_ALIGNMENT_RIGHT':'Ti.UI.TEXT_ALIGNMENT_RIGHT'
		};
		
		for (var sn in s)
		{
			var v = s[sn];
			var q = typeof(v) === 'string';
			var cf = constants[v];
			if (cf) {
				v = cf;
				q = false;
			} else if (q) {
				str.push("\t\t"+sn+':'+'"'+v+'"');
			} else {
				str.push("\t\t"+sn+':'+ JSON.stringify(v));
			}
		}
		return str.join(",\n");
	}

	function generateController(name, parameters, dir, state, id)
	{
		var code = '';
		var cd = dir ? path.join(dir,'controllers') : controllersDir;
		var p = path.join(cd,name+'.js');
		var symbol = generateVarName(id);
		
		if (path.existsSync(p)) {
			var js = fs.readFileSync(p);

			if (name == 'widget') { 
				state.globals.push(symbol);
			} 

			return js;
		} else {
			return '';
		}
	}

	function findWidget(id)
	{
		var files = fs.readdirSync(widgetsDir);
		for (var c=0;c<files.length;c++)
		{
			var dirname = files[c];
			var f = path.join(widgetsDir,dirname,'widget.json');
			if (path.existsSync(f))
			{
				var json = fs.readFileSync(f);
				var manifest = JSON.parse(json);
				if (manifest.id == id)
				{
					return [manifest,path.join(widgetsDir,dirname)];
				}
			}
		}
		return null;
	}

	function generateUniqueId() {
		return alloyUniqueIdPrefix + alloyUniqueIdCounter++;
	};

	function generateNode(ischild,viewFile,node,state,defId)
	{
		if (node.nodeType != 1) return '';

		var code = '';
		var id = node.getAttribute('id') || defId || generateUniqueId();
		var symbol = generateVarName(id);
		var nodename = node.nodeName;
		var classes = node.getAttribute('class').split(' ');

		switch(nodename)
		{
			case 'View':
			{
				// do a full import if a require tag is used
				//var req = node.getAttribute('require');
				//if (req)
				//{
					//parseView(req,state,null,id);
					//return;
				//}
				break;
			}
			case 'Widget':
			{
				// if we're not inside a widget file, then attempt to pull it in
				if (viewFile.indexOf('widget.xml')==-1)
				{
					var req = node.getAttribute('require');
					if (!req)
					{
						die("Widget at '"+viewFile+"' doesn't specify a 'require' attribute which is required");
					}
					var widget = findWidget(req);
					parseView('widget',state,widget[1],id);
					return;
				}
				else
				{
					// a widget should actually get turned into an empty view container
					nodename = 'View';
					break;
				}
			}
		}

		// if (id !== undefined && ids[id])
		// {
		// 	die("<"+nodename+"> from '"+viewFile+"' attempted to use the id '"+id+"' which has already been defined in the view '"+ids[id]+"'");
		// }

		// ids[id]=viewFile;

		var ns = node.getAttribute('ns') || "Ti.UI";
		var fn = "create" + nodename;
		
		
		if (node.childNodes.length > 0)
		{
			var processors = 
			[
				['Label','text'],
				['Button','title']
			];
			_.every(processors, function(el)
			{
				if (nodename == el[0])
				{
					var k = el[1];
					var str = getNodeText(node);
					if (!state.styles['#'+id])
					{
						state.styles['#'+id]={};
					}
					state.styles['#'+id][k]=str;
					return false;
				} 
				return true;
			});
		}

			appendSource("}),'" + node.nodeName + "');");
			code += generateStyleParams(state.styles,classes,id,node.nodeName);
			if (state.parentNode) {
				code += "\n\t}),'" + node.nodeName + "', " + state.parentNode + ");\n\t";
				code += state.parentNode+".add("+symbol+");\n";
			} else {
				code += "}),'" + node.nodeName + "');\n";
			}
		}

		var childstate = {
			parentNode: symbol,
			globals: state.globals,
			styles: state.styles,
			models:state.models
		};

		if (id && state.parentNode!=symbol) state.globals.push(symbol);

		for (var c=0;c<node.childNodes.length;c++)
		{
			var child = node.childNodes[c];
			code += generateNode(true,viewFile,child,childstate);
		}

		return code;
	}
	
	function findModelMigrations(state,name)<commit_msg>don't process required views in markup, just reference them via commonjs modules in generated code<commit_after>/**
 * Alloy
 * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved.
 * See LICENSE for more information on licensing.
 */
var fs = require('fs'),
	path = require('path')
	program = require('commander'),
	logger = require("./common/logger"),
	U = require('./utils'),
	wrench = require("wrench"),
	colors = require("colors"),
	_ = require("./lib/alloy/underscore")._,
	DOMParser = require("xmldom").DOMParser,
	XMLSerializer = require("xmldom").XMLSerializer,
	jsp = require("./uglify-js/uglify-js").parser,
	pro = require("./uglify-js/uglify-js").uglify,
	generators = require('./generators'),
	pkginfo = require('pkginfo')(module, 'name', 'version');

//
//TODO: we need a much more robust help from command line -- see sort of what i did in titanium
//TODO: handle localization files and merging
//TODO: uglify all files not just our main app.js
//

/**
 * ACTIONS:
 *
 * - new [path] - create a new project
 * - compile - compile the project
 * - generate [type] - generate an object such as a model or controller
 */
		
program
	.version(module.exports.version)
	.description('Alloy command line')
	.usage('ACTION [ARGS] [OPTIONS]')
	.option('-o, --outputPath <outputPath>', 'Output path for generated code')
	.option('-l, --logLevel <logLevel>', 'Log level (default: 3 [DEBUG])')
	.option('-d, --dump','Dump the generated app.js to console')
	.option('-f, --force','Force the command to execute')
	.option('-n, --no-colors','Turn off colors')
	.option('-c, --config <config>','Pass in compiler configuration')
	.parse(process.argv);

var outputPath,
	// ids = {},
	compilerMakeFile,
	alloyUniqueIdPrefix = '__alloyId',
	alloyUniqueIdCounter = 0;
	
function CompilerMakeFile()
{
	var handlers = {};
	
	this.require = require;
	this.process = process;
	
	this.task = function(event, fn)
	{
		logger.debug('adding task: '+event.yellow);
		handlers[event] = fn;
	};
	
	this.trigger = function(event, config)
	{
		logger.debug("compile:trigger-> "+event.yellow);
		var fn = handlers[event];
		if (fn)
		{
			return fn(config,logger);
		}
		return null;
	};
	
	return this;
}	

function generateVarName(id)
{
	return '$.'+id;
}

function die(msg, printUsage) 
{
	printUsage = typeof printUsage === 'undefined' ? false : printUsage;
	logger.error(msg);
	if (printUsage) {
		logger.info(program.helpInformation());
	}
	process.exit(1);
}

function getNodeText(node)
{
	var str = '';
	var serializer = new XMLSerializer();
	for (var c=0;c<node.childNodes.length;c++)
	{
		if (node.childNodes[c].nodeType!=1)
		{
			str += serializer.serializeToString(node.childNodes[c]);
		}
	}
	return str;
}

function loadStyle(p)
{
	if (path.existsSync(p))
	{
		var f = fs.readFileSync(p);
		f = String(f);

		// skip empty files
		if (/^\s*$/.test(f)) {
			return {};
		}

		f = f.replace(/Titanium\./g,"Ti.");
		// fixup constants so we can use them in JSON but then we do magic conversions
		f = f.replace(/Ti\.UI\.FILL/g,'"TI_UI_FILL"');
		f = f.replace(/Ti\.UI\.SIZE/g,'"TI_UI_SIZE"');
		f = f.replace(/Ti\.UI\.TEXT_ALIGNMENT_LEFT/g,'"TI_UI_TEXT_ALIGNMENT_LEFT"')
		f = f.replace(/Ti\.UI\.TEXT_ALIGNMENT_RIGHT/g,'"TI_UI_TEXT_ALIGNMENT_RIGHT"')
		f = f.replace(/Ti\.UI\.TEXT_ALIGNMENT_CENTER/g,'"TI_UI_TEXT_ALIGNMENT_CENTER"')
		try 
		{
			return JSON.parse(f);
		}
		catch(E)
		{
			die("Error parsing style at "+p.yellow+".  Error was: "+String(E).red);
		}
	}
	return {};
}

function banner()
{
	var str = 
	"       .__  .__                \n"+
	"_____  |  | |  |   ____ ___.__.\n"+
	"\\__  \\ |  | |  |  /  _ <   |  |\n"+
	" / __ \\|  |_|  |_(  <_> )___  |\n"+
	"(____  /____/____/\\____// ____|\n"+
	"     \\/                 \\/";
	
	if (!program.dump)
	{
		console.log(logger.stripColors ? str : str.blue);
		var m = "Alloy by Appcelerator. The MVC app framework for Titanium.\n".white;
		console.log(logger.stripColors ? colors.stripColors(m) : m);
	}
}

function compile(args)
{
	var inputPath = args.length > 0 ? args[0] : resolveAppHome();

	if (!path.existsSync(inputPath)) 
	{
		die('inputPath "' + inputPath + '" does not exist');
	}	

	if (!program.outputPath)
	{
		var t = path.join(inputPath,'views','index.xml');
		if (path.existsSync(t))
		{
			outputPath = path.join(inputPath,'..');
		}
	}
	outputPath = outputPath ? outputPath : (program.outputPath || path.join(resolveAppHome(),".."));
	U.ensureDir(outputPath);
	
	if (program.config)
	{
		var pc = {};
		_.each(program.config.split(','),function(v)
		{
			var a = v.split('=');
			pc[a[0]]=a[1];
		});
		program.config = pc;
	}
	
	var alloyConfig = {};
	var alloyCF = path.join(inputPath,'alloy.json');
	if (path.existsSync(alloyCF))
	{
		var c = fs.readFileSync(alloyCF);
		alloyConfig = JSON.parse(c);
		logger.info("found alloy configuration at "+alloyCF.yellow);
	}

	var viewsDir = path.join(inputPath,'views');
	if (!path.existsSync(viewsDir))
	{
		die("Couldn't find expected views directory at '"+viewsDir+"'");
	}
	var stylesDir = path.join(inputPath,'styles');
	var controllersDir = path.join(inputPath,'controllers');
	var widgetsDir = path.join(inputPath,'widgets');
	var modelsDir = path.join(inputPath,'models');
	var migrationsDir = path.join(inputPath,'migrations');
	var configDir = path.join(inputPath,'config');

	var indexView = path.join(viewsDir,"index.xml");
	if (!path.existsSync(indexView))
	{
		die("Couldn't find expected index view at '"+indexView+"'");
	}
	var resourcesDir = path.join(outputPath,"Resources");
	U.ensureDir(resourcesDir);

	var assetsDir = path.join(inputPath,'assets');

	logger.info("Generating to "+resourcesDir.yellow+" from ".cyan + inputPath.yellow);

	// setup the compiler makefile
	compilerMakeFile = new CompilerMakeFile();
	var compileConfig = 
	{
		alloyConfig: alloyConfig,
		home: path.resolve(inputPath),
		projectDir : path.resolve(outputPath),
		viewsDir: path.resolve(viewsDir),
		controllersDir: path.resolve(controllersDir),
		widgetsDir: path.resolve(widgetsDir),
		modelsDir: path.resolve(modelsDir),
		migrationsDir: path.resolve(migrationsDir),
		resourcesDir: path.resolve(resourcesDir),
		assetsDir: path.resolve(assetsDir),
		configDir:path.resolve(configDir)
	};
	
	var alloyJMK = path.resolve(path.normalize(path.join(inputPath,"alloy.jmk")));
	if (path.existsSync(alloyJMK))
	{
		logger.info("Found project specific makefile at " + "app/alloy.jmk".yellow);
		var vm = require('vm'),
			util = require('util');
		var script = vm.createScript(fs.readFileSync(alloyJMK), 'alloy.jmk');
		try
		{
			script.runInNewContext(compilerMakeFile);
		}
		catch(E)
		{
			logger.error("project build at "+alloyJMK.yellow + " generated an error during load: "+E.red);
		}
	}
	
	// trigger our custom compiler makefile
	compilerMakeFile.trigger("pre:compile",_.clone(compileConfig));

	function generateConfig()
	{
		var cf = path.join(configDir,'config.json');
		if (path.existsSync(cf))
		{
			var jf = fs.readFileSync(cf);
			var j = JSON.parse(jf);
			
			var o = j.global || {};
			if (program.config)
			{
				o = _.extend(o, j['env:'+program.config.deploytype]);
				o = _.extend(o, j['os:'+program.config.platform]);
			}
			return "$.CFG = " + JSON.stringify(o) + ";";
		}
		return '';
	}

	function generateSourceCode()
	{
		var appJS = path.join(resourcesDir,"app.js");
		
		if (!program.config)
		{
			program.config = {
				deploytype:"development"
			};
		}
		
		var DEFINES = 
		{
			OS_IOS : program.config.platform == 'ios',
			OS_ANDROID: program.config.platform == 'android',
			OS_MOBILEWEB: program.config.platform == 'mobileweb',
			ENV_DEV: program.config.deploytype == 'development',
			ENV_DEVELOPMENT: program.config.deploytype == 'development',
			ENV_TEST: program.config.deploytype == 'test',
			ENV_PRODUCTION: program.config.deploytype == 'production'
		};
		
		var defines = {};
		for (var k in DEFINES)
		{
			defines[k] = [ "num", DEFINES[k] ? 1 : 0 ];
		}
		
		var cfg = generateConfig();
		var code = _.template(fs.readFileSync(path.join(outputPath,'app','template','app.js'),'utf8'),{config:cfg});
		var beautify = alloyConfig.compiler && typeof alloyConfig.compiler.beautify !== 'undefined' ? alloyConfig.compiler.beautify : program.config.deploytype === 'development' ? true : false;

		var ast = jsp.parse(code); // parse code and get the initial AST
		ast = pro.ast_mangle(ast,{except:['Ti','Titanium'],defines:defines}); // get a new AST with mangled names
		ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
		var final_code = pro.gen_code(ast,{beautify:beautify}); 

		// trigger our custom compiler makefile
		var njs = compilerMakeFile.trigger("compile:app.js",_.extend(_.clone(compileConfig), {"code":final_code, "appJSFile" : path.resolve(appJS)}));
		if (njs)
		{
			final_code = njs;
		}
		
		fs.writeFileSync(appJS,final_code);
		logger.info("compiling alloy to " + appJS.yellow);

		if (program.dump) console.log(final_code.blue);
	}

	function copyAssets()
	{
		if (path.existsSync(assetsDir))
		{
			logger.info('Copying assets from: '+assetsDir.yellow);
			U.copyFilesAndDirs(assetsDir,resourcesDir);
		}
	}

	function copyLibs()
	{
		var lib = path.join(inputPath,'lib');
		if (path.existsSync(lib))
		{
			logger.info('Copying app libs: '+lib.yellow);
			U.copyFilesAndDirs(lib,resourcesDir);
		}
		var vendor = path.join(inputPath,'vendor');
		if (path.existsSync(vendor))
		{
			logger.info('Copying vendor libs: '+vendor.yellow);
			U.copyFilesAndDirs(vendor,path.join(resourcesDir,'vendor'));
		}
	}

	function copyAlloy()
	{
		var lib = path.join(__dirname,'lib');
		U.copyFilesAndDirs(lib,resourcesDir);
	}

	var JSON_NULL = JSON.parse('null');

	function mergeStyles(from,to)
	{
		if (from)
		{
			for (var k in from)
			{
				var v = from[k];
				// for optimization, remove null or undefined values
				if (v == JSON_NULL || typeof(v)==='undefined' || typeof(v)==='null')
				{
					delete to[k];
				}
				else
				{
					to[k] = from[k];
				}
			}
		}
	}

	function properCase (n)
	{
		return n.charAt(0).toUpperCase() + n.substring(1);
	}

	function generateStyleParams(styles,classes,id,className)
	{
		var s = {};
		mergeStyles(styles['View'],s);
		mergeStyles(styles[properCase(className)],s);
		for (var c=0;c<classes.length;c++)
		{
			var clsn = classes[c];
			mergeStyles(styles['.'+clsn],s);
		}
		mergeStyles(styles['#'+id],s);
		if (id) s['id'] = id;
		var str = [];
		
		var constants = {
			'TI_UI_FILL':'TFL$',
			'TI_UI_SIZE':'TSZ$',
			'TI_UI_TEXT_ALIGNMENT_LEFT':'Ti.UI.TEXT_ALIGNMENT_LEFT',
			'TI_UI_TEXT_ALIGNMENT_CENTER':'Ti.UI.TEXT_ALIGNMENT_CENTER',
			'TI_UI_TEXT_ALIGNMENT_RIGHT':'Ti.UI.TEXT_ALIGNMENT_RIGHT'
		};
		
		for (var sn in s)
		{
			var v = s[sn];
			var q = typeof(v) === 'string';
			var cf = constants[v];
			if (cf) {
				v = cf;
				q = false;
			} else if (q) {
				str.push("\t\t"+sn+':'+'"'+v+'"');
			} else {
				str.push("\t\t"+sn+':'+ JSON.stringify(v));
			}
		}
		return str.join(",\n");
	}

	function generateController(name, parameters, dir, state, id)
	{
		var code = '';
		var cd = dir ? path.join(dir,'controllers') : controllersDir;
		var p = path.join(cd,name+'.js');
		var symbol = generateVarName(id);
		
		if (path.existsSync(p)) {
			var js = fs.readFileSync(p);

			if (name == 'widget') { 
				state.globals.push(symbol);
			} 

			return js;
		} else {
			return '';
		}
	}

	function findWidget(id)
	{
		var files = fs.readdirSync(widgetsDir);
		for (var c=0;c<files.length;c++)
		{
			var dirname = files[c];
			var f = path.join(widgetsDir,dirname,'widget.json');
			if (path.existsSync(f))
			{
				var json = fs.readFileSync(f);
				var manifest = JSON.parse(json);
				if (manifest.id == id)
				{
					return [manifest,path.join(widgetsDir,dirname)];
				}
			}
		}
		return null;
	}

	function generateUniqueId() {
		return alloyUniqueIdPrefix + alloyUniqueIdCounter++;
	};

	function generateNode(ischild,viewFile,node,state,defId)
	{
		if (node.nodeType != 1) return '';

		var code = '';
		var id = node.getAttribute('id') || defId || generateUniqueId();
		var symbol = generateVarName(id);
		var nodename = node.nodeName;
		var classes = node.getAttribute('class').split(' ');
		var req = node.getAttribute('require');

		switch(nodename)
		{
			case 'View':
			{
				// do a full import if a require tag is used
				//var req = node.getAttribute('require');
				//if (req)
				//{
					//parseView(req,state,null,id);
					//return;
				//}
				break;
			}
			case 'Widget':
			{
				// if we're not inside a widget file, then attempt to pull it in
				if (viewFile.indexOf('widget.xml')==-1)
				{
					var req = node.getAttribute('require');
					if (!req)
					{
						die("Widget at '"+viewFile+"' doesn't specify a 'require' attribute which is required");
					}
					var widget = findWidget(req);
					parseView('widget',state,widget[1],id);
					return;
				}
				else
				{
					// a widget should actually get turned into an empty view container
					nodename = 'View';
					break;
				}
			}
		}

		// if (id !== undefined && ids[id])
		// {
		// 	die("<"+nodename+"> from '"+viewFile+"' attempted to use the id '"+id+"' which has already been defined in the view '"+ids[id]+"'");
		// }

		// ids[id]=viewFile;

		if (req) {
			code += symbol + " = (require('alloy/components/" + id + "')).create();\n"
		} else {
			var ns = node.getAttribute('ns') || "Ti.UI";
			var fn = "create" + nodename;
			
			
			if (node.childNodes.length > 0)
			{
				var processors = 
				[
					['Label','text'],
					['Button','title']
				];
				_.every(processors, function(el)
				{
					if (nodename == el[0])
					{
						var k = el[1];
						var str = getNodeText(node);
						if (!state.styles['#'+id])
						{
							state.styles['#'+id]={};
						}
						state.styles['#'+id][k]=str;
						return false;
					} 
					return true;
				});
			}

			code += '\t' + symbol + " = A$(" + ns + "." + fn + "({\n";
			code += generateStyleParams(state.styles,classes,id,node.nodeName);
			if (state.parentNode) {
				code += "\n\t}),'" + node.nodeName + "', " + state.parentNode + ");\n\t";
				code += state.parentNode+".add("+symbol+");\n";
			} else {
				code += "}),'" + node.nodeName + "');\n";
			}
		}

		var childstate = {
			parentNode: symbol,
			globals: state.globals,
			styles: state.styles,
			models:state.models
		};

		if (id && state.parentNode!=symbol) state.globals.push(symbol);

		for (var c=0;c<node.childNodes.length;c++)
		{
			var child = node.childNodes[c];
			code += generateNode(true,viewFile,child,childstate);
		}

		return code;
	}
	
	function findModelMigrations(state,name)
{'fraction_non_alphanumeric': 0.17681179435831923, 'fraction_numerical': 0.001864447743672962, 'mean_word_length': 3.463553706272153, 'pattern_counts': {'":': 2, '<': 23, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 10}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '175112', 'n_tokens_mistral': 11880, 'n_tokens_neox': 10322, 'n_words': 2083}