You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by br...@apache.org on 2013/09/23 20:00:12 UTC

[3/4] Refactor to use Q.js promises in place of callbacks everywhere.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/metadata/wp7_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/wp7_parser.spec.js b/spec/metadata/wp7_parser.spec.js
index c7a74df..d7ac782 100644
--- a/spec/metadata/wp7_parser.spec.js
+++ b/spec/metadata/wp7_parser.spec.js
@@ -23,6 +23,8 @@ var platforms = require('../../platforms'),
     shell = require('shelljs'),
     fs = require('fs'),
     ET = require('elementtree'),
+    Q = require('q'),
+    child_process = require('child_process'),
     config = require('../../src/config'),
     config_parser = require('../../src/config_parser'),
     cordova = require('../../cordova');
@@ -32,14 +34,26 @@ describe('wp7 project parser', function() {
     var exists, exec, custom, readdir, cfg_parser;
     beforeEach(function() {
         exists = spyOn(fs, 'existsSync').andReturn(true);
-        exec = spyOn(shell, 'exec').andCallFake(function(cmd, opts, cb) {
-            cb(0, '');
+        exec = spyOn(child_process, 'exec').andCallFake(function(cmd, opts, cb) {
+            (cb || opts)(0, '', '');
         });
         custom = spyOn(config, 'has_custom_path').andReturn(false);
         readdir = spyOn(fs, 'readdirSync').andReturn(['test.csproj']);
         cfg_parser = spyOn(util, 'config_parser');
     });
 
+    function wrapper(p, done, post) {
+        p.then(post, function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    }
+
+    function errorWrapper(p, done, post) {
+        p.then(function() {
+            expect('this call').toBe('fail');
+        }, post).fin(done);
+    }
+
     describe('constructions', function() {
         it('should throw if provided directory does not contain a csproj file', function() {
             readdir.andReturn([]);
@@ -59,29 +73,24 @@ describe('wp7 project parser', function() {
     describe('check_requirements', function() {
         it('should fire a callback if there is an error during shelling out', function(done) {
             exec.andCallFake(function(cmd, opts, cb) {
-                cb(50, 'there was an errorz!');
+                (cb || opts)(50, 'there was an errorz!');
             });
-            platforms.wp7.parser.check_requirements(proj, function(err) {
+            errorWrapper(platforms.wp7.parser.check_requirements(proj), done, function(err) {
                 expect(err).toContain('there was an errorz!');
-                done();
             });
         });
         it('should check by calling check_reqs on the stock lib path if no custom path is defined', function(done) {
-            platforms.wp7.parser.check_requirements(proj, function(err) {
-                expect(err).toEqual(false);
+            wrapper(platforms.wp7.parser.check_requirements(proj), done, function(err) {
                 expect(exec.mostRecentCall.args[0]).toContain(util.libDirectory);
                 expect(exec.mostRecentCall.args[0]).toMatch(/check_reqs"$/);
-                done();
             });
         });
         it('should check by calling check_reqs on a custom path if it is so defined', function(done) {
             var custom_path = path.join('some','custom','path','to','wp7','lib');
             custom.andReturn(custom_path);
-            platforms.wp7.parser.check_requirements(proj, function(err) {
-                expect(err).toEqual(false);
+            wrapper(platforms.wp7.parser.check_requirements(proj), done, function() {
                 expect(exec.mostRecentCall.args[0]).toContain(custom_path);
                 expect(exec.mostRecentCall.args[0]).toMatch(/check_reqs"$/);
-                done();
             });
         });
     });
@@ -217,29 +226,32 @@ describe('wp7 project parser', function() {
                 staging = spyOn(p, 'update_staging');
                 svn = spyOn(util, 'deleteSvnFolders');
             });
-            it('should call update_from_config', function() {
-                p.update_project();
-                expect(config).toHaveBeenCalled();
+            it('should call update_from_config', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(config).toHaveBeenCalled();
+                });
             });
             it('should throw if update_from_config throws', function(done) {
                 var err = new Error('uh oh!');
                 config.andCallFake(function() { throw err; });
-                p.update_project({}, function(err) {
-                    expect(err).toEqual(err);
-                    done();
+                errorWrapper(p.update_project({}), done, function(e) {
+                    expect(e).toEqual(err);
                 });
             });
-            it('should call update_www', function() {
-                p.update_project();
-                expect(www).toHaveBeenCalled();
+            it('should call update_www', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(www).toHaveBeenCalled();
+                });
             });
-            it('should call update_staging', function() {
-                p.update_project();
-                expect(staging).toHaveBeenCalled();
+            it('should call update_staging', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(staging).toHaveBeenCalled();
+                });
             });
-            it('should call deleteSvnFolders', function() {
-                p.update_project();
-                expect(svn).toHaveBeenCalled();
+            it('should call deleteSvnFolders', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(svn).toHaveBeenCalled();
+                });
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/metadata/wp8_parser.spec.js
----------------------------------------------------------------------
diff --git a/spec/metadata/wp8_parser.spec.js b/spec/metadata/wp8_parser.spec.js
index 67a1c89..fc90d8a 100644
--- a/spec/metadata/wp8_parser.spec.js
+++ b/spec/metadata/wp8_parser.spec.js
@@ -23,6 +23,8 @@ var platforms = require('../../platforms'),
     shell = require('shelljs'),
     fs = require('fs'),
     ET = require('elementtree'),
+    Q = require('q'),
+    child_process = require('child_process'),
     config = require('../../src/config'),
     config_parser = require('../../src/config_parser'),
     cordova = require('../../cordova');
@@ -32,14 +34,26 @@ describe('wp8 project parser', function() {
     var exists, exec, custom, readdir, cfg_parser;
     beforeEach(function() {
         exists = spyOn(fs, 'existsSync').andReturn(true);
-        exec = spyOn(shell, 'exec').andCallFake(function(cmd, opts, cb) {
-            cb(0, '');
+        exec = spyOn(child_process, 'exec').andCallFake(function(cmd, opts, cb) {
+            (cb || opts)(0, '', '');
         });
         custom = spyOn(config, 'has_custom_path').andReturn(false);
         readdir = spyOn(fs, 'readdirSync').andReturn(['test.csproj']);
         cfg_parser = spyOn(util, 'config_parser');
     });
 
+    function wrapper(p, done, post) {
+        p.then(post, function(err) {
+            expect(err).toBeUndefined();
+        }).fin(done);
+    }
+
+    function errorWrapper(p, done, post) {
+        p.then(function() {
+            expect('this call').toBe('fail');
+        }, post).fin(done);
+    }
+
     describe('constructions', function() {
         it('should throw if provided directory does not contain a csproj file', function() {
             readdir.andReturn([]);
@@ -59,29 +73,24 @@ describe('wp8 project parser', function() {
     describe('check_requirements', function() {
         it('should fire a callback if there is an error during shelling out', function(done) {
             exec.andCallFake(function(cmd, opts, cb) {
-                cb(50, 'there was an errorz!');
+                (cb || opts)(50, 'there was an errorz!');
             });
-            platforms.wp8.parser.check_requirements(proj, function(err) {
+            errorWrapper(platforms.wp8.parser.check_requirements(proj), done, function(err) {
                 expect(err).toContain('there was an errorz!');
-                done();
             });
         });
         it('should check by calling check_reqs on the stock lib path if no custom path is defined', function(done) {
-            platforms.wp8.parser.check_requirements(proj, function(err) {
-                expect(err).toEqual(false);
+            wrapper(platforms.wp8.parser.check_requirements(proj), done, function() {
                 expect(exec.mostRecentCall.args[0]).toContain(util.libDirectory);
                 expect(exec.mostRecentCall.args[0]).toMatch(/check_reqs"$/);
-                done();
             });
         });
         it('should check by calling check_reqs on a custom path if it is so defined', function(done) {
             var custom_path = path.join('some','custom','path','to','wp8','lib');
             custom.andReturn(custom_path);
-            platforms.wp8.parser.check_requirements(proj, function(err) {
-                expect(err).toEqual(false);
+            wrapper(platforms.wp8.parser.check_requirements(proj), done, function(err) {
                 expect(exec.mostRecentCall.args[0]).toContain(custom_path);
                 expect(exec.mostRecentCall.args[0]).toMatch(/check_reqs"$/);
-                done();
             });
         });
     });
@@ -217,29 +226,32 @@ describe('wp8 project parser', function() {
                 staging = spyOn(p, 'update_staging');
                 svn = spyOn(util, 'deleteSvnFolders');
             });
-            it('should call update_from_config', function() {
-                p.update_project();
-                expect(config).toHaveBeenCalled();
+            it('should call update_from_config', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(config).toHaveBeenCalled();
+                });
             });
             it('should throw if update_from_config throws', function(done) {
                 var err = new Error('uh oh!');
                 config.andCallFake(function() { throw err; });
-                p.update_project({}, function(err) {
-                    expect(err).toEqual(err);
-                    done();
+                errorWrapper(p.update_project({}), done, function(e) {
+                    expect(e).toEqual(err);
                 });
             });
-            it('should call update_www', function() {
-                p.update_project();
-                expect(www).toHaveBeenCalled();
+            it('should call update_www', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(www).toHaveBeenCalled();
+                });
             });
-            it('should call update_staging', function() {
-                p.update_project();
-                expect(staging).toHaveBeenCalled();
+            it('should call update_staging', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(staging).toHaveBeenCalled();
+                });
             });
-            it('should call deleteSvnFolders', function() {
-                p.update_project();
-                expect(svn).toHaveBeenCalled();
+            it('should call deleteSvnFolders', function(done) {
+                wrapper(p.update_project(), done, function() {
+                    expect(svn).toHaveBeenCalled();
+                });
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/platform.spec.js
----------------------------------------------------------------------
diff --git a/spec/platform.spec.js b/spec/platform.spec.js
index b167b20..a4ae605 100644
--- a/spec/platform.spec.js
+++ b/spec/platform.spec.js
@@ -19,12 +19,14 @@
 var cordova = require('../cordova'),
     path = require('path'),
     shell = require('shelljs'),
+    child_process = require('child_process'),
     plugman = require('plugman'),
     fs = require('fs'),
     util = require('../src/util'),
     config = require('../src/config'),
     hooker = require('../src/hooker'),
     lazy_load = require('../src/lazy_load'),
+    Q = require('q'),
     platform = require('../src/platform'),
     platforms = require('../platforms');
 
@@ -41,10 +43,7 @@ describe('platform command', function() {
             });
         });
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
-        fire = spyOn(hooker.prototype, 'fire').andCallFake(function(e, opts, cb) {
-            if (cb === undefined) cb = opts;
-            cb(false);
-        });
+        fire = spyOn(hooker.prototype, 'fire').andReturn(Q());
         name = jasmine.createSpy('config name').andReturn('magical mystery tour');
         pkg = jasmine.createSpy('config packageName').andReturn('ca.filmaj.id');
         config_parser = spyOn(util, 'config_parser').andReturn({
@@ -55,52 +54,52 @@ describe('platform command', function() {
         list_platforms = spyOn(util, 'listPlatforms').andReturn(supported_platforms);
         util.libDirectory = path.join('HOMEDIR', '.cordova', 'lib');
         config_read = spyOn(config, 'read').andReturn({});
-        load = spyOn(lazy_load, 'based_on_config').andCallFake(function(root, platform, cb) {
-            cb();
-        });
-        load_custom = spyOn(lazy_load, 'custom').andCallFake(function(url, id, p, v, cb) {
-            cb();
-        });
+        load = spyOn(lazy_load, 'based_on_config').andReturn(Q());
+        load_custom = spyOn(lazy_load, 'custom').andReturn(Q());
         rm = spyOn(shell, 'rm');
         mkdir = spyOn(shell, 'mkdir');
         existsSync = spyOn(fs, 'existsSync').andReturn(false);
-        supports = spyOn(platform, 'supports').andCallFake(function(project_root, name, cb) {
-            cb();
-        });
-        exec = spyOn(shell, 'exec').andCallFake(function(cmd, opts, cb) {
-            if (cb) cb(0, '');
-            else return { code: 0, output: '' };
+        supports = spyOn(platform, 'supports').andReturn(Q());
+        exec = spyOn(child_process, 'exec').andCallFake(function(cmd, opts, cb) {
+            if (!cb) cb = opts;
+            cb(null, '', '');
         });
-        prep_spy = spyOn(cordova, 'prepare').andCallFake(function(t, cb) {
-            cb();
-        });
-        plugman_install = spyOn(plugman, 'install');
+        prep_spy = spyOn(cordova.raw, 'prepare').andReturn(Q());
+        plugman_install = spyOn(plugman, 'install').andReturn(Q());
     });
 
     describe('failure', function() {
-        it('should not run outside of a Cordova-based project by calling util.isCordova', function() {
+        function expectFailure(p, done, post) {
+            p.then(function() {
+                expect('this call').toBe('fail');
+            }, post).fin(done);
+        }
+
+        it('should not run outside of a Cordova-based project by calling util.isCordova', function(done) {
             is_cordova.andReturn(false);
-            expect(function() {
-                cordova.platform();
+            expectFailure(cordova.raw.platform(), done, function(err) {
                 expect(is_cordova).toHaveBeenCalled();
-            }).toThrow('Current working directory is not a Cordova-based project.');
+                expect(err).toEqual(new Error('Current working directory is not a Cordova-based project.'));
+            });
         });
-        it('should report back an error if used with `add` and no platform is specified', function() {
-            expect(function() {
-               cordova.platform('add');
-            }).toThrow('You need to qualify `add` or `remove` with one or more platforms!');
+        it('should report back an error if used with `add` and no platform is specified', function(done) {
+            expectFailure(cordova.raw.platform('add'), done, function(err) {
+                expect(err).toEqual(new Error('You need to qualify `add` or `remove` with one or more platforms!'));
+            });
         });
-        it('should report back an error if used with `rm` and no platform is specified', function() {
-            expect(function() {
-               cordova.platform('rm');
-            }).toThrow('You need to qualify `add` or `remove` with one or more platforms!');
+        it('should report back an error if used with `rm` and no platform is specified', function(done) {
+            expectFailure(cordova.raw.platform('rm'), done, function(err) {
+                expect(err).toEqual(new Error('You need to qualify `add` or `remove` with one or more platforms!'));
+            });
         });
     });
 
     describe('success', function() {
-        it('should run inside a Cordova-based project by calling util.isCordova', function() {
-            cordova.platform();
-            expect(is_cordova).toHaveBeenCalled();
+        it('should run inside a Cordova-based project by calling util.isCordova', function(done) {
+            cordova.raw.platform().then(function() {
+                expect(is_cordova).toHaveBeenCalled();
+                done();
+            });
         });
 
         describe('`ls`', function() {
@@ -113,7 +112,7 @@ describe('platform command', function() {
                     expect(res).toMatch(/^Installed platforms:\s*Available platforms:.*$/);
                     done();
                 });
-                cordova.platform('list');
+                cordova.raw.platform('list');
             });
 
             it('should list out added platforms in a project', function(done) {
@@ -121,19 +120,23 @@ describe('platform command', function() {
                     expect(res).toMatch(/^Installed platforms: ios, android, wp7, wp8, blackberry10, firefoxos\s*Available platforms:\s*$/);
                     done();
                 });
-                cordova.platform('list');
+                cordova.raw.platform('list');
             });
         });
         describe('`add`', function() {
-            it('should shell out to specified platform\'s bin/create, using the version that is specified in platforms manifest', function() {
-                cordova.platform('add', 'android');
-                expect(exec.mostRecentCall.args[0]).toMatch(/lib.android.cordova.\d.\d.\d[\d\w]*.bin.create/gi);
-                expect(exec.mostRecentCall.args[0]).toContain(project_dir);
-                cordova.platform('add', 'wp8');
-                expect(exec.mostRecentCall.args[0]).toMatch(/lib.wp.cordova.\d.\d.\d[\d\w]*.wp8.bin.create/gi);
-                expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+            it('should shell out to specified platform\'s bin/create, using the version that is specified in platforms manifest', function(done) {
+                cordova.raw.platform('add', 'android').then(function() {
+                    expect(exec.mostRecentCall.args[0]).toMatch(/lib.android.cordova.\d.\d.\d[\d\-\w]*.bin.create/gi);
+                    expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                }).then(function() {
+                    return cordova.raw.platform('add', 'wp8');
+                }).then(function() {
+                    expect(exec.mostRecentCall.args[0]).toMatch(/lib.wp.cordova.\d.\d.\d[\d\w\-]*.wp8.bin.create/gi);
+                    expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                    done();
+                });
             });
-            it('should call into lazy_load.custom if there is a user-specified configruation for consuming custom libraries', function() {
+            it('should call into lazy_load.custom if there is a user-specified configruation for consuming custom libraries', function(done) {
                 load.andCallThrough();
                 config_read.andReturn({
                     lib:{
@@ -144,12 +147,14 @@ describe('platform command', function() {
                         }
                     }
                 });
-                cordova.platform('add', 'wp8');
-                expect(load_custom).toHaveBeenCalledWith('haha', 'phonegap', 'wp8', 'bleeding edge', jasmine.any(Function));
-                expect(exec.mostRecentCall.args[0]).toMatch(/lib.wp.phonegap.bleeding edge.wp8.bin.create/gi);
-                expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                cordova.raw.platform('add', 'wp8').then(function() {
+                    expect(load_custom).toHaveBeenCalledWith('haha', 'phonegap', 'wp8', 'bleeding edge');
+                    expect(exec.mostRecentCall.args[0]).toMatch(/lib.wp.phonegap.bleeding edge.wp8.bin.create/gi);
+                    expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                    done();
+                });
             });
-            it('should use a custom template directory if there is one specified in the configuration', function() {
+            it('should use a custom template directory if there is one specified in the configuration', function(done) {
                 var template_dir = "/tmp/custom-template"
                 load.andCallThrough();
                 config_read.andReturn({
@@ -162,12 +167,14 @@ describe('platform command', function() {
                         }
                     }
                 });
-                cordova.platform('add', 'android');
-                expect(exec.mostRecentCall.args[0]).toMatch(/^"[^ ]*" +"[^"]*" +"[^"]*" +"[^"]*" +"[^"]*"$/g);
-                expect(exec.mostRecentCall.args[0]).toContain(project_dir);
-                expect(exec.mostRecentCall.args[0]).toContain(template_dir);
+                cordova.raw.platform('add', 'android').then(function() {
+                    expect(exec.mostRecentCall.args[0]).toMatch(/^"[^ ]*" +"[^"]*" +"[^"]*" +"[^"]*" +"[^"]*"$/g);
+                    expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                    expect(exec.mostRecentCall.args[0]).toContain(template_dir);
+                    done();
+                });
             });
-            it('should not use a custom template directory if there is not one specified in the configuration', function() {
+            it('should not use a custom template directory if there is not one specified in the configuration', function(done) {
                 load.andCallThrough();
                 config_read.andReturn({
                     lib: {
@@ -178,44 +185,54 @@ describe('platform command', function() {
                         }
                     }
                 });
-                cordova.platform('add', 'android');
-                expect(exec.mostRecentCall.args[0]).toMatch(/^"[^ ]*" +"[^"]*" +"[^"]*" +"[^"]*"$/g);
-                expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                cordova.raw.platform('add', 'android').then(function() {
+                    expect(exec.mostRecentCall.args[0]).toMatch(/^"[^ ]*" +"[^"]*" +"[^"]*" +"[^"]*"$/g);
+                    expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                    done();
+                });
             });
-            it('should not use a custom template directory if there is no user-defined configuration', function() {
-                cordova.platform('add', 'android');
-                expect(exec.mostRecentCall.args[0]).toMatch(/^"[^ ]*" +"[^"]*" +"[^"]*" +"[^"]*"$/g);
-                expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+            it('should not use a custom template directory if there is no user-defined configuration', function(done) {
+                cordova.raw.platform('add', 'android').then(function() {
+                    expect(exec.mostRecentCall.args[0]).toMatch(/^"[^ ]*" +"[^"]*" +"[^"]*" +"[^"]*"$/g);
+                    expect(exec.mostRecentCall.args[0]).toContain(project_dir);
+                    done();
+                });
             });
         });
         describe('`remove`',function() {
-            it('should remove a supported and added platform', function() {
-                cordova.platform('remove', 'android');
-                expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'platforms', 'android'));
-                expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'merges', 'android'));
+            it('should remove a supported and added platform', function(done) {
+                cordova.raw.platform('remove', 'android').then(function() {
+                    expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'platforms', 'android'));
+                    expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'merges', 'android'));
+                    done();
+                });
             });
 
-            it('should be able to remove multiple platforms', function() {
-                cordova.platform('remove', ['android', 'blackberry10']);
-                expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'platforms', 'android'));
-                expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'merges', 'android'));
-                expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'platforms', 'blackberry10'));
-                expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'merges', 'blackberry10'));
+            it('should be able to remove multiple platforms', function(done) {
+                cordova.raw.platform('remove', ['android', 'blackberry10']).then(function() {
+                    expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'platforms', 'android'));
+                    expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'merges', 'android'));
+                    expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'platforms', 'blackberry10'));
+                    expect(rm).toHaveBeenCalledWith('-rf', path.join(project_dir, 'merges', 'blackberry10'));
+                    done();
+                });
             });
         });
         describe('`update`', function() {
             describe('failure', function() {
                 it('should fail if no platform is specified', function(done) {
-                    cordova.platform('update', [], function(err) {
+                    cordova.raw.platform('update', []).then(function() {
+                        expect('this call').toBe('fail');
+                    }, function(err) {
                         expect(err).toEqual(new Error('No platform provided. Please specify a platform to update.'));
-                        done();
-                    });
+                    }).fin(done);
                 });
                 it('should fail if more than one platform is specified', function(done) {
-                    cordova.platform('update', ['android', 'ios'], function(err) {
+                    cordova.raw.platform('update', ['android', 'ios']).then(function() {
+                        expect('this call').toBe('fail');
+                    }, function(err) {
                         expect(err).toEqual(new Error('Platform update can only be executed on one platform at a time.'));
-                        done();
-                    });
+                    }).fin(done);
                 });
             });
 
@@ -223,10 +240,11 @@ describe('platform command', function() {
                 it('should shell out to the platform update script', function(done) {
                     var oldVersion = platforms['ios'].version;
                     platforms['ios'].version = '1.0.0';
-                    cordova.platform('update', ['ios'], function(err) {
+                    cordova.raw.platform('update', ['ios']).then(function() {
+                        expect(exec).toHaveBeenCalledWith('HOMEDIR/.cordova/lib/ios/cordova/1.0.0/bin/update "some/path/platforms/ios"', jasmine.any(Function));
+                    }, function(err) {
                         expect(err).toBeUndefined();
-                        expect(exec).toHaveBeenCalledWith('HOMEDIR/.cordova/lib/ios/cordova/1.0.0/bin/update "some/path/platforms/ios"', jasmine.any(Object), jasmine.any(Function));
-
+                    }).fin(function() {
                         platforms['ios'].version = oldVersion;
                         done();
                     });
@@ -235,81 +253,92 @@ describe('platform command', function() {
         });
     });
     describe('hooks', function() {
-        describe('list (ls) hooks', function() {
+        describe('list (ls) hooks', function(done) {
             it('should fire before hooks through the hooker module', function() {
-                cordova.platform();
-                expect(fire).toHaveBeenCalledWith('before_platform_ls', jasmine.any(Function));
+                cordova.raw.platform().then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_platform_ls');
+                    done();
+                });
             });
-            it('should fire after hooks through the hooker module', function() {
-                cordova.platform();
-                expect(fire).toHaveBeenCalledWith('after_platform_ls', jasmine.any(Function));
+            it('should fire after hooks through the hooker module', function(done) {
+                cordova.raw.platform().then(function() {
+                    expect(fire).toHaveBeenCalledWith('after_platform_ls');
+                    done();
+                });
             });
         });
         describe('remove (rm) hooks', function() {
-            it('should fire before hooks through the hooker module', function() {
-                cordova.platform('rm', 'android');
-                expect(fire).toHaveBeenCalledWith('before_platform_rm', {platforms:['android']}, jasmine.any(Function));
+            it('should fire before hooks through the hooker module', function(done) {
+                cordova.raw.platform('rm', 'android').then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_platform_rm', {platforms:['android']});
+                    done();
+                });
             });
-            it('should fire after hooks through the hooker module', function() {
-                cordova.platform('rm', 'android');
-                expect(fire).toHaveBeenCalledWith('after_platform_rm', {platforms:['android']}, jasmine.any(Function));
+            it('should fire after hooks through the hooker module', function(done) {
+                cordova.raw.platform('rm', 'android').then(function() {
+                    expect(fire).toHaveBeenCalledWith('after_platform_rm', {platforms:['android']});
+                    done();
+                });
             });
         });
         describe('add hooks', function() {
-            it('should fire before and after hooks through the hooker module', function() {
-                cordova.platform('add', 'android');
-                expect(fire).toHaveBeenCalledWith('before_platform_add', {platforms:['android']}, jasmine.any(Function));
-                expect(fire).toHaveBeenCalledWith('after_platform_add', {platforms:['android']}, jasmine.any(Function));
+            it('should fire before and after hooks through the hooker module', function(done) {
+                cordova.raw.platform('add', 'android').then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_platform_add', {platforms:['android']});
+                    expect(fire).toHaveBeenCalledWith('after_platform_add', {platforms:['android']});
+                    done();
+                });
             });
         });
     });
 });
 
-describe('platform.supports(name, callback)', function() {
+describe('platform.supports(name)', function() {
     var supports = {};
     beforeEach(function() {
         supported_platforms.forEach(function(p) {
-            supports[p] = spyOn(platforms[p].parser, 'check_requirements').andCallFake(function(project, cb) { cb(); });
+            supports[p] = spyOn(platforms[p].parser, 'check_requirements').andReturn(Q());
         });
     });
-    it('should require a platform name', function() {
-        expect(function() {
-            cordova.platform.supports(project_dir, undefined, function(e){});
-        }).toThrow();
-    });
 
-    it('should require a callback function', function() {
-        expect(function() {
-            cordova.platform.supports(project_dir, 'android', undefined);
-        }).toThrow();
+    function expectFailure(p, done, post) {
+        p.then(function() {
+            expect('this call').toBe('fail');
+        }, post).fin(done);
+    }
+
+    it('should require a platform name', function(done) {
+        expectFailure(cordova.raw.platform.supports(project_dir, undefined), done, function(err) {
+            expect(err).toEqual(jasmine.any(Error));
+        });
     });
 
     describe('when platform is unknown', function() {
-        it('should trigger callback with false', function(done) {
-            cordova.platform.supports(project_dir, 'windows-3.1', function(e) {
-                expect(e).toEqual(jasmine.any(Error));
+        it('should reject', function(done) {
+            expectFailure(cordova.raw.platform.supports(project_dir, 'windows-3.1'), done, function(err) {
+                expect(err).toEqual(jasmine.any(Error));
                 done();
             });
         });
     });
 
     describe('when platform is supported', function() {
-        it('should trigger callback without error', function(done) {
-            cordova.platform.supports(project_dir, 'android', function(e) {
-                expect(e).toBeNull();
-                done();
-            });
+        it('should resolve', function(done) {
+            cordova.raw.platform.supports(project_dir, 'android').then(function() {
+                expect(1).toBe(1);
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
         });
     });
 
     describe('when platform is unsupported', function() {
-        it('should trigger callback with error', function(done) {
+        it('should reject', function(done) {
             supported_platforms.forEach(function(p) {
-                supports[p].andCallFake(function(project, cb) { cb(new Error('no sdk')); });
+                supports[p].andReturn(Q.reject(new Error('no sdk')));
             });
-            cordova.platform.supports(project_dir, 'android', function(e) {
-                expect(e).toEqual(jasmine.any(Error));
-                done();
+            expectFailure(cordova.raw.platform.supports(project_dir, 'android'), done, function(err) {
+                expect(err).toEqual(jasmine.any(Error));
             });
         });
     });
@@ -318,9 +347,9 @@ describe('platform.supports(name, callback)', function() {
 describe('platform parsers', function() {
     it('should be exposed on the platform module', function() {
         for (var platform in platforms) {
-            expect(cordova.platform[platform]).toBeDefined();
+            expect(cordova.raw.platform[platform]).toBeDefined();
             for (var prop in platforms[platform]) {
-                expect(cordova.platform[platform][prop]).toBeDefined();
+                expect(cordova.raw.platform[platform][prop]).toBeDefined();
             }
         }
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/plugin.spec.js
----------------------------------------------------------------------
diff --git a/spec/plugin.spec.js b/spec/plugin.spec.js
index 5e021e2..1f4bf97 100644
--- a/spec/plugin.spec.js
+++ b/spec/plugin.spec.js
@@ -19,11 +19,13 @@
 var cordova = require('../cordova'),
     path = require('path'),
     shell = require('shelljs'),
+    child_process = require('child_process'),
     plugman = require('plugman'),
     fs = require('fs'),
     util = require('../src/util'),
     config = require('../src/config'),
     hooker = require('../src/hooker'),
+    Q = require('q'),
     platforms = require('../platforms');
 
 var cwd = process.cwd();
@@ -36,14 +38,9 @@ describe('plugin command', function() {
     var is_cordova, list_platforms, fire, find_plugins, rm, mkdir, existsSync, exec, prep_spy, plugman_install, plugman_fetch, parsers = {}, uninstallPlatform, uninstallPlugin;
     beforeEach(function() {
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
-        fire = spyOn(hooker.prototype, 'fire').andCallFake(function(e, opts, cb) {
-            if (cb === undefined) cb = opts;
-            cb(false);
-        });
+        fire = spyOn(hooker.prototype, 'fire').andReturn(Q());
         supported_platforms.forEach(function(p) {
-            parsers[p] = jasmine.createSpy(p + ' update_project').andCallFake(function(cfg, cb) {
-                cb();
-            });
+            parsers[p] = jasmine.createSpy(p + ' update_project').andReturn(Q());
             spyOn(platforms[p], 'parser').andReturn({
                 staging_dir:function(){return ''}
             });
@@ -53,47 +50,49 @@ describe('plugin command', function() {
         rm = spyOn(shell, 'rm');
         mkdir = spyOn(shell, 'mkdir');
         existsSync = spyOn(fs, 'existsSync').andReturn(false);
-        exec = spyOn(shell, 'exec').andCallFake(function(cmd, opts, cb) {
-            cb(0, '');
-        });
-        prep_spy = spyOn(cordova, 'prepare').andCallFake(function(t, cb) {
-            cb();
-        });
-        plugman_install = spyOn(plugman, 'install').andCallFake(function(platform, platform_dir, plugin, plugins_dir, options, callback) {
-            callback();
-        });
-        plugman_fetch = spyOn(plugman, 'fetch').andCallFake(function(target, plugins_dir, opts, cb) { cb(false, path.join(plugins_dir, target)); });
-        plugman_search = spyOn(plugman, 'search').andCallFake(function(params, cb) { cb(); });
-        uninstallPlatform = spyOn(plugman.uninstall, 'uninstallPlatform');
-        uninstallPlugin = spyOn(plugman.uninstall, 'uninstallPlugin').andCallFake(function(target, plugins_dir, cb) {
-            cb && cb();
+        exec = spyOn(child_process, 'exec').andCallFake(function(cmd, opts, cb) {
+            if (!cb) cb = opts;
+            cb(0, '', '');
         });
+        prep_spy = spyOn(cordova.raw, 'prepare').andReturn(Q());
+        plugman_install = spyOn(plugman.raw, 'install').andReturn(Q());
+        plugman_fetch = spyOn(plugman.raw, 'fetch').andCallFake(function(target, plugins_dir, opts) { return Q(path.join(plugins_dir, target)); });
+        plugman_search = spyOn(plugman.raw, 'search').andReturn(Q());
+        uninstallPlatform = spyOn(plugman.raw.uninstall, 'uninstallPlatform').andReturn(Q());
+        uninstallPlugin = spyOn(plugman.raw.uninstall, 'uninstallPlugin').andReturn(Q());
     });
 
     describe('failure', function() {
-        it('should not run outside of a Cordova-based project by calling util.isCordova', function() {
+        function expectFailure(p, done, post) {
+            p.then(function() {
+                expect('this call').toBe('fail');
+            }, post).fin(done);
+        }
+
+        it('should not run outside of a Cordova-based project by calling util.isCordova', function(done) {
             is_cordova.andReturn(false);
-            expect(function() {
-                cordova.plugin();
-                expect(is_cordova).toHaveBeenCalled();
-            }).toThrow('Current working directory is not a Cordova-based project.');
+            expectFailure(cordova.raw.plugin(), done, function(err) {
+                expect(err).toEqual(new Error('Current working directory is not a Cordova-based project.'));
+            });
         });
-        it('should report back an error if used with `add` and no plugin is specified', function() {
-            expect(function() {
-               cordova.plugin('add');
-            }).toThrow('You need to qualify `add` or `remove` with one or more plugins!');
+        it('should report back an error if used with `add` and no plugin is specified', function(done) {
+            expectFailure(cordova.raw.plugin('add'), done, function(err) {
+                expect(err).toEqual(new Error('You need to qualify `add` or `remove` with one or more plugins!'));
+            });
         });
-        it('should report back an error if used with `rm` and no plugin is specified', function() {
-            expect(function() {
-               cordova.plugin('rm');
-            }).toThrow('You need to qualify `add` or `remove` with one or more plugins!');
+        it('should report back an error if used with `rm` and no plugin is specified', function(done) {
+            expectFailure(cordova.raw.plugin('rm'), done, function(err) {
+                expect(err).toEqual(new Error('You need to qualify `add` or `remove` with one or more plugins!'));
+            });
         });
     });
 
     describe('success', function() {
-        it('should run inside a Cordova-based project by calling util.isCordova', function() {
-            cordova.plugin();
-            expect(is_cordova).toHaveBeenCalled();
+        it('should run inside a Cordova-based project by calling util.isCordova', function(done) {
+            cordova.raw.plugin().then(function() {
+                expect(is_cordova).toHaveBeenCalled();
+                done();
+            });
         });
 
         describe('`ls`', function() {
@@ -106,55 +105,68 @@ describe('plugin command', function() {
                     expect(res).toEqual('No plugins added. Use `cordova plugin add <plugin>`.');
                     done();
                 });
-                cordova.plugin('list');
+                cordova.raw.plugin('list');
             });
             it('should list out added plugins in a project', function(done) {
                 cordova.on('results', function(res) {
                     expect(res).toEqual(sample_plugins);
                     done();
                 });
-                cordova.plugin('list');
+                cordova.raw.plugin('list');
             });
-            it('should trigger callback with list of plugins', function(done) {
-                cordova.plugin('list', [], function(e, plugins) {
-                    expect(e).not.toBeDefined();
+            it('should resolve with a list of plugins', function(done) {
+                cordova.raw.plugin('list', []).then(function(plugins) {
                     expect(plugins).toEqual(sample_plugins);
-                    done();
-                });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
         describe('`add`', function() {
-            it('should call plugman.fetch for each plugin', function() {
-                cordova.plugin('add', sample_plugins);
-                sample_plugins.forEach(function(p) {
-                    expect(plugman_fetch).toHaveBeenCalledWith(p, plugins_dir, {}, jasmine.any(Function));
-                });
+            it('should call plugman.fetch for each plugin', function(done) {
+                cordova.raw.plugin('add', sample_plugins).then(function() {
+                    sample_plugins.forEach(function(p) {
+                        expect(plugman_fetch).toHaveBeenCalledWith(p, plugins_dir, {});
+                    });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should call plugman.install, for each plugin, for every platform', function() {
-                cordova.plugin('add', sample_plugins);
-                sample_plugins.forEach(function(plug) {
-                    supported_platforms.forEach(function(plat) {
-                        expect(plugman_install).toHaveBeenCalledWith((plat=='blackberry'?'blackberry10':plat), path.join(project_dir, 'platforms', plat), plug, plugins_dir, jasmine.any(Object), jasmine.any(Function));
+            it('should call plugman.install, for each plugin, for every platform', function(done) {
+                cordova.raw.plugin('add', sample_plugins).then(function(err) {
+                    sample_plugins.forEach(function(plug) {
+                        supported_platforms.forEach(function(plat) {
+                            expect(plugman_install).toHaveBeenCalledWith((plat=='blackberry'?'blackberry10':plat), path.join(project_dir, 'platforms', plat), plug, plugins_dir, jasmine.any(Object));
+                        });
                     });
-                });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should pass down variables into plugman', function() {
-                cordova.plugin('add', "one", "--variable", "foo=bar");
-                supported_platforms.forEach(function(plat) {
-                    expect(plugman_install).toHaveBeenCalledWith((plat=='blackberry'?'blackberry10':plat), path.join(project_dir, 'platforms', plat), "one", plugins_dir, {www_dir: jasmine.any(String), cli_variables: { FOO: "bar"}}, jasmine.any(Function));
-                });
+            it('should pass down variables into plugman', function(done) {
+                cordova.raw.plugin('add', "one", "--variable", "foo=bar").then(function() {
+                    supported_platforms.forEach(function(plat) {
+                        expect(plugman_install).toHaveBeenCalledWith((plat=='blackberry'?'blackberry10':plat), path.join(project_dir, 'platforms', plat), "one", plugins_dir, {www_dir: jasmine.any(String), cli_variables: { FOO: "bar"}});
+                    });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should trigger callback without an error', function(done) {
-                cordova.plugin('add', sample_plugins, function(e) {
-                    expect(e).not.toBeDefined();
-                    done();
-                });
+            it('should resolve without an error', function(done) {
+                cordova.raw.plugin('add', sample_plugins).then(function() {
+                    expect(1).toBe(1);
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
         describe('`search`', function() {
-            it('should call plugman.search', function() {
-                cordova.plugin('search', sample_plugins);
-                expect(plugman_search).toHaveBeenCalledWith(sample_plugins, jasmine.any(Function));
+            it('should call plugman.search', function(done) {
+                cordova.raw.plugin('search', sample_plugins).then(function() {
+                    expect(plugman_search).toHaveBeenCalledWith(sample_plugins);
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
         describe('`remove`',function() {
@@ -165,30 +177,39 @@ describe('plugin command', function() {
                     platforms:subset
                 });
             });
-            it('should throw if plugin is not installed', function() {
-                expect(function() {
-                    cordova.plugin('rm', 'somethingrandom');
-                }).toThrow('Plugin "somethingrandom" not added to project.');
+            it('should throw if plugin is not installed', function(done) {
+                cordova.raw.plugin('rm', 'somethingrandom').then(function() {
+                    expect('this call').toBe('fail');
+                }, function(err) {
+                    expect(err).toEqual(new Error('Plugin "somethingrandom" not added to project.'));
+                }).fin(done);
             });
 
-            it('should call plugman.uninstall.uninstallPlatform for every matching installedplugin-supportedplatform pair', function() {
-                cordova.plugin('rm', sample_plugins);
-                sample_plugins.forEach(function(plug) {
-                    subset.forEach(function(plat) {
-                        expect(uninstallPlatform).toHaveBeenCalledWith(plat, path.join(project_dir, 'platforms', plat), plug, plugins_dir, jasmine.any(Object));
+            it('should call plugman.uninstall.uninstallPlatform for every matching installedplugin-supportedplatform pair', function(done) {
+                cordova.raw.plugin('rm', sample_plugins).then(function() {
+                    sample_plugins.forEach(function(plug) {
+                        subset.forEach(function(plat) {
+                            expect(uninstallPlatform).toHaveBeenCalledWith(plat, path.join(project_dir, 'platforms', plat), plug, plugins_dir, jasmine.any(Object));
+                        });
                     });
-                });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should call plugman.uninstall.uninstallPlugin once for every removed plugin', function() {
+            it('should call plugman.uninstall.uninstallPlugin once for every removed plugin', function(done) {
                 uninstallPlugin.reset();
-                cordova.plugin('rm', sample_plugins);
-                expect(uninstallPlugin.callCount).toBe(2);
+                cordova.raw.plugin('rm', sample_plugins).then(function() {
+                    expect(uninstallPlugin.callCount).toBe(2);
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should trigger callback without an error', function(done) {
-                cordova.plugin('rm', sample_plugins, function(e) {
-                    expect(e).not.toBeDefined();
-                    done();
-                });
+            it('should resolve without an error', function(done) {
+                cordova.raw.plugin('rm', sample_plugins).then(function() {
+                    expect(1).toBe(1);
+                }, function(err) {
+                    expect(err).not.toBeDefined();
+                }).fin(done);
             });
         });
     });
@@ -200,30 +221,45 @@ describe('plugin command', function() {
             });
         });
         describe('list (ls) hooks', function() {
-            it('should fire before hooks through the hooker module', function() {
-                cordova.plugin();
-                expect(fire).toHaveBeenCalledWith('before_plugin_ls', jasmine.any(Function));
+            it('should fire before hooks through the hooker module', function(done) {
+                cordova.raw.plugin().then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_plugin_ls');
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should fire after hooks through the hooker module', function() {
-                cordova.plugin();
-                expect(fire).toHaveBeenCalledWith('after_plugin_ls', jasmine.any(Function));
+            it('should fire after hooks through the hooker module', function(done) {
+                cordova.raw.plugin().then(function() {
+                    expect(fire).toHaveBeenCalledWith('after_plugin_ls');
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
         describe('remove (rm) hooks', function() {
-            it('should fire before hooks through the hooker module', function() {
-                cordova.plugin('rm', 'two');
-                expect(fire).toHaveBeenCalledWith('before_plugin_rm', {plugins:['two'], options: []}, jasmine.any(Function));
+            it('should fire before hooks through the hooker module', function(done) {
+                cordova.raw.plugin('rm', 'two').then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_plugin_rm', {plugins:['two'], options: []});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should fire after hooks through the hooker module', function() {
-                cordova.plugin('rm', 'one');
-                expect(fire).toHaveBeenCalledWith('after_plugin_rm', {plugins:['one'], options:[]}, jasmine.any(Function));
+            it('should fire after hooks through the hooker module', function(done) {
+                cordova.raw.plugin('rm', 'one').then(function() {
+                    expect(fire).toHaveBeenCalledWith('after_plugin_rm', {plugins:['one'], options:[]});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
         describe('add hooks', function() {
-            it('should fire before and after hooks through the hooker module', function() {
-                cordova.plugin('add', 'android');
-                expect(fire).toHaveBeenCalledWith('before_plugin_add', {plugins:['android'], options: []}, jasmine.any(Function));
-                expect(fire).toHaveBeenCalledWith('after_plugin_add', {plugins:['android'], options: []}, jasmine.any(Function));
+            it('should fire before and after hooks through the hooker module', function(done) {
+                cordova.raw.plugin('add', 'android').then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_plugin_add', {plugins:['android'], options: []});
+                    expect(fire).toHaveBeenCalledWith('after_plugin_add', {plugins:['android'], options: []});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/prepare.spec.js b/spec/prepare.spec.js
index 19710c9..c392e75 100644
--- a/spec/prepare.spec.js
+++ b/spec/prepare.spec.js
@@ -26,6 +26,7 @@ var cordova = require('../cordova'),
     platforms = require('../platforms'),
     hooker = require('../src/hooker'),
     fixtures = path.join(__dirname, 'fixtures'),
+    Q = require('q'),
     hooks = path.join(fixtures, 'hooks');
 
 var project_dir = '/some/path';
@@ -37,86 +38,99 @@ describe('prepare command', function() {
     beforeEach(function() {
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
         list_platforms = spyOn(util, 'listPlatforms').andReturn(supported_platforms);
-        fire = spyOn(hooker.prototype, 'fire').andCallFake(function(e, opts, cb) {
-            cb(false);
-        });
+        fire = spyOn(hooker.prototype, 'fire').andReturn(Q());
         config_parser = spyOn(util, 'config_parser');
         supported_platforms.forEach(function(p) {
-            parsers[p] = jasmine.createSpy(p + ' update_project').andCallFake(function(cfg, cb) {
-                cb();
-            });
+            parsers[p] = jasmine.createSpy(p + ' update_project').andReturn(Q());
             spyOn(platforms[p], 'parser').andReturn({
                 update_project:parsers[p],
                 www_dir:function() { return path.join(project_dir, 'platforms', p, 'www'); }
             });
         });
-        plugman_prepare = spyOn(plugman, 'prepare');
+        plugman_prepare = spyOn(plugman, 'prepare').andReturn(Q());
         find_plugins = spyOn(util, 'findPlugins').andReturn([]);
         plugman_get_json = spyOn(plugman.config_changes, 'get_platform_json').andReturn({});
-        load = spyOn(lazy_load, 'based_on_config').andCallFake(function(root, platform, cb) { cb(); });
+        load = spyOn(lazy_load, 'based_on_config').andReturn(Q());
     });
 
     describe('failure', function() {
-        it('should not run outside of a cordova-based project by calling util.isCordova', function() {
+        it('should not run outside of a cordova-based project by calling util.isCordova', function(done) {
             is_cordova.andReturn(false);
-            expect(function() {
-                cordova.prepare();
-                expect(is_cordova).toHaveBeenCalled();
-            }).toThrow('Current working directory is not a Cordova-based project.');
+            cordova.raw.prepare().then(function() {
+                expect('this call').toBe('fail');
+            }, function(err) {
+                expect(err).toEqual(new Error('Current working directory is not a Cordova-based project.'));
+            }).fin(done);
         });
-        it('should not run inside a cordova-based project with no platforms', function() {
+        it('should not run inside a cordova-based project with no platforms', function(done) {
             list_platforms.andReturn([]);
-            expect(function() {
-                cordova.prepare();
-            }).toThrow('No platforms added to this project. Please use `cordova platform add <platform>`.');
+            cordova.raw.prepare().then(function() {
+                expect('this call').toBe('fail');
+            }, function(err) {
+                expect(err).toEqual(new Error('No platforms added to this project. Please use `cordova platform add <platform>`.'));
+            }).fin(done);
         });
     });
 
     describe('success', function() {
-        it('should run inside a Cordova-based project by calling util.isCordova', function() {
-            cordova.prepare();
-            expect(is_cordova).toHaveBeenCalled();
+        it('should run inside a Cordova-based project by calling util.isCordova', function(done) {
+            cordova.raw.prepare().then(function() {
+                expect(is_cordova).toHaveBeenCalled();
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
         });
-        it('should parse user\'s config.xml by instantiating a config_parser only _after_ before_prepare is called', function() {
-            var before_prep, after_prep, cont;
-            fire.andCallFake(function(e, opts, cb) {
+        it('should parse user\'s config.xml by instantiating a config_parser only _after_ before_prepare is called', function(done) {
+            var before_prep;
+            config_parser.andCallFake(function() {
+                expect(before_prep).toBe(true);
+            });
+            fire.andCallFake(function(e, opts) {
                 if (e == 'before_prepare') {
                     before_prep = true;
+                    expect(config_parser).not.toHaveBeenCalled();
                 }
-                cont = cb;
+                return Q();
             });
-            runs(function() {
-                cordova.prepare();
-            });
-            waitsFor(function() { return before_prep; });
-            runs(function() {
-                expect(config_parser).not.toHaveBeenCalled();
-                cont();
+
+            cordova.raw.prepare().then(function() {
+                expect(before_prep).toBe(true);
                 expect(config_parser).toHaveBeenCalledWith(path.join(project_dir, 'www', 'config.xml'));
-            });
-        });
-        it('should invoke each platform\'s parser\'s update_project method', function() {
-            cordova.prepare();
-            supported_platforms.forEach(function(p) {
-                expect(parsers[p]).toHaveBeenCalled();
-            });
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
         });
-        it('should invoke lazy_load for each platform to make sure platform libraries are loaded', function() {
-            cordova.prepare();
-            supported_platforms.forEach(function(p) {
-                expect(load).toHaveBeenCalledWith(project_dir, p, jasmine.any(Function));
-            });
+        it('should invoke each platform\'s parser\'s update_project method', function(done) {
+            cordova.raw.prepare().then(function() {
+                supported_platforms.forEach(function(p) {
+                    expect(parsers[p]).toHaveBeenCalled();
+                });
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
         });
-        describe('plugman integration', function() {
-            it('should invoke plugman.prepare after update_project', function() {
-                cordova.prepare();
-                var plugins_dir = path.join(project_dir, 'plugins');
+        it('should invoke lazy_load for each platform to make sure platform libraries are loaded', function(done) {
+            cordova.raw.prepare().then(function() {
                 supported_platforms.forEach(function(p) {
-                    var platform_path = path.join(project_dir, 'platforms', p);
-                    expect(plugman_prepare).toHaveBeenCalledWith(platform_path, (p=='blackberry'?'blackberry10':p), plugins_dir);
+                    expect(load).toHaveBeenCalledWith(project_dir, p);
                 });
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
+        });
+        describe('plugman integration', function() {
+            it('should invoke plugman.prepare after update_project', function(done) {
+                cordova.raw.prepare().then(function() {
+                    var plugins_dir = path.join(project_dir, 'plugins');
+                    supported_platforms.forEach(function(p) {
+                        var platform_path = path.join(project_dir, 'platforms', p);
+                        expect(plugman_prepare).toHaveBeenCalledWith(platform_path, (p=='blackberry'?'blackberry10':p), plugins_dir);
+                    });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
-            it('should invoke add_plugin_changes for any added plugins to verify configuration changes for plugins are in place', function() {
+            it('should invoke add_plugin_changes for any added plugins to verify configuration changes for plugins are in place', function(done) {
                 var plugins_dir = path.join(project_dir, 'plugins');
                 find_plugins.andReturn(['testPlugin']);
                 plugman_get_json.andReturn({
@@ -125,11 +139,14 @@ describe('prepare command', function() {
                     }
                 });
                 var add_plugin_changes = spyOn(plugman.config_changes, 'add_plugin_changes');
-                cordova.prepare();
-                supported_platforms.forEach(function(p) {
-                    var platform_path = path.join(project_dir, 'platforms', p);
-                    expect(add_plugin_changes).toHaveBeenCalledWith((p=='blackberry'?'blackberry10':p), platform_path, plugins_dir, 'testPlugin', 'plugin vars', true, false);
-                });
+                cordova.raw.prepare().then(function() {
+                    supported_platforms.forEach(function(p) {
+                        var platform_path = path.join(project_dir, 'platforms', p);
+                        expect(add_plugin_changes).toHaveBeenCalledWith((p=='blackberry'?'blackberry10':p), platform_path, plugins_dir, 'testPlugin', 'plugin vars', true, false);
+                    });
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
     });
@@ -137,15 +154,19 @@ describe('prepare command', function() {
 
     describe('hooks', function() {
         describe('when platforms are added', function() {
-            it('should fire before hooks through the hooker module, and pass in platforms and paths as data object', function() {
-                cordova.prepare();
-                expect(fire).toHaveBeenCalledWith('before_prepare', {verbose: false, platforms:supported_platforms, options: [], paths:supported_platforms_paths}, jasmine.any(Function));
+            it('should fire before hooks through the hooker module, and pass in platforms and paths as data object', function(done) {
+                cordova.raw.prepare().then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_prepare', {verbose: false, platforms:supported_platforms, options: [], paths:supported_platforms_paths});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
             it('should fire after hooks through the hooker module, and pass in platforms and paths as data object', function(done) {
-                cordova.prepare('android', function() {
-                     expect(fire).toHaveBeenCalledWith('after_prepare', {verbose: false, platforms:['android'], options: [], paths:[path.join(project_dir, 'platforms', 'android', 'www')]}, jasmine.any(Function));
-                     done();
-                });
+                cordova.raw.prepare('android').then(function() {
+                     expect(fire).toHaveBeenCalledWith('after_prepare', {verbose: false, platforms:['android'], options: [], paths:[path.join(project_dir, 'platforms', 'android', 'www')]});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
 
@@ -153,12 +174,14 @@ describe('prepare command', function() {
             beforeEach(function() {
                 list_platforms.andReturn([]);
             });
-            it('should not fire the hooker', function() {
-                expect(function() {
-                    cordova.prepare();
-                }).toThrow();
-                expect(fire).not.toHaveBeenCalledWith('before_prepare');
-                expect(fire).not.toHaveBeenCalledWith('after_prepare');
+            it('should not fire the hooker', function(done) {
+                cordova.raw.prepare().then(function() {
+                    expect('this call').toBe('fail');
+                }, function(err) {
+                    expect(err).toEqual(jasmine.any(Error));
+                    expect(fire).not.toHaveBeenCalledWith('before_prepare');
+                    expect(fire).not.toHaveBeenCalledWith('after_prepare');
+                }).fin(done);
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/run.spec.js
----------------------------------------------------------------------
diff --git a/spec/run.spec.js b/spec/run.spec.js
index df113bd..302c425 100644
--- a/spec/run.spec.js
+++ b/spec/run.spec.js
@@ -18,10 +18,11 @@
 */
 var cordova = require('../cordova'),
     platforms = require('../platforms'),
-    shell = require('shelljs'),
+    child_process = require('child_process'),
     path = require('path'),
     fs = require('fs'),
     hooker = require('../src/hooker'),
+    Q = require('q'),
     util = require('../src/util');
 
 var supported_platforms = Object.keys(platforms).filter(function(p) { return p != 'www'; });
@@ -33,68 +34,79 @@ describe('run command', function() {
     beforeEach(function() {
         is_cordova = spyOn(util, 'isCordova').andReturn(project_dir);
         list_platforms = spyOn(util, 'listPlatforms').andReturn(supported_platforms);
-        fire = spyOn(hooker.prototype, 'fire').andCallFake(function(e, opts, cb) {
-            cb(false);
+        fire = spyOn(hooker.prototype, 'fire').andReturn(Q());
+        prepare_spy = spyOn(cordova.raw, 'prepare').andReturn(Q());
+        exec = spyOn(child_process, 'exec').andCallFake(function(cmd, opts, cb) {
+            if (!cb) cb = opts;
+            cb(0, '', '');
         });
-        prepare_spy = spyOn(cordova, 'prepare').andCallFake(function(platforms, cb) {
-            cb();
-        });
-        exec = spyOn(shell, 'exec').andCallFake(function(cmd, opts, cb) { cb(0, ''); });
     });
     describe('failure', function() {
-        it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function() {
+        it('should not run inside a Cordova-based project with no added platforms by calling util.listPlatforms', function(done) {
             list_platforms.andReturn([]);
-            expect(function() {
-                cordova.run();
-            }).toThrow('No platforms added to this project. Please use `cordova platform add <platform>`.');
+            cordova.raw.run().then(function() {
+                expect('this call').toBe('fail');
+            }, function(err) {
+                expect(err).toEqual(new Error('No platforms added to this project. Please use `cordova platform add <platform>`.'));
+            }).fin(done);
         });
-        it('should not run outside of a Cordova-based project', function() {
+        it('should not run outside of a Cordova-based project', function(done) {
             is_cordova.andReturn(false);
-            expect(function() {
-                cordova.run();
-            }).toThrow('Current working directory is not a Cordova-based project.');
+            cordova.raw.run().then(function() {
+                expect('this call').toBe('fail');
+            }, function(err) {
+                expect(err).toEqual(new Error('Current working directory is not a Cordova-based project.'));
+            }).fin(done);
         });
     });
 
     describe('success', function() {
         it('should run inside a Cordova-based project with at least one added platform and call prepare and shell out to the run script', function(done) {
-            cordova.run(['android','ios'], function(err) {
-                expect(prepare_spy).toHaveBeenCalledWith(['android', 'ios'], jasmine.any(Function));
-                expect(exec).toHaveBeenCalledWith('"' + path.join(project_dir, 'platforms', 'android', 'cordova', 'run') + '" --device', jasmine.any(Object), jasmine.any(Function));
-                expect(exec).toHaveBeenCalledWith('"' + path.join(project_dir, 'platforms', 'ios', 'cordova', 'run') + '" --device', jasmine.any(Object), jasmine.any(Function));
-                done();
-            });
+            cordova.raw.run(['android','ios']).then(function() {
+                expect(prepare_spy).toHaveBeenCalledWith(['android', 'ios']);
+                expect(exec).toHaveBeenCalledWith('"' + path.join(project_dir, 'platforms', 'android', 'cordova', 'run') + '" --device', jasmine.any(Function));
+                expect(exec).toHaveBeenCalledWith('"' + path.join(project_dir, 'platforms', 'ios', 'cordova', 'run') + '" --device', jasmine.any(Function));
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
         });
         it('should pass down parameters', function(done) {
-            cordova.run({platforms: ['blackberry10'], options:['--device', '--password', '1q1q']}, function(err) {
-                expect(prepare_spy).toHaveBeenCalledWith(['blackberry10'], jasmine.any(Function));
-                expect(exec).toHaveBeenCalledWith('"' + path.join(project_dir, 'platforms', 'blackberry10', 'cordova', 'run') + '" --device --password 1q1q', jasmine.any(Object), jasmine.any(Function));
-                done();
-            });
+            cordova.raw.run({platforms: ['blackberry10'], options:['--device', '--password', '1q1q']}).then(function() {
+                expect(prepare_spy).toHaveBeenCalledWith(['blackberry10']);
+                expect(exec).toHaveBeenCalledWith('"' + path.join(project_dir, 'platforms', 'blackberry10', 'cordova', 'run') + '" --device --password 1q1q', jasmine.any(Function));
+            }, function(err) {
+                expect(err).toBeUndefined();
+            }).fin(done);
         });
     });
 
     describe('hooks', function() {
         describe('when platforms are added', function() {
-            it('should fire before hooks through the hooker module', function() {
-                cordova.run(['android', 'ios']);
-                expect(fire).toHaveBeenCalledWith('before_run', {verbose: false, platforms:['android', 'ios'], options: []}, jasmine.any(Function));
+            it('should fire before hooks through the hooker module', function(done) {
+                cordova.raw.run(['android', 'ios']).then(function() {
+                    expect(fire).toHaveBeenCalledWith('before_run', {verbose: false, platforms:['android', 'ios'], options: []});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
             it('should fire after hooks through the hooker module', function(done) {
-                cordova.run('android', function() {
-                     expect(fire).toHaveBeenCalledWith('after_run', {verbose: false, platforms:['android'], options: []}, jasmine.any(Function));
-                     done();
-                });
+                cordova.raw.run('android').then(function() {
+                     expect(fire).toHaveBeenCalledWith('after_run', {verbose: false, platforms:['android'], options: []});
+                }, function(err) {
+                    expect(err).toBeUndefined();
+                }).fin(done);
             });
         });
 
         describe('with no platforms added', function() {
-            it('should not fire the hooker', function() {
+            it('should not fire the hooker', function(done) {
                 list_platforms.andReturn([]);
-                expect(function() {
-                    cordova.run();
-                }).toThrow();
-                expect(fire).not.toHaveBeenCalled();
+                cordova.raw.run().then(function() {
+                    expect('this call').toBe('fail');
+                }, function(err) {
+                    expect(fire).not.toHaveBeenCalled();
+                    expect(err).toEqual(new Error('No platforms added to this project. Please use `cordova platform add <platform>`.'));
+                }).fin(done);
             });
         });
     });

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/serve.spec.js
----------------------------------------------------------------------
diff --git a/spec/serve.spec.js b/spec/serve.spec.js
index aab1e43..6c43da0 100644
--- a/spec/serve.spec.js
+++ b/spec/serve.spec.js
@@ -59,10 +59,10 @@ xdescribe('serve command', function() {
         };
 
         beforeEach(function() {
-            cordova.create(tempDir);
+            cordova.raw.create(tempDir);
             process.chdir(tempDir);
-            cordova.platform('add', 'android');
-            cordova.platform('add', 'ios');
+            cordova.raw.platform('add', 'android');
+            cordova.raw.platform('add', 'ios');
 
             // Write testing HTML files into the directory.
             fs.writeFileSync(path.join(tempDir, 'platforms', 'android', 'assets', 'www', 'test.html'), payloads.android);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/spec/wrappers.spec.js
----------------------------------------------------------------------
diff --git a/spec/wrappers.spec.js b/spec/wrappers.spec.js
new file mode 100644
index 0000000..c002856
--- /dev/null
+++ b/spec/wrappers.spec.js
@@ -0,0 +1,39 @@
+var Q = require('q'),
+    cordova = require('../cordova');
+
+describe('callback wrapper', function() {
+    var calls = ['prepare', 'build', 'create', 'emulate', 'plugin', 'platform', 'compile', 'run'];
+    for (var i = 0; i < calls.length; i++) {
+        var call = calls[i];
+
+        describe('`' + call + '`', function() {
+            var raw;
+            beforeEach(function() {
+                raw = spyOn(cordova.raw, call);
+            });
+
+            it('should work with no callback and success', function() {
+                raw.andReturn(Q());
+                cordova[call]();
+                expect(raw).toHaveBeenCalled();
+            });
+
+            it('should call the callback on success', function(done) {
+                raw.andReturn(Q());
+                cordova[call](function(err) {
+                    expect(err).toBeUndefined();
+                    done();
+                });
+            });
+
+            it('should call the callback with the error on failure', function(done) {
+                raw.andReturn(Q.reject(new Error('junk')));
+                cordova[call](function(err) {
+                    expect(err).toEqual(new Error('junk'));
+                    done();
+                });
+            });
+        });
+    }
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/src/build.js
----------------------------------------------------------------------
diff --git a/src/build.js b/src/build.js
index 5883762..440817c 100644
--- a/src/build.js
+++ b/src/build.js
@@ -17,18 +17,14 @@
     under the License.
 */
 var cordova_util      = require('./util'),
-    path              = require('path'),
-    fs                = require('fs'),
-    shell             = require('shelljs'),
-    hooker            = require('./hooker'),
-    events            = require('./events'),
-    n                 = require('ncallbacks');
+    Q                 = require('q'),
+    hooker            = require('./hooker');
 
-module.exports = function build(options, callback) {
+// Returns a promise.
+module.exports = function build(options) {
     var projectRoot = cordova_util.isCordova(process.cwd());
 
-    if (options instanceof Function && callback === undefined) {
-        callback = options;
+    if (!options) {
         options = {
             verbose: false,
             platforms: [],
@@ -38,39 +34,17 @@ module.exports = function build(options, callback) {
 
     options = cordova_util.preProcessOptions(options);
     if (options.constructor.name === "Error") {
-        if (callback) return callback(options);
-        else throw options;
+        return Q.reject(options);
     }
 
     // fire build hooks
     var hooks = new hooker(projectRoot);
-    hooks.fire('before_build', options, function(err) {
-        if (err) {
-            if (callback) callback(err);
-            else throw err;
-        } else {
-            require('../cordova').prepare(options, function(err) {
-                if (err) {
-                    if (callback) callback(err);
-                    else throw err;
-                } else {
-                    require('../cordova').compile(options, function(err) {
-                        if (err) {
-                            if (callback) callback(err);
-                            else throw err;
-                        } else {
-                            hooks.fire('after_build', options, function(err) {
-                                if (err) {
-                                    if (callback) callback(err);
-                                    else throw err;
-                                } else {
-                                    if (callback) callback();
-                                }
-                            });
-                        }
-                    });
-                }
-            });
-        }
+    return hooks.fire('before_build', options)
+    .then(function() {
+        return require('../cordova').raw.prepare(options);
+    }).then(function() {
+        return require('../cordova').raw.compile(options);
+    }).then(function() {
+        return hooks.fire('after_build', options);
     });
 };

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/src/cli.js
----------------------------------------------------------------------
diff --git a/src/cli.js b/src/cli.js
index 0e7ea76..4faff80 100755
--- a/src/cli.js
+++ b/src/cli.js
@@ -21,7 +21,7 @@ module.exports = function CLI(inputArgs) {
     var optimist  = require('optimist'),
         cordova   = require('../cordova');
 
-    args = optimist(inputArgs)
+   args = optimist(inputArgs)
         .boolean('d')
         .boolean('verbose')
         .boolean('v')
@@ -84,15 +84,15 @@ module.exports = function CLI(inputArgs) {
                     opts.options.push(option);
                 }
             });
-            cordova[cmd].call(this, opts);
+            cordova.raw[cmd].call(this, opts).done();
         } else if (cmd == 'create' || cmd == 'serve') {
-            cordova[cmd].apply(this, tokens);
+            cordova.raw[cmd].apply(this, tokens).done();
         } else {
             // platform/plugins add/rm [target(s)]
             var invocation = tokens.slice(0,1); // this has the sub-command, i.e. "platform add" or "plugin rm"
             var targets = tokens.slice(1); // this should be an array of targets, be it platforms or plugins
             invocation.push(targets);
-            cordova[cmd].apply(this, invocation);
+            cordova.raw[cmd].apply(this, invocation).done();
         }
     } else {
         throw new Error('Cordova does not know ' + cmd + '; try help for a list of all the available commands.');

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/src/compile.js
----------------------------------------------------------------------
diff --git a/src/compile.js b/src/compile.js
index e1ffde7..9716224 100644
--- a/src/compile.js
+++ b/src/compile.js
@@ -18,34 +18,33 @@
 */
 var cordova_util      = require('./util'),
     path              = require('path'),
-    fs                = require('fs'),
-    shell             = require('shelljs'),
-    et                = require('elementtree'),
+    child_process     = require('child_process'),
     hooker            = require('./hooker'),
     events            = require('./events'),
-    n                 = require('ncallbacks');
+    Q                 = require('q');
 
-function shell_out_to_build(projectRoot, platform, options,  error_callback, done) {
+// Returns a promise.
+function shell_out_to_build(projectRoot, platform, options) {
     var cmd = '"' + path.join(projectRoot, 'platforms', platform, 'cordova', 'build') + (options.length ? '" ' + options.join(" ") : '"');
     events.emit('log', 'Compiling platform "' + platform + '" with command "' + cmd + '" (output to follow)...');
-    shell.exec(cmd, {silent:true, async:true}, function(code, output) {
-        events.emit('log', output);
-        if (code > 0) {
-            var err = new Error('An error occurred while building the ' + platform + ' project. ' + output);
-            if (error_callback) error_callback(err);
-            else throw err;
+    var d = Q.defer();
+    child_process.exec(cmd, function(err, stdout, stderr) {
+        events.emit('log', stdout);
+        if (err) {
+            d.reject(new Error('An error occurred while building the ' + platform + ' project. ' + stderr));
         } else {
             events.emit('log', 'Platform "' + platform + '" compiled successfully.');
-            if (done) done();
+            d.resolve();
         }
     });
+    return d.promise;
 }
 
-module.exports = function compile(options, callback) {
+// Returns a promise.
+module.exports = function compile(options) {
     var projectRoot = cordova_util.isCordova(process.cwd());
 
-    if (options instanceof Function && callback === undefined) {
-        callback = options;
+    if (!options) {
         options = {
             verbose: false,
             platforms: [],
@@ -55,36 +54,17 @@ module.exports = function compile(options, callback) {
 
     options = cordova_util.preProcessOptions(options);
     if (options.constructor.name === "Error") {
-        if (callback) return callback(options);
-        else throw options;
+        return Q.reject(options);
     }
 
     var hooks = new hooker(projectRoot);
-    hooks.fire('before_compile', options, function(err) {
-        if (err) {
-            if (callback) callback(err);
-            else throw err;
-        } else {
-            var end = n(options.platforms.length, function() {
-                hooks.fire('after_compile', options, function(err) {
-                    if (err) {
-                        if (callback) callback(err);
-                        else throw err;
-                    } else {
-                        if (callback) callback();
-                    }
-                });
-            });
-
-            // Iterate over each added platform
-            options.platforms.forEach(function(platform) {
-                try {
-                    shell_out_to_build(projectRoot, platform, options.options, callback, end);
-                } catch(e) {
-                    if (callback) callback(e);
-                    else throw e;
-                }
-            });
-        }
+    return hooks.fire('before_compile', options)
+    .then(function() {
+        // Iterate over each added platform
+        return Q.all(options.platforms.map(function(platform) {
+            return shell_out_to_build(projectRoot, platform, options.options);
+        }));
+    }).then(function() {
+        return hooks.fire('after_compile', options);
     });
 };

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/src/config.js
----------------------------------------------------------------------
diff --git a/src/config.js b/src/config.js
index f6c7b88..2fa2557 100644
--- a/src/config.js
+++ b/src/config.js
@@ -20,9 +20,7 @@
 var path          = require('path'),
     fs            = require('fs'),
     url           = require('url'),
-    shell         = require('shelljs'),
-    events        = require('./events'),
-    util          = require('./util');
+    shell         = require('shelljs');
 
 module.exports = function config(project_root, opts) {
     var json = module.exports.read(project_root);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/017b7d9b/src/create.js
----------------------------------------------------------------------
diff --git a/src/create.js b/src/create.js
index da5cb02..6ec73db 100644
--- a/src/create.js
+++ b/src/create.js
@@ -24,6 +24,7 @@ var path          = require('path'),
     events        = require('./events'),
     config        = require('./config'),
     lazy_load     = require('./lazy_load'),
+    Q             = require('q'),
     util          = require('./util');
 
 var DEFAULT_NAME = "HelloCordova",
@@ -35,21 +36,17 @@ var DEFAULT_NAME = "HelloCordova",
  * create(dir, name) - as above, but with specified name
  * create(dir, id, name) - you get the gist
  **/
-module.exports = function create (dir, id, name, callback) {
+// Returns a promise.
+module.exports = function create (dir, id, name) {
     var options = [];
 
     if (arguments.length === 0) {
-        return help();
+        return Q(help());
     }
 
-    // Massage parameters
     var args = Array.prototype.slice.call(arguments, 0);
-    if (typeof args[args.length-1] == 'function') {
-        callback = args.pop();
-    } else if (typeof callback !== 'function') {
-        callback = undefined;
-    }
 
+    // Massage parameters
     if (args.length === 0) {
         dir = process.cwd();
         id = DEFAULT_ID;
@@ -117,6 +114,7 @@ module.exports = function create (dir, id, name, callback) {
 
     var config_json = config.read(dir);
 
+    // Returns a promise.
     var finalize = function(www_lib) {
         // Keep going into child "www" folder if exists in stock app package.
         while (fs.existsSync(path.join(www_lib, 'www'))) {
@@ -134,32 +132,24 @@ module.exports = function create (dir, id, name, callback) {
         var config = new util.config_parser(configPath);
         config.packageName(id);
         config.name(name);
-        if (callback) callback();
+        return Q();
     };
 
     // Check if www assets to use was overridden.
     if (config_json.lib && config_json.lib.www) {
         events.emit('log', 'Using custom www assets ('+config_json.lib.www.id+').');
-        lazy_load.custom(config_json.lib.www.uri, config_json.lib.www.id, 'www', config_json.lib.www.version, function(err) {
-            if (err) {
-                if (callback) callback(err);
-                else throw err;
-            } else {
-                events.emit('log', 'Copying custom www assets into "' + www_dir + '"');
-                finalize(path.join(util.libDirectory, 'www', config_json.lib.www.id, config_json.lib.www.version));
-            }
+        return lazy_load.custom(config_json.lib.www.uri, config_json.lib.www.id, 'www', config_json.lib.www.version)
+        .then(function() {
+            events.emit('log', 'Copying custom www assets into "' + www_dir + '"');
+            return finalize(path.join(util.libDirectory, 'www', config_json.lib.www.id, config_json.lib.www.version));
         });
     } else {
         // Nope, so use stock cordova-hello-world-app one.
         events.emit('log', 'Using stock cordova hello-world application.');
-        lazy_load.cordova('www', function(err) {
-            if (err) {
-                if (callback) callback(err);
-                else throw err;
-            } else {
-                events.emit('log', 'Copying stock Cordova www assets into "' + www_dir + '"');
-                finalize(path.join(util.libDirectory, 'www', 'cordova', platforms.www.version));
-            }
+        return lazy_load.cordova('www')
+        .then(function() {
+            events.emit('log', 'Copying stock Cordova www assets into "' + www_dir + '"');
+            return finalize(path.join(util.libDirectory, 'www', 'cordova', platforms.www.version));
         });
     }
 };