Explorar o código

Merge pull request #799 from mkroehnert/compiler

Amberc: update to use ES6 Promises
Nicolas Petton %!s(int64=10) %!d(string=hai) anos
pai
achega
efcb7c6b3c
Modificáronse 4 ficheiros con 332 adicións e 396 borrados
  1. 1 1
      cli/support/amberc-cli.js
  2. 327 394
      cli/support/amberc.js
  3. 1 1
      grunt/tasks/grunt-amberc.js
  4. 3 0
      package.json

+ 1 - 1
cli/support/amberc-cli.js

@@ -32,7 +32,7 @@ compiler.main(configuration);
 function handle_options(optionsArray) {
 	var programName = [];
 	var currentItem = optionsArray.shift();
-	var defaults = amberc.createDefaults();
+	var defaults = amberc.createDefaultConfiguration();
 
 	while(undefined !== currentItem) {
 		switch(currentItem) {

+ 327 - 394
cli/support/amberc.js

@@ -6,43 +6,7 @@
  *     var options = amberc.createDefaults();
  *     // edit options entries
  *     compiler.main(options);
- *
- * Execute 'node compiler.js' without arguments or with -h / --help for help.
- */
-
-/**
- * Map the async filter function onto array and evaluate callback, once all have finished.
- * Taken from: http://howtonode.org/control-flow-part-iii
  */
-function async_map(array, filter, callback) {
-	if (0 === array.length) {
-		callback(null, null);
-		return;
-	}
-	var counter = array.length;
-	var new_array = [];
-	array.forEach(function (item, index) {
-		filter(item, function (err, result) {
-			if (err) { callback(err); return; }
-			new_array[index] = result;
-			counter--;
-			if (counter === 0) {
-				callback(null, new_array);
-			}
-		});
-	});
-}
-
-
-/**
- * Always evaluates the callback parameter.
- * Used by Combo blocks to always call the next function,
- * even if all of the other functions did not run.
- */
-function always_resolve(callback) {
-	callback();
-}
-
 
 /**
  * Helper for concatenating Amber generated AMD modules.
@@ -92,38 +56,10 @@ function createConcatenator () {
 	};
 }
 
-/**
- * Combine several async functions and evaluate callback once all of them have finished.
- * Taken from: http://howtonode.org/control-flow
- */
-function Combo(callback) {
-	this.callback = callback;
-	this.items = 0;
-	this.results = [];
-}
-
-Combo.prototype = {
-	add: function () {
-		var self = this,
-		id = this.items;
-		this.items++;
-		return function () {
-			self.check(id, arguments);
-		};
-	},
-	check: function (id, theArguments) {
-		this.results[id] = Array.prototype.slice.call(theArguments);
-		this.items--;
-		if (this.items === 0) {
-			this.callback.apply(this, this.results);
-		}
-	}
-};
 
 var path = require('path'),
-	util = require('util'),
 	fs = require('fs'),
-	exec = require('child_process').exec;
+	Promise = require('es6-promise').Promise;
 
 /**
  * AmberC constructor function.
@@ -146,7 +82,7 @@ function AmberC(amber_dir) {
 /**
  * Default values.
  */
-var createDefaults = function(finished_callback){
+var createDefaultConfiguration = function() {
 	return {
 		'load': [],
 		'main': undefined,
@@ -164,23 +100,18 @@ var createDefaults = function(finished_callback){
 		'compiled': [],
 		'program': undefined,
 		'output_dir': undefined,
-		'verbose': false,
-		'finished_callback': finished_callback
+		'verbose': false
 	};
 };
 
 
 /**
  * Main function for executing the compiler.
- * If check_configuration_ok() returns successfully the configuration is set on the current compiler
- * instance and check_for_closure_compiler() gets called.
- * The last step is to call collect_files().
+ * If check_configuration_ok() returns successfully
+ * the configuration is used to trigger the following compilation steps.
  */
 AmberC.prototype.main = function(configuration, finished_callback) {
 	console.time('Compile Time');
-	if (undefined !== finished_callback) {
-		configuration.finished_callback = finished_callback;
-	}
 
 	if (configuration.amd_namespace.length === 0) {
 		configuration.amd_namespace = 'amber_core';
@@ -196,115 +127,106 @@ AmberC.prototype.main = function(configuration, finished_callback) {
 		console.log = function() {};
 	}
 
-	if (this.check_configuration_ok(configuration)) {
-		this.defaults = configuration;
-		this.defaults.smalltalk = {}; // the evaluated compiler will be stored in this variable (see create_compiler)
-		this.collect_files(this.defaults.stFiles, this.defaults.jsFiles)
-	}
+	// the evaluated compiler will be stored in this variable (see create_compiler)
+	configuration.smalltalk = {};
+	configuration.kernel_libraries = this.kernel_libraries;
+	configuration.compiler_libraries = this.compiler_libraries;
+	configuration.amber_dir = this.amber_dir;
+
+	function logError(error) {
+		console.log(error);
+		finished_callback();
+	};
+
+	check_configuration(configuration)
+	.then(collect_st_files, logError)
+	.then(collect_js_files, logError)
+	.then(resolve_kernel, logError)
+	.then(create_compiler, logError)
+	.then(compile, logError)
+	.then(category_export, logError)
+	.then(verify, logError)
+	.then(compose_js_files, logError)
+	.then(function() {
+		console.log = console.ambercLog;
+		console.timeEnd('Compile Time');
+		finished_callback();
+	});
 };
 
 
 /**
- * Check if the passed in configuration object has sufficient/nonconflicting values
+ * Check if the passed in configuration object has sufficient/nonconflicting values.
+ * Returns a Promise which resolves into the configuration object.
  */
-AmberC.prototype.check_configuration_ok = function(configuration) {
-	if (undefined === configuration) {
-		throw new Error('AmberC.check_configuration_ok(): missing configuration object');
-	}
+function check_configuration(configuration) {
+	return new Promise(function(resolve, reject) {
+		if (undefined === configuration) {
+			reject(Error('AmberC.check_configuration_ok(): missing configuration object'));
+		}
 
-	if (0 === configuration.jsFiles.length && 0 === configuration.stFiles.length) {
-		throw new Error('AmberC.check_configuration_ok(): no files to compile/link specified in configuration object');
-	}
-	return true;
+		if (0 === configuration.jsFiles.length && 0 === configuration.stFiles.length) {
+			reject(Error('AmberC.check_configuration_ok(): no files to compile/link specified in configuration object'));
+		}
+
+		resolve(configuration);
+	});
 };
 
 
 /**
  * Check if the file given as parameter exists in any of the following directories:
  *  1. current local directory
- *  2. defauls.jsLibraryDirs
+ *  2. configuration.jsLibraryDirs
  *  3. $AMBER/js/
  *  3. $AMBER/support/
  *
  * @param filename name of a file without '.js' prefix
- * @param callback gets called on success with path to .js file as parameter
+ * @param configuration the main amberc configuration object
  */
-AmberC.prototype.resolve_js = function(filename, callback) {
+function resolve_js(filename, configuration) {
 	var baseName = path.basename(filename, '.js');
-	var jsFile = baseName + this.defaults.loadsuffix + '.js';
-	var defaults = this.defaults;
-	console.log('Resolving: ' + jsFile);
-	fs.exists(jsFile, function(exists) {
-		if (exists) {
-			callback(jsFile);
-		} else {
-			var amberJsFile = '';
-			// check for specified .js file in any of the directories from jsLibraryDirs
-			var found = defaults.jsLibraryDirs.some(function(directory) {
-				amberJsFile = path.join(directory, jsFile);
-				return fs.existsSync(amberJsFile);
-			});
-			if (found) {
-				callback(amberJsFile);
-			} else {
-				throw(new Error('JavaScript file not found: ' + jsFile));
-			}
-		}
-	});
+	var jsFile = baseName + configuration.loadsuffix + '.js';
+	return resolve_file(jsFile, configuration.jsLibraryDirs);
 };
 
 
 /**
- * Collect libraries and Smalltalk files looking
- * both locally and in $AMBER/js and $AMBER/st.
- * Followed by resolve_libraries().
+ * Check if the file given as parameter exists in any of the following directories:
+ *  1. current local directory
+ *  2. $AMBER/
+ *
+ * @param filename name of a .st file
+ * @param configuration the main amberc configuration object
  */
-AmberC.prototype.collect_files = function(stFiles, jsFiles) {
-	var self = this;
-	var collected_files = new Combo(function() {
-		self.resolve_libraries();
-	});
-	if (0 !== stFiles.length) {
-		self.collect_st_files(stFiles, collected_files.add());
-	}
-	if (0 !== jsFiles.length) {
-		self.collect_js_files(jsFiles, collected_files.add());
-	}
+function resolve_st(filename, configuration) {
+	return resolve_file(filename, [configuration.amber_dir]);
 };
 
 
 /**
- * Resolve st files given by stFiles and add them to defaults.compile.
- * Respective categories get added to defaults.compile_categories.
- * callback is evaluated afterwards.
+ * Resolve the location of a file given as parameter filename.
+ * First check if the file exists at given location,
+ * then check in each of the directories specified in parameter searchDirectories.
  */
-AmberC.prototype.collect_st_files = function(stFiles, callback) {
-	var defaults = this.defaults;
-	var self = this;
-	var collected_st_files = new Combo(function() {
-		Array.prototype.slice.call(arguments).forEach(function(data) {
-			var stFile = data[0];
-			defaults.compile.push(stFile);
-		});
-		callback();
-	});
-
-	stFiles.forEach(function(stFile) {
-		var _callback = collected_st_files.add();
-		console.log('Checking: ' + stFile);
-		var amberStFile = path.join(self.amber_dir, 'st', stFile);
-		fs.exists(stFile, function(exists) {
+function resolve_file(filename, searchDirectories) {
+	return new Promise(function(resolve, reject) {
+		console.log('Resolving: ' + filename);
+		fs.exists(filename, function(exists) {
 			if (exists) {
-				_callback(stFile);
+				resolve(filename);
 			} else {
-				console.log('Checking: ' + amberStFile);
-				fs.exists(amberStFile, function(exists) {
-					if (exists) {
-						_callback(amberStFile);
-					} else {
-						throw(new Error('Smalltalk file not found: ' + amberStFile));
-					}
+				var alternativeFile = '';
+				// check for filename in any of the given searchDirectories
+				var found = searchDirectories.some(function(directory) {
+					alternativeFile = path.join(directory, filename);
+					return fs.existsSync(alternativeFile);
 				});
+				if (found) {
+					resolve(alternativeFile);
+				} else {
+					reject(Error('File not found: ' + alternativeFile));
+				}
 			}
 		});
 	});
@@ -312,230 +234,240 @@ AmberC.prototype.collect_st_files = function(stFiles, callback) {
 
 
 /**
- * Resolve js files given by jsFiles and add them to defaults.libraries.
- * callback is evaluated afterwards.
+ * Resolve st files given by stFiles and add them to configuration.compile.
+ * Returns a Promise which resolves into the configuration object.
  */
-AmberC.prototype.collect_js_files = function(jsFiles, callback) {
-	var self = this;
-	var collected_js_files = new Combo(function() {
-		Array.prototype.slice.call(arguments).forEach(function(file) {
-			self.defaults.libraries.push(file[0]);
+function collect_st_files(configuration) {
+	return new Promise(function(resolve, reject) {
+		Promise.all(
+			configuration.stFiles.map(function(stFile) {
+				return resolve_st(stFile, configuration);
+			})
+		).then(function(data) {
+			configuration.compile = configuration.compile.concat(data);
+			resolve(configuration);
+		}, function(error) {
+			reject(error);
 		});
-		callback();
-	});
-
-	jsFiles.forEach(function(jsFile) {
-		self.resolve_js(jsFile, collected_js_files.add());
-	});
-};
-
-
-/**
- * Resolve kernel and compiler files.
- * Followed by resolve_init().
- */
-AmberC.prototype.resolve_libraries = function() {
-	// Resolve libraries listed in this.kernel_libraries
-	var self = this;
-	var all_resolved = new Combo(function(resolved_kernel_files, resolved_compiler_files) {
-		self.create_compiler(resolved_compiler_files[0]);
 	});
-	this.resolve_kernel(all_resolved.add());
-	this.resolve_compiler(all_resolved.add());
 };
 
 
 /**
- * Resolve .js files needed by kernel
- * callback is evaluated afterwards.
+ * Resolve js files given by jsFiles and add them to configuration.libraries.
+ * Returns a Promise which resolves into the configuration object.
  */
-AmberC.prototype.resolve_kernel = function(callback) {
-	var self = this;
-	var kernel_files = this.kernel_libraries.concat(this.defaults.load);
-	var kernel_resolved = new Combo(function() {
-		var foundLibraries = [];
-		Array.prototype.slice.call(arguments).forEach(function(file) {
-			if (undefined !== file[0]) {
-				foundLibraries.push(file[0]);
-			}
+function collect_js_files(configuration) {
+	return new Promise(function(resolve, reject) {
+		Promise.all(
+			configuration.jsFiles.map(function(file) {
+				return resolve_js(file, configuration);
+			})
+		).then(function(data) {
+			configuration.libraries = configuration.libraries.concat(data);
+			resolve(configuration);
+		}, function(error) {
+			reject(error);
 		});
-		// boot.js and Kernel files need to be used first
-		// otherwise the global smalltalk object is undefined
-		self.defaults.libraries = foundLibraries.concat(self.defaults.libraries);
-		callback(null);
 	});
-
-	kernel_files.forEach(function(file) {
-		self.resolve_js(file, kernel_resolved.add());
-	});
-
-	always_resolve(kernel_resolved.add());
 };
 
 
 /**
- * Resolve .js files needed by compiler.
- * callback is evaluated afterwards with resolved files as argument.
+ * Resolve .js files needed by kernel.
+ * Returns a Promise which resolves into the configuration object.
  */
-AmberC.prototype.resolve_compiler = function(callback) {
-	// Resolve compiler libraries
-	var compiler_files = this.compiler_libraries.concat(this.defaults.load);
-	var compiler_resolved = new Combo(function() {
-		var compilerFiles = [];
-		Array.prototype.slice.call(arguments).forEach(function(file) {
-			if (undefined !== file[0]) {
-				compilerFiles.push(file[0]);
-			}
+function resolve_kernel(configuration) {
+	var kernel_files = configuration.kernel_libraries.concat(configuration.load);
+	return new Promise(function(resolve, reject) {
+		Promise.all(
+			kernel_files.map(function(file) {
+				return resolve_js(file, configuration, resolve);
+			})
+		).then(function(data) {
+			// boot.js and Kernel files need to be used first
+			// otherwise the global smalltalk object is undefined
+			configuration.libraries = data.concat(configuration.libraries);
+			resolve(configuration);
+		}, function(error) {
+			reject(error);
 		});
-		callback(compilerFiles);
-	});
-	var self = this;
-	compiler_files.forEach(function(file) {
-		self.resolve_js(file, compiler_resolved.add());
 	});
-
-	always_resolve(compiler_resolved.add());
 };
 
 
 /**
- * Read all .js files needed by compiler and eval() them.
- * The finished Compiler gets stored in defaults.smalltalk.
- * Followed by compile().
+ * Resolve .js files needed by compiler, read and eval() them.
+ * The finished Compiler gets stored in configuration.smalltalk.
+ * Returns a Promise object which resolves into the configuration object.
  */
-AmberC.prototype.create_compiler = function(compilerFilesArray) {
-	var self = this;
-	var compiler_files = new Combo(function() {
-		var builder = createConcatenator();
-		builder.add('(function() {');
-		builder.start();
+function create_compiler(configuration) {
+	return new Promise(function(resolve, reject) {
+		var compiler_files = configuration.compiler_libraries.concat(configuration.load);
+		Promise.all(
+			compiler_files.map(function(file) {
+				return resolve_js(file, configuration, resolve);
+			})
+		)
+		.then(function(compilerFilesArray) {
+			return Promise.all(
+				compilerFilesArray.map(function(file) {
+					return new Promise(function(resolve, reject) {
+						console.log('Loading file: ' + file);
+						fs.readFile(file, function(err, data) {
+							if (err)
+								reject(err);
+							else
+								resolve(data);
+						});
+					});
+				})
+			)
+		}).then(function(files) {
+			var builder = createConcatenator();
+			builder.add('(function() {');
+			builder.start();
+
+			files.forEach(function(data) {
+				// data is an array where index 0 is the error code and index 1 contains the data
+				builder.add(data);
+				// matches and returns the "module_id" string in the AMD definition: define("module_id", ...)
+				var match = ('' + data).match(/^define\("([^"]*)"/);
+				if (match) {
+					builder.addId(match[1]);
+				}
+			});
+			// store the generated smalltalk env in configuration.smalltalk
+			builder.finish('configuration.smalltalk = smalltalk;');
+			builder.add('})();');
 
-		Array.prototype.slice.call(arguments).forEach(function(data) {
-			// data is an array where index 0 is the error code and index 1 contains the data
-			builder.add(data[1]);
-			// matches and returns the "module_id" string in the AMD definition: define("module_id", ...)
-			var match = ('' + data[1]).match(/^define\("([^"]*)"/);
-			if (match) {
-				builder.addId(match[1]);
-			}
-		});
-		// store the generated smalltalk env in self.defaults.smalltalk
-		builder.finish('self.defaults.smalltalk = smalltalk;');
-		builder.add('})();');
-		eval(builder.toString());
-		console.log('Compiler loaded');
-		self.defaults.smalltalk.ErrorHandler._setCurrent_(self.defaults.smalltalk.RethrowErrorHandler._new());
-
-		if(0 !== self.defaults.jsGlobals.length) {
-			var jsGlobalVariables = self.defaults.smalltalk.globalJsVariables;
-			jsGlobalVariables.push.apply(jsGlobalVariables, self.defaults.jsGlobals);
-		}
+			eval(builder.toString());
+			console.log('Compiler loaded');
+			configuration.smalltalk.ErrorHandler._setCurrent_(configuration.smalltalk.RethrowErrorHandler._new());
 
-		self.compile();
-	});
+			if(0 !== configuration.jsGlobals.length) {
+				var jsGlobalVariables = configuration.smalltalk.globalJsVariables;
+				jsGlobalVariables.push.apply(jsGlobalVariables, configuration.jsGlobals);
+			}
 
-	compilerFilesArray.forEach(function(file) {
-		console.log('Loading file: ' + file);
-		fs.readFile(file, compiler_files.add());
+			resolve(configuration);
+		}, function(error) {
+			reject(Error('Error creating compiler'));
+		});
 	});
 };
 
 
 /**
  * Compile all given .st files by importing them.
- * Followed by category_export().
+ * Returns a Promise object that resolves into the configuration object.
  */
-AmberC.prototype.compile = function() {
-	console.log('Compiling collected .st files');
-	// import .st files
-	var self = this;
-	var imports = new Combo(function() {
-		Array.prototype.slice.call(arguments).forEach(function(code) {
-			if (undefined !== code[0]) {
-				// get element 0 of code since all return values are stored inside an array by Combo
-                var importer = self.defaults.smalltalk.Importer._new();
-                try {
-                    importer._import_(code[0]._stream());
-                } catch (ex) {
-                    throw new Error("Import error in section:\n" +
-                        importer._lastSection() +
-                        "\n\n" +
-                        "while processing chunk:\n" +
-                        importer._lastChunk() +
-                        "\n\n" +
-                        (ex._messageText && ex._messageText() || ex.message || ex));
-                }
-			}
+function compile(configuration) {
+	// return function which does the actual work
+	// and use the compile function to reference the configuration object
+	return new Promise(function(resolve, reject) {
+		Promise.all(
+			configuration.compile.map(function(stFile) {
+				return new Promise(function(resolve, reject) {
+					if (/\.st/.test(stFile)) {
+						console.ambercLog('Importing: ' + stFile);
+						fs.readFile(stFile, 'utf8', function(err, data) {
+							if (!err)
+								resolve(data);
+							else
+								reject(Error('Could not import: ' + stFile));
+						});
+					}
+				});
+			})
+		)
+		.then(function(fileContents) {
+			console.log('Compiling collected .st files');
+			// import/compile content of .st files
+			Promise.all(
+				fileContents.map(function(code) {
+					return new Promise(function(resolve, reject) {
+						var importer = configuration.smalltalk.Importer._new();
+						try {
+							importer._import_(code._stream());
+							resolve(true);
+						} catch (ex) {
+							reject(Error("Import error in section:\n" +
+								importer._lastSection() + "\n\n" +
+								"while processing chunk:\n" +
+								importer._lastChunk() + "\n\n" +
+								(ex._messageText && ex._messageText() || ex.message || ex))
+							);
+						}
+					});
+				})
+			);
+		})
+		.then(function() {
+			resolve(configuration);
 		});
-		self.category_export();
-	});
-
-	this.defaults.compile.forEach(function(stFile) {
-		var callback = imports.add();
-		if (/\.st/.test(stFile)) {
-			console.ambercLog('Importing: ' + stFile);
-			fs.readFile(stFile, 'utf8', function(err, data) {
-				if (!err)
-					callback(data);
-				else
-					throw new Error('Could not import: ' + stFile);
-			});
-		}
 	});
-	always_resolve(imports.add());
 };
 
 
 /**
  * Export compiled categories to JavaScript files.
- * Followed by verify().
+ * Returns a Promise() that resolves into the configuration object.
  */
-AmberC.prototype.category_export = function() {
-	var defaults = this.defaults;
-	var self = this;
-	// export categories as .js
-	async_map(defaults.compile, function(stFile, callback) {
-		var category = path.basename(stFile, '.st');
-		var jsFilePath = defaults.output_dir;
-		if (undefined === jsFilePath) {
-			jsFilePath = path.dirname(stFile);
-		}
-		var jsFile = category + defaults.suffix_used + '.js';
-		jsFile = path.join(jsFilePath, jsFile);
-		defaults.compiled.push(jsFile);
-		var smalltalk = defaults.smalltalk;
-		var packageObject = smalltalk.Package._named_(category);
-		packageObject._transport()._namespace_(defaults.amd_namespace);
-		fs.writeFile(jsFile, smalltalk.String._streamContents_(function (stream) {
-			smalltalk.AmdExporter._new()._exportPackage_on_(packageObject, stream); }), function(err) {
-				callback(null, null);
-			});
-	}, function(err, result){
-		self.verify();
+function category_export(configuration) {
+	return new Promise(function(resolve, reject) {
+		Promise.all(
+			configuration.compile.map(function(stFile) {
+				return new Promise(function(resolve, reject) {
+					var category = path.basename(stFile, '.st');
+					var jsFilePath = configuration.output_dir;
+					if (undefined === jsFilePath) {
+						jsFilePath = path.dirname(stFile);
+					}
+					var jsFile = category + configuration.suffix_used + '.js';
+					jsFile = path.join(jsFilePath, jsFile);
+					configuration.compiled.push(jsFile);
+					var smalltalk = configuration.smalltalk;
+					var packageObject = smalltalk.Package._named_(category);
+					packageObject._transport()._namespace_(configuration.amd_namespace);
+					fs.writeFile(jsFile, smalltalk.String._streamContents_(function (stream) {
+						smalltalk.AmdExporter._new()._exportPackage_on_(packageObject, stream);
+					}), function(err) {
+						if (err)
+							reject(err);
+						else
+							resolve(true);
+					});
+				});
+			})
+		).then(function() {
+			resolve(configuration);
+		});
 	});
 };
 
 
 /**
  * Verify if all .st files have been compiled.
- * Followed by compose_js_files().
+ * Returns a Promise() that resolves into the configuration object.
  */
-AmberC.prototype.verify = function() {
+function verify(configuration) {
 	console.log('Verifying if all .st files were compiled');
-	var self = this;
-	// copy array
-	var compiledFiles = this.defaults.compiled.slice(0);
-
-	async_map(compiledFiles,
-		function(file, callback) {
-			fs.exists(file, function(exists) {
-				if (exists)
-					callback(null, null);
-				else
-					throw(new Error('Compilation failed of: ' + file));
-			});
-		}, function(err, result) {
-			self.compose_js_files();
+	return new Promise(function(resolve, reject) {
+		Promise.all(
+			configuration.compiled.map(function(file) {
+				return new Promise(function(resolve, reject) {
+					fs.exists(file, function(exists) {
+						if (exists)
+							resolve(true);
+						else
+							reject(Error('Compilation failed of: ' + file));
+					});
+				});
+			})
+		).then(function() {
+			resolve(configuration);
+		});
 	});
 };
 
@@ -543,88 +475,89 @@ AmberC.prototype.verify = function() {
 /**
  * Synchronous function.
  * Concatenates compiled JavaScript files into one file in the correct order.
- * The name of the produced file is given by defaults.program (set by the last commandline option).
+ * The name of the produced file is given by configuration.program.
+ * Returns a Promise which resolves into the configuration object.
  */
-AmberC.prototype.compose_js_files = function() {
-	var defaults = this.defaults;
-	var programFile = defaults.program;
-	if (undefined === programFile) {
-		return;
-	}
-	if (undefined !== defaults.output_dir) {
-		programFile = path.join(defaults.output_dir, programFile);
-	}
+function compose_js_files(configuration) {
+	return new Promise(function(resolve, reject) {
+		var programFile = configuration.program;
+		if (undefined === programFile) {
+			return;
+		}
+		if (undefined !== configuration.output_dir) {
+			programFile = path.join(configuration.output_dir, programFile);
+		}
 
-	var program_files = [];
-	if (0 !== defaults.libraries.length) {
-		console.log('Collecting libraries: ' + defaults.libraries);
-		program_files.push.apply(program_files, defaults.libraries);
-	}
+		var program_files = [];
+		if (0 !== configuration.libraries.length) {
+			console.log('Collecting libraries: ' + configuration.libraries);
+			program_files.push.apply(program_files, configuration.libraries);
+		}
 
-	if (0 !== defaults.compiled.length) {
-		var compiledFiles = defaults.compiled.slice(0);
+		if (0 !== configuration.compiled.length) {
+			var compiledFiles = configuration.compiled.slice(0);
 
-		console.log('Collecting compiled files: ' + compiledFiles);
-		program_files.push.apply(program_files, compiledFiles);
-	}
+			console.log('Collecting compiled files: ' + compiledFiles);
+			program_files.push.apply(program_files, compiledFiles);
+		}
 
-	console.ambercLog('Writing program file: %s.js', programFile);
+		console.ambercLog('Writing program file: %s.js', programFile);
 
-	var fileStream = fs.createWriteStream(programFile + defaults.suffix_used + '.js');
-	fileStream.on('error', function(error) {
-		fileStream.end();
-		console.ambercLog(error);
-	});
+		var fileStream = fs.createWriteStream(programFile + configuration.suffix_used + '.js');
+		fileStream.on('error', function(error) {
+			fileStream.end();
+			console.ambercLog(error);
+		});
 
-	fileStream.on('close', function(){
-		return;
-	});
+		fileStream.on('close', function(){
+			return;
+		});
 
-	var builder = createConcatenator();
-	builder.add('#!/usr/bin/env node');
-	builder.start();
-
-	program_files.forEach(function(file) {
-		if(fs.existsSync(file)) {
-			console.log('Adding : ' + file);
-			var buffer = fs.readFileSync(file);
-			// matches and returns the "module_id" string in the AMD define: define("module_id", ...)
-			var match = buffer.toString().match(/^define\("([^"]*)"/);
-			if (match /*&& match[1].slice(0,9) !== "amber_vm/"*/) {
-				builder.addId(match[1]);
+		var builder = createConcatenator();
+		builder.add('#!/usr/bin/env node');
+		builder.start();
+
+		program_files.forEach(function(file) {
+			if(fs.existsSync(file)) {
+				console.log('Adding : ' + file);
+				var buffer = fs.readFileSync(file);
+				// matches and returns the "module_id" string in the AMD define: define("module_id", ...)
+				var match = buffer.toString().match(/^define\("([^"]*)"/);
+				if (match /*&& match[1].slice(0,9) !== "amber_vm/"*/) {
+					builder.addId(match[1]);
+				}
+				builder.add(buffer);
+			} else {
+				fileStream.end();
+				reject(Error('Can not find file ' + file));
 			}
-			builder.add(buffer);
-		} else {
-			fileStream.end();
-			throw(new Error('Can not find file ' + file));
-		}
-	});
+		});
 
-	var mainFunctionOrFile = '';
+		var mainFunctionOrFile = '';
 
-	if (undefined !== defaults.main) {
-		console.log('Adding call to: %s>>main', defaults.main);
-		mainFunctionOrFile += 'smalltalk.' + defaults.main + '._main();';
-	}
+		if (undefined !== configuration.main) {
+			console.log('Adding call to: %s>>main', configuration.main);
+			mainFunctionOrFile += 'smalltalk.' + configuration.main + '._main();';
+		}
 
-	if (undefined !== defaults.mainfile && fs.existsSync(defaults.mainfile)) {
-		console.log('Adding main file: ' + defaults.mainfile);
-		mainFunctionOrFile += '\n' + fs.readFileSync(defaults.mainfile);
-	}
+		if (undefined !== configuration.mainfile && fs.existsSync(configuration.mainfile)) {
+			console.log('Adding main file: ' + configuration.mainfile);
+			mainFunctionOrFile += '\n' + fs.readFileSync(configuration.mainfile);
+		}
 
-	builder.finish(mainFunctionOrFile);
+		builder.finish(mainFunctionOrFile);
 
-	console.log('Writing...');
-	builder.forEach(function (element) {
-		fileStream.write(element);
-		fileStream.write('\n');
+		console.log('Writing...');
+		builder.forEach(function (element) {
+			fileStream.write(element);
+			fileStream.write('\n');
+		});
+		console.log('Done.');
+		fileStream.end();
+		resolve(configuration);
 	});
-	console.log('Done.');
-	fileStream.end();
 };
 
 
 module.exports.Compiler = AmberC;
-module.exports.createDefaults = createDefaults;
-module.exports.Combo = Combo;
-module.exports.map = async_map;
+module.exports.createDefaultConfiguration = createDefaultConfiguration;

+ 1 - 1
grunt/tasks/grunt-amberc.js

@@ -75,7 +75,7 @@ module.exports = function(grunt) {
 
 
   function generateCompilerConfiguration(data, sourceFiles) {
-    var configuration = amberc.createDefaults();
+    var configuration = amberc.createDefaultConfiguration();
     var parameters = [];
 
     var libraries = data.libraries;

+ 3 - 0
package.json

@@ -32,6 +32,9 @@
   "scripts": {
     "test": "grunt amberc:amber_test_runner && node ./test/amber_test_runner.js"
   },
+  "dependencies": {
+    "es6-promise": "~0.1.1"
+  },
   "devDependencies": {
     "pegjs": "~0.7.0",
     "grunt": "~0.4.0",