start of serializing events/hooks
diff --git a/src/compile.js b/src/compile.js
index 8d9d217..f969624 100644
--- a/src/compile.js
+++ b/src/compile.js
@@ -26,7 +26,6 @@
     events            = require('./events'),
     n                 = require('ncallbacks');
 
-
 function shell_out_to_build(projectRoot, platform, callback) {
     var cmd = '"' + path.join(projectRoot, 'platforms', platform, 'cordova', 'build') + '"';
     events.emit('log', 'Compiling platform "' + platform + '" with command "' + cmd + '" (output to follow)...');
@@ -70,25 +69,26 @@
     }
 
     var hooks = new hooker(projectRoot);
-    if (!(hooks.fire('before_compile'))) {
-        var err = new Error('before_compile hooks exited with non-zero code. Aborting.');
-        if (callback) callback(err);
-        else throw err;
-        return;
-    }
+    hooks.fire('before_compile', function(err) {
+        if (err) {
+            var error = new Error('before_compile hooks exited with non-zero code or caused an error: ' + err.message);
+            if (callback) callback(error);
+            else throw error;
+        } else {
+            var end = n(platformList.length, function() {
+                hooks.fire('after_compile', function(error) {
+                    if (error) {
+                        var arr = new Error('after_compile hooks exited with non-zero code. Aborting.');
+                        if (callback) callback(arr);
+                        else throw arr;
+                    } else if (callback) callback();
+                });
+            });
 
-    var end = n(platformList.length, function() {
-        if (!(hooks.fire('after_compile'))) {
-            var err = new Error('after_compile hooks exited with non-zero code. Aborting.');
-            if (callback) callback(err);
-            else throw err;
-            return;
+            // Iterate over each added platform
+            platformList.forEach(function(platform) {
+                shell_out_to_build(projectRoot, platform, end);
+            });
         }
-        if (callback) callback();
-    });
-
-    // Iterate over each added platform
-    platformList.forEach(function(platform) {
-        shell_out_to_build(projectRoot, platform, end);
     });
 };
diff --git a/src/hooker.js b/src/hooker.js
index 0d51304..15c9a6c 100644
--- a/src/hooker.js
+++ b/src/hooker.js
@@ -29,25 +29,63 @@
 }
 
 module.exports.prototype = {
-    fire:function fire(hook) {
+    fire:function fire(hook, callback) {
+        var self = this;
         var dir = path.join(this.root, '.cordova', 'hooks', hook);
         if (!(fs.existsSync(dir))) return true; // hooks directory got axed post-create; ignore.
 
-        // Fire JS hook/event
-        events.emit(hook, this.root);
-
-        // Fire script-based hooks
-        var contents = fs.readdirSync(dir);
-        var self = this;
-        contents.forEach(function(script) {
-            var fullpath = path.join(dir, script);
-            if (fs.statSync(fullpath).isDirectory()) return; // skip directories if they're in there.
-            var command = fullpath + ' "' + self.root + '"';
-            events.emit('log', 'Executing hook "' + hook + '" (output to follow)...');
-            var status = shell.exec(command);
-            events.emit('log', status.output);
-            if (status.code !== 0) throw new Error('Script "' + path.basename(script) + '"' + 'in the ' + hook + ' hook exited with non-zero status code. Aborting.');
+        // Fire JS hook for the event
+        // These ones need to "serialize" events, that is, each handler attached to the event needs to finish processing (if it "opted in" to the callback) before the next one will fire.
+        var handlers = events.listeners(hook);
+        execute_handlers_serially(handlers, this.root, function() {
+            // Fire script-based hooks
+            var scripts = fs.readdirSync(dir);
+            execute_scripts_serially(scripts, self.root, dir, function(err) {
+                if (err) {
+                    callback(err);
+                } else {
+                    callback();
+                }
+            });
         });
-        return true;
+    }
+}
+
+function execute_scripts_serially(scripts, root, dir, callback) {
+    if (scripts.length) {
+        var s = scripts.shift();
+        var fullpath = path.join(dir, s);
+        if (fs.statSync(fullpath).isDirectory()) {
+            execute_scripts_serially(scripts, root, dir, callback); // skip directories if they're in there.
+        } else {
+            var command = fullpath + ' "' + root + '"';
+            events.emit('log', 'Executing hook "' + command + '" (output to follow)...');
+            shell.exec(command, {silent:true, async:true}, function(code, output) {
+                events.emit('log', output);
+                if (code !== 0) {
+                    callback(new Error('Script "' + fullpath + '" exited with non-zero status code. Aborting. Output: ' + output));
+                } else {
+                    execute_scripts_serially(scripts, root, dir, callback);
+                }
+            });
+        }
+    } else {
+        callback();
+    }
+}
+
+function execute_handlers_serially(handlers, root, callback) {
+    if (handlers.length) {
+        var h = handlers.shift();
+        if (h.length > 1) {
+            h(root, function() {
+                execute_handlers_serially(handlers, root, callback);
+            });
+        } else {
+            h(root);
+            execute_handlers_serially(handlers, root, callback);
+        }
+    } else {
+        callback();
     }
 }