You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ra...@apache.org on 2018/06/05 06:08:56 UTC

[cordova-common] branch master updated: Replace shelljs calls with fs-extra & which (#21)

This is an automated email from the ASF dual-hosted git repository.

raphinesse pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-common.git


The following commit(s) were added to refs/heads/master by this push:
     new d781ffd  Replace shelljs calls with fs-extra & which (#21)
d781ffd is described below

commit d781ffdc4ebd0ef092d7bd157b4fec54a2e153f7
Author: Darryl Pogue <dv...@gmail.com>
AuthorDate: Mon Jun 4 23:08:50 2018 -0700

    Replace shelljs calls with fs-extra & which (#21)
---
 package.json                             |   5 +-
 spec/ConfigChanges/ConfigChanges.spec.js | 105 ++++++++++++++++++-------------
 spec/ConfigChanges/ConfigFile.spec.js    |   2 +-
 spec/ConfigParser/ConfigParser.spec.js   |   2 +-
 spec/CordovaCheck.spec.js                |  44 ++++++-------
 spec/FileUpdater.spec.js                 |   9 ++-
 spec/PlatformJson.spec.js                |  16 +++++
 spec/PluginManager.spec.js               |   6 +-
 src/ConfigChanges/ConfigFile.js          |   2 +-
 src/ConfigParser/ConfigParser.js         |   2 +-
 src/CordovaCheck.js                      |   2 +-
 src/FileUpdater.js                       |  39 ++++--------
 src/PlatformJson.js                      |  10 +--
 src/PluginInfo/PluginInfo.js             |   2 +-
 src/PluginInfo/PluginInfoProvider.js     |   2 +-
 src/PluginManager.js                     |   2 +-
 src/superspawn.js                        |   6 +-
 src/util/xml-helpers.js                  |   2 +-
 18 files changed, 135 insertions(+), 123 deletions(-)

diff --git a/package.json b/package.json
index dc68545..f0ce2e6 100644
--- a/package.json
+++ b/package.json
@@ -27,13 +27,14 @@
     "bplist-parser": "^0.1.0",
     "cordova-registry-mapper": "^1.1.8",
     "elementtree": "0.1.7",
+    "fs-extra": "^6.0.1",
     "glob": "^7.1.2",
     "minimatch": "^3.0.0",
     "plist": "^3.0.1",
     "q": "^1.4.1",
-    "shelljs": "^0.8.1",
     "strip-bom": "^3.0.0",
-    "underscore": "^1.8.3"
+    "underscore": "^1.8.3",
+    "which": "^1.3.0"
   },
   "devDependencies": {
     "eslint": "^4.0.0",
diff --git a/spec/ConfigChanges/ConfigChanges.spec.js b/spec/ConfigChanges/ConfigChanges.spec.js
index a0abe5c..fe83c70 100644
--- a/spec/ConfigChanges/ConfigChanges.spec.js
+++ b/spec/ConfigChanges/ConfigChanges.spec.js
@@ -19,11 +19,10 @@
 
 var configChanges = require('../../src/ConfigChanges/ConfigChanges');
 var xml_helpers = require('../../src/util/xml-helpers');
-var fs = require('fs');
+var fs = require('fs-extra');
 var os = require('osenv');
 var et = require('elementtree');
 var path = require('path');
-var shell = require('shelljs');
 var temp = path.join(os.tmpdir(), 'plugman');
 var dummyplugin = path.join(__dirname, '../fixtures/plugins/org.test.plugins.dummyplugin');
 var cbplugin = path.join(__dirname, '../fixtures/plugins/org.test.plugins.childbrowser');
@@ -35,9 +34,9 @@ var editconfigplugin_two = path.join(__dirname, '../fixtures/plugins/org.test.ed
 var varplugin = path.join(__dirname, '../fixtures/plugins/com.adobe.vars');
 var plistplugin = path.join(__dirname, '../fixtures/plugins/org.apache.plist');
 var bplistplugin = path.join(__dirname, '../fixtures/plugins/org.apache.bplist');
-var android_two_project = path.join(__dirname, '../fixtures/projects/android_two/*');
-var android_two_no_perms_project = path.join(__dirname, '../fixtures/projects/android_two_no_perms', '*');
-var ios_config_xml = path.join(__dirname, '../fixtures/projects/ios-config-xml/*');
+var android_two_project = path.join(__dirname, '../fixtures/projects/android_two/');
+var android_two_no_perms_project = path.join(__dirname, '../fixtures/projects/android_two_no_perms');
+var ios_config_xml = path.join(__dirname, '../fixtures/projects/ios-config-xml/');
 var plugins_dir = path.join(temp, 'cordova', 'plugins');
 var mungeutil = require('../../src/ConfigChanges/munge-util');
 var PlatformJson = require('../../src/PlatformJson');
@@ -61,13 +60,17 @@ function get_munge_change () {
     return mungeutil.deep_find.apply(null, arguments);
 }
 
+function install_plugin (pluginPath) {
+    fs.copySync(pluginPath, path.join(plugins_dir, path.basename(pluginPath)));
+}
+
 describe('config-changes module', function () {
     beforeEach(function () {
-        shell.mkdir('-p', temp);
-        shell.mkdir('-p', plugins_dir);
+        fs.ensureDirSync(temp);
+        fs.ensureDirSync(plugins_dir);
     });
     afterEach(function () {
-        shell.rm('-rf', temp);
+        fs.removeSync(temp);
     });
 
     describe('queue methods', function () {
@@ -131,7 +134,7 @@ describe('config-changes module', function () {
     describe('generate_plugin_config_munge method', function () {
         describe('for android projects', function () {
             beforeEach(function () {
-                shell.cp('-rf', android_two_project, temp);
+                fs.copySync(android_two_project, temp);
             });
             it('Test 007 : should return a flat config hierarchy for simple, one-off config changes', function () {
                 var xml;
@@ -196,10 +199,10 @@ describe('config-changes module', function () {
 
     describe('processing of plugins (via process method)', function () {
         beforeEach(function () {
-            shell.cp('-rf', dummyplugin, plugins_dir);
+            install_plugin(dummyplugin);
         });
         it('Test 014 : should generate config munges for queued plugins', function () {
-            shell.cp('-rf', android_two_project, temp);
+            fs.copySync(android_two_project, temp);
             var platformJson = PlatformJson.load(plugins_dir, 'android');
             platformJson.root.prepare_queue.installed = [{'plugin': 'org.test.plugins.dummyplugin', 'vars': {}}];
             var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
@@ -210,7 +213,7 @@ describe('config-changes module', function () {
         describe(': installation', function () {
             describe('of xml config files', function () {
                 beforeEach(function () {
-                    shell.cp('-rf', android_two_project, temp);
+                    fs.copySync(android_two_project, temp);
                 });
                 it('Test 015 : should call graftXML for every new config munge it introduces (every leaf in config munge that does not exist)', function () {
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
@@ -227,7 +230,7 @@ describe('config-changes module', function () {
                     expect(spy.calls.argsFor(3)[2]).toEqual('/cordova/plugins');
                 });
                 it('Test 016 : should not call graftXML for a config munge that already exists from another plugin', function () {
-                    shell.cp('-rf', configplugin, plugins_dir);
+                    install_plugin(configplugin);
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.configtest', {});
 
@@ -247,7 +250,8 @@ describe('config-changes module', function () {
                     expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8');
                 });
                 it('Test 018 : should call graftXMLMerge for every new config munge with mode \'merge\' it introduces', function () {
-                    shell.cp('-rf', editconfigplugin, plugins_dir);
+                    install_plugin(editconfigplugin);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {});
 
@@ -259,8 +263,9 @@ describe('config-changes module', function () {
                     expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/application/activity[@android:name=\'org.test.DroidGap\']');
                 });
                 it('Test 019 : should call graftXMLMerge with --force for every new config munge with mode \'merge\' it introduces', function () {
-                    shell.cp('-rf', editconfigplugin, plugins_dir);
-                    shell.cp('-rf', editconfigplugin_two, plugins_dir);
+                    install_plugin(editconfigplugin);
+                    install_plugin(editconfigplugin_two);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {});
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true);
@@ -275,7 +280,8 @@ describe('config-changes module', function () {
                     expect(spy.calls.argsFor(2)[2]).toEqual('/manifest/uses-sdk');
                 });
                 it('Test 020 : should call graftXMLOverwrite for every new config munge with mode \'overwrite\' it introduces', function () {
-                    shell.cp('-rf', editconfigplugin, plugins_dir);
+                    install_plugin(editconfigplugin);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {});
 
@@ -287,8 +293,9 @@ describe('config-changes module', function () {
                     expect(spy.calls.argsFor(0)[2]).toEqual('/manifest/application/activity');
                 });
                 it('Test 021 : should call graftXMLOverwrite with --force for every new config munge with mode \'overwrite\' it introduces', function () {
-                    shell.cp('-rf', editconfigplugin, plugins_dir);
-                    shell.cp('-rf', editconfigplugin_two, plugins_dir);
+                    install_plugin(editconfigplugin);
+                    install_plugin(editconfigplugin_two);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {});
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true);
@@ -302,8 +309,9 @@ describe('config-changes module', function () {
                     expect(spy.calls.argsFor(1)[2]).toEqual('/manifest/application/activity[@android:name=\'ChildApp\']');
                 });
                 it('Test 022 : should not install plugin when there are edit-config conflicts', function () {
-                    shell.cp('-rf', editconfigplugin, plugins_dir);
-                    shell.cp('-rf', editconfigplugin_two, plugins_dir);
+                    install_plugin(editconfigplugin);
+                    install_plugin(editconfigplugin_two);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest', {});
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {});
@@ -348,7 +356,8 @@ describe('config-changes module', function () {
                     expect(sdk.attrib['android:maxSdkVersion']).toBeUndefined();
                 });
                 it('should overwrite plugin config munge for every conflicting config.xml config munge', function () {
-                    shell.cp('-rf', editconfigplugin_two, plugins_dir);
+                    install_plugin(editconfigplugin_two);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true);
 
@@ -396,7 +405,8 @@ describe('config-changes module', function () {
                     expect(am_file.indexOf('android:name="zoo"')).toBeLessThan(am_file.indexOf('android:name="com.foo.Bar"'));
                 });
                 it('should throw error for conflicting plugin config munge with config.xml config munge', function () {
-                    shell.cp('-rf', editconfigplugin_two, plugins_dir);
+                    install_plugin(editconfigplugin_two);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
                     platformJson.addInstalledPluginToPrepareQueue('org.test.editconfigtest_two', {}, true, true);
 
@@ -408,16 +418,18 @@ describe('config-changes module', function () {
             });
             describe('of plist config files', function () {
                 it('Test 023 : should write empty string nodes with no whitespace', function () {
-                    shell.cp('-rf', ios_config_xml, temp);
-                    shell.cp('-rf', varplugin, plugins_dir);
+                    fs.copySync(ios_config_xml, temp);
+                    install_plugin(varplugin);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'ios');
                     platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {});
                     configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider);
                     expect(fs.readFileSync(path.join(temp, 'SampleApp', 'SampleApp-Info.plist'), 'utf-8')).toMatch(/<key>APluginNode<\/key>\n {4}<string\/>/m);
                 });
                 it('Test 024 : should merge dictionaries and arrays, removing duplicates', function () {
-                    shell.cp('-rf', ios_config_xml, temp);
-                    shell.cp('-rf', plistplugin, plugins_dir);
+                    fs.copySync(ios_config_xml, temp);
+                    install_plugin(plistplugin);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'ios');
                     platformJson.addInstalledPluginToPrepareQueue('org.apache.plist', {});
                     configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider);
@@ -428,8 +440,9 @@ describe('config-changes module', function () {
             });
             describe('of binary plist config files', function () {
                 it('should merge dictionaries and arrays, removing duplicates', function () {
-                    shell.cp('-rf', ios_config_xml, temp);
-                    shell.cp('-rf', bplistplugin, plugins_dir);
+                    fs.copySync(ios_config_xml, temp);
+                    install_plugin(bplistplugin);
+
                     var platformJson = PlatformJson.load(plugins_dir, 'ios');
                     platformJson.addInstalledPluginToPrepareQueue('org.apache.bplist', {});
                     configChanges.process(plugins_dir, temp, 'ios', platformJson, pluginInfoProvider);
@@ -440,8 +453,9 @@ describe('config-changes module', function () {
                 });
             });
             it('Test 025 : should resolve wildcard config-file targets to the project, if applicable', function () {
-                shell.cp('-rf', ios_config_xml, temp);
-                shell.cp('-rf', cbplugin, plugins_dir);
+                fs.copySync(ios_config_xml, temp);
+                install_plugin(cbplugin);
+
                 var platformJson = PlatformJson.load(plugins_dir, 'ios');
                 platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.childbrowser', {});
                 var spy = spyOn(fs, 'readFileSync').and.callThrough();
@@ -451,8 +465,9 @@ describe('config-changes module', function () {
                 expect(spy).toHaveBeenCalledWith(path.join(temp, 'SampleApp', 'SampleApp-Info.plist').replace(/\\/g, '/'), 'utf8');
             });
             it('Test 026 : should move successfully installed plugins from queue to installed plugins section, and include/retain vars if applicable', function () {
-                shell.cp('-rf', android_two_project, temp);
-                shell.cp('-rf', varplugin, plugins_dir);
+                fs.copySync(android_two_project, temp);
+                install_plugin(varplugin);
+
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
                 platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {'API_KEY': 'hi'}, true);
 
@@ -467,7 +482,7 @@ describe('config-changes module', function () {
 
         describe(': uninstallation', function () {
             it('Test 027 : should call pruneXML for every config munge it completely removes from the app (every leaf that is decremented to 0)', function () {
-                shell.cp('-rf', android_two_project, temp);
+                fs.copySync(android_two_project, temp);
 
                 // Run through an "install"
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
@@ -486,8 +501,9 @@ describe('config-changes module', function () {
                 expect(spy.calls.argsFor(3)[2]).toEqual('/cordova/plugins');
             });
             it('Test 028 : should generate a config munge that interpolates variables into config changes, if applicable', function () {
-                shell.cp('-rf', android_two_project, temp);
-                shell.cp('-rf', varplugin, plugins_dir);
+                fs.copySync(android_two_project, temp);
+                install_plugin(varplugin);
+
                 // Run through an "install"
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
                 platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {'API_KEY': 'canucks'});
@@ -504,9 +520,9 @@ describe('config-changes module', function () {
                 expect(munge_params[1]['API_KEY']).toEqual('canucks');
             });
             it('Test 029 : should not call pruneXML for a config munge that another plugin depends on', function () {
-                shell.cp('-rf', android_two_no_perms_project, temp);
-                shell.cp('-rf', childrenplugin, plugins_dir);
-                shell.cp('-rf', shareddepsplugin, plugins_dir);
+                fs.copySync(android_two_no_perms_project, temp);
+                install_plugin(childrenplugin);
+                install_plugin(shareddepsplugin);
 
                 // Run through and "install" two plugins (they share a permission for INTERNET)
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
@@ -525,7 +541,7 @@ describe('config-changes module', function () {
                 expect(permission.attrib['android:name']).toEqual('android.permission.INTERNET');
             });
             it('Test 030 : should not call pruneXML for a config munge targeting a config file that does not exist', function () {
-                shell.cp('-rf', android_two_project, temp);
+                fs.copySync(android_two_project, temp);
                 // install a plugin
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
                 platformJson.addInstalledPluginToPrepareQueue('org.test.plugins.dummyplugin', {});
@@ -541,8 +557,9 @@ describe('config-changes module', function () {
                 expect(spy).not.toHaveBeenCalledWith(path.join(temp, 'res', 'xml', 'plugins.xml'), 'utf-8');
             });
             it('Test 031 : should remove uninstalled plugins from installed plugins list', function () {
-                shell.cp('-rf', android_two_project, temp);
-                shell.cp('-rf', varplugin, plugins_dir);
+                fs.copySync(android_two_project, temp);
+                install_plugin(varplugin);
+
                 // install the var plugin
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
                 platformJson.addInstalledPluginToPrepareQueue('com.adobe.vars', {'API_KEY': 'eat my shorts'});
@@ -557,8 +574,8 @@ describe('config-changes module', function () {
                 expect(platformJson.root.installed_plugins['com.adobe.vars']).not.toBeDefined();
             });
             it('Test 032 : should call pruneXMLRestore for every config munge with mode \'merge\' or \'overwrite\' it removes from the app', function () {
-                shell.cp('-rf', android_two_project, temp);
-                shell.cp('-rf', editconfigplugin, plugins_dir);
+                fs.copySync(android_two_project, temp);
+                install_plugin(editconfigplugin);
 
                 // Run through an "install"
                 var platformJson = PlatformJson.load(plugins_dir, 'android');
diff --git a/spec/ConfigChanges/ConfigFile.spec.js b/spec/ConfigChanges/ConfigFile.spec.js
index 2da5ee8..98b1d98 100644
--- a/spec/ConfigChanges/ConfigFile.spec.js
+++ b/spec/ConfigChanges/ConfigFile.spec.js
@@ -17,7 +17,7 @@
 
 var rewire = require('rewire');
 var configFile = rewire('../../src/ConfigChanges/ConfigFile');
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 var projectDir = path.join('project_dir', 'app', 'src', 'main');
 
diff --git a/spec/ConfigParser/ConfigParser.spec.js b/spec/ConfigParser/ConfigParser.spec.js
index 2129565..9416863 100644
--- a/spec/ConfigParser/ConfigParser.spec.js
+++ b/spec/ConfigParser/ConfigParser.spec.js
@@ -18,7 +18,7 @@
 */
 
 var path = require('path');
-var fs = require('fs');
+var fs = require('fs-extra');
 var ConfigParser = require('../../src/ConfigParser/ConfigParser');
 var xml = path.join(__dirname, '../fixtures/test-config.xml');
 var xml_contents = fs.readFileSync(xml, 'utf-8');
diff --git a/spec/CordovaCheck.spec.js b/spec/CordovaCheck.spec.js
index 5a2fdc0..ea12d03 100644
--- a/spec/CordovaCheck.spec.js
+++ b/spec/CordovaCheck.spec.js
@@ -17,7 +17,7 @@
     under the License.
 */
 
-var shell = require('shelljs');
+var fs = require('fs-extra');
 var path = require('path');
 var CordovaCheck = require('../src/CordovaCheck');
 
@@ -30,13 +30,10 @@ describe('findProjectRoot method', function () {
         process.env.PWD = origPWD;
         process.chdir(cwd);
     });
-    function removeDir (someDirectory) {
-        shell.rm('-rf', someDirectory);
-    }
+
     it('Test 001 : should return false if it hits the home directory', function () {
         var somedir = path.join(home, 'somedir');
-        removeDir(somedir);
-        shell.mkdir(somedir);
+        fs.emptyDirSync(somedir);
         expect(CordovaCheck.findProjectRoot(somedir)).toEqual(false);
     });
     it('Test 002 : should return false if it cannot find a .cordova directory up the directory tree', function () {
@@ -46,28 +43,28 @@ describe('findProjectRoot method', function () {
     it('Test 003 : should return the first directory it finds with a .cordova folder in it', function () {
         var somedir = path.join(home, 'somedir');
         var anotherdir = path.join(somedir, 'anotherdir');
-        removeDir(somedir);
-        shell.mkdir('-p', anotherdir);
-        shell.mkdir('-p', path.join(somedir, 'www', 'config.xml'));
+        fs.removeSync(somedir);
+        fs.ensureDirSync(anotherdir);
+        fs.ensureFileSync(path.join(somedir, 'www', 'config.xml'));
         expect(CordovaCheck.findProjectRoot(somedir)).toEqual(somedir);
     });
     it('Test 004 : should ignore PWD when its undefined', function () {
         delete process.env.PWD;
         var somedir = path.join(home, 'somedir');
         var anotherdir = path.join(somedir, 'anotherdir');
-        removeDir(somedir);
-        shell.mkdir('-p', anotherdir);
-        shell.mkdir('-p', path.join(somedir, 'www'));
-        shell.mkdir('-p', path.join(somedir, 'config.xml'));
+        fs.removeSync(somedir);
+        fs.ensureDirSync(anotherdir);
+        fs.ensureDirSync(path.join(somedir, 'www'));
+        fs.ensureFileSync(path.join(somedir, 'config.xml'));
         process.chdir(anotherdir);
         expect(CordovaCheck.findProjectRoot()).toEqual(somedir);
     });
     it('Test 005 : should use PWD when available', function () {
         var somedir = path.join(home, 'somedir');
         var anotherdir = path.join(somedir, 'anotherdir');
-        removeDir(somedir);
-        shell.mkdir('-p', anotherdir);
-        shell.mkdir('-p', path.join(somedir, 'www', 'config.xml'));
+        fs.removeSync(somedir);
+        fs.ensureDirSync(anotherdir);
+        fs.ensureFileSync(path.join(somedir, 'www', 'config.xml'));
         process.env.PWD = anotherdir;
         process.chdir(path.sep);
         expect(CordovaCheck.findProjectRoot()).toEqual(somedir);
@@ -75,9 +72,9 @@ describe('findProjectRoot method', function () {
     it('Test 006 : should use cwd as a fallback when PWD is not a cordova dir', function () {
         var somedir = path.join(home, 'somedir');
         var anotherdir = path.join(somedir, 'anotherdir');
-        removeDir(somedir);
-        shell.mkdir('-p', anotherdir);
-        shell.mkdir('-p', path.join(somedir, 'www', 'config.xml'));
+        fs.removeSync(somedir);
+        fs.ensureDirSync(anotherdir);
+        fs.ensureFileSync(path.join(somedir, 'www', 'config.xml'));
         process.env.PWD = path.sep;
         process.chdir(anotherdir);
         expect(CordovaCheck.findProjectRoot()).toEqual(somedir);
@@ -85,11 +82,10 @@ describe('findProjectRoot method', function () {
     it('Test 007 : should ignore platform www/config.xml', function () {
         var somedir = path.join(home, 'somedir');
         var anotherdir = path.join(somedir, 'anotherdir');
-        removeDir(somedir);
-        shell.mkdir('-p', anotherdir);
-        shell.mkdir('-p', path.join(anotherdir, 'www', 'config.xml'));
-        shell.mkdir('-p', path.join(somedir, 'www'));
-        shell.mkdir('-p', path.join(somedir, 'config.xml'));
+        fs.removeSync(somedir);
+        fs.ensureFileSync(path.join(anotherdir, 'www', 'config.xml'));
+        fs.ensureDirSync(path.join(somedir, 'www'));
+        fs.ensureFileSync(path.join(somedir, 'config.xml'));
         expect(CordovaCheck.findProjectRoot(anotherdir)).toEqual(somedir);
     });
 });
diff --git a/spec/FileUpdater.spec.js b/spec/FileUpdater.spec.js
index 8370b97..1f00f02 100644
--- a/spec/FileUpdater.spec.js
+++ b/spec/FileUpdater.spec.js
@@ -53,7 +53,7 @@ function mockDirStats () {
     };
 }
 
-// Create a mock to replace the fs and shelljs modules used by the FileUpdater,
+// Create a mock to replace the fs-extra module used by the FileUpdater,
 // so the tests don't have to actually touch the filesystem.
 var mockFs = {
     mkdirPaths: [],
@@ -86,20 +86,19 @@ var mockFs = {
         return result;
     },
 
-    mkdir: function (flags, path) {
+    ensureDirSync: function (path) {
         this.mkdirPaths.push(path);
     },
 
-    cp: function (flags, sourcePath, targetPath) {
+    copySync: function (sourcePath, targetPath) {
         this.cpPaths.push([sourcePath, targetPath]);
     },
 
-    rm: function (flags, path) {
+    removeSync: function (path) {
         this.rmPaths.push(path);
     }
 };
 FileUpdater.__set__('fs', mockFs);
-FileUpdater.__set__('shell', mockFs);
 
 // Define some constants used in the test cases.
 var testRootDir = 'testRootDir';
diff --git a/spec/PlatformJson.spec.js b/spec/PlatformJson.spec.js
index fbf7746..97d04ff 100644
--- a/spec/PlatformJson.spec.js
+++ b/spec/PlatformJson.spec.js
@@ -117,6 +117,22 @@ describe('PlatformJson class', function () {
                 expect(meta).toMatch(JSON.stringify(platformJson.root.plugin_metadata, null, 2));
             });
         });
+
+        describe('generateAndSaveMetadata method', function () {
+            it('should save generated metadata', function () {
+                // Needs to use graceful-fs, since that is used by fs-extra
+                const spy = spyOn(require('graceful-fs'), 'writeFileSync');
+
+                const dest = require('path').join(__dirname, 'test-destination');
+                platformJson.addPluginMetadata(fakePlugin).generateAndSaveMetadata(dest);
+
+                expect(spy).toHaveBeenCalledTimes(1);
+                const [file, data] = spy.calls.argsFor(0);
+                expect(file).toBe(dest);
+                expect(data.indexOf(JSON.stringify(platformJson.root.modules, null, 2))).toBeGreaterThan(0);
+                expect(data).toMatch(JSON.stringify(platformJson.root.plugin_metadata, null, 2));
+            });
+        });
     });
 });
 
diff --git a/spec/PluginManager.spec.js b/spec/PluginManager.spec.js
index 1cea782..adefa8c 100644
--- a/spec/PluginManager.spec.js
+++ b/spec/PluginManager.spec.js
@@ -21,9 +21,8 @@
 // require('promise-matchers');
 
 var Q = require('q');
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
-var shell = require('shelljs');
 var rewire = require('rewire');
 var PluginManager = rewire('../src/PluginManager');
 var PluginInfo = require('../src/PluginInfo/PluginInfo');
@@ -41,8 +40,9 @@ describe('PluginManager class', function () {
 
     beforeEach(function () {
         spyOn(ConfigChanges, 'PlatformMunger');
+        spyOn(fs, 'outputJsonSync');
         spyOn(fs, 'writeFileSync');
-        spyOn(shell, 'mkdir');
+        spyOn(fs, 'ensureDirSync');
     });
 
     it('Test 001 : should be constructable', function () {
diff --git a/src/ConfigChanges/ConfigFile.js b/src/ConfigChanges/ConfigFile.js
index 54353ef..0b2a23b 100644
--- a/src/ConfigChanges/ConfigFile.js
+++ b/src/ConfigChanges/ConfigFile.js
@@ -16,7 +16,7 @@
 
 /* eslint no-control-regex: 0 */
 
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 
 var modules = {};
diff --git a/src/ConfigParser/ConfigParser.js b/src/ConfigParser/ConfigParser.js
index a393755..910b2db 100644
--- a/src/ConfigParser/ConfigParser.js
+++ b/src/ConfigParser/ConfigParser.js
@@ -20,7 +20,7 @@
 var et = require('elementtree');
 var xml = require('../util/xml-helpers');
 var CordovaError = require('../CordovaError/CordovaError');
-var fs = require('fs');
+var fs = require('fs-extra');
 var events = require('../events');
 
 /** Wraps a config.xml file */
diff --git a/src/CordovaCheck.js b/src/CordovaCheck.js
index 28f629d..5b20ade 100644
--- a/src/CordovaCheck.js
+++ b/src/CordovaCheck.js
@@ -17,7 +17,7 @@
     under the License.
 */
 
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 
 function isRootDir (dir) {
diff --git a/src/FileUpdater.js b/src/FileUpdater.js
index ea5d9e2..accf63f 100644
--- a/src/FileUpdater.js
+++ b/src/FileUpdater.js
@@ -19,9 +19,8 @@
 
 'use strict';
 
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
-var shell = require('shelljs');
 var minimatch = require('minimatch');
 
 /**
@@ -66,31 +65,24 @@ function updatePathWithStats (sourcePath, sourceStats, targetPath, targetStats,
     if (sourceStats) {
         var sourceFullPath = path.join(rootDir || '', sourcePath);
 
-        if (targetStats) {
+        if (targetStats && (targetStats.isDirectory() !== sourceStats.isDirectory())) {
             // The target exists. But if the directory status doesn't match the source, delete it.
-            if (targetStats.isDirectory() && !sourceStats.isDirectory()) {
-                log('rmdir  ' + targetPath + ' (source is a file)');
-                shell.rm('-rf', targetFullPath);
-                targetStats = null;
-                updated = true;
-            } else if (!targetStats.isDirectory() && sourceStats.isDirectory()) {
-                log('delete ' + targetPath + ' (source is a directory)');
-                shell.rm('-f', targetFullPath);
-                targetStats = null;
-                updated = true;
-            }
+            log('delete ' + targetPath);
+            fs.removeSync(targetFullPath);
+            targetStats = null;
+            updated = true;
         }
 
         if (!targetStats) {
             if (sourceStats.isDirectory()) {
                 // The target directory does not exist, so it should be created.
                 log('mkdir ' + targetPath);
-                shell.mkdir('-p', targetFullPath);
+                fs.ensureDirSync(targetFullPath);
                 updated = true;
             } else if (sourceStats.isFile()) {
                 // The target file does not exist, so it should be copied from the source.
                 log('copy  ' + sourcePath + ' ' + targetPath + (copyAll ? '' : ' (new file)'));
-                shell.cp('-f', sourceFullPath, targetFullPath);
+                fs.copySync(sourceFullPath, targetFullPath);
                 updated = true;
             }
         } else if (sourceStats.isFile() && targetStats.isFile()) {
@@ -98,7 +90,7 @@ function updatePathWithStats (sourcePath, sourceStats, targetPath, targetStats,
             if (copyAll) {
                 // The caller specified all files should be copied.
                 log('copy  ' + sourcePath + ' ' + targetPath);
-                shell.cp('-f', sourceFullPath, targetFullPath);
+                fs.copySync(sourceFullPath, targetFullPath);
                 updated = true;
             } else {
                 // Copy if the source has been modified since it was copied to the target, or if
@@ -108,20 +100,15 @@ function updatePathWithStats (sourcePath, sourceStats, targetPath, targetStats,
                 if (sourceStats.mtime.getTime() >= targetStats.mtime.getTime() ||
                         sourceStats.size !== targetStats.size) {
                     log('copy  ' + sourcePath + ' ' + targetPath + ' (updated file)');
-                    shell.cp('-f', sourceFullPath, targetFullPath);
+                    fs.copySync(sourceFullPath, targetFullPath);
                     updated = true;
                 }
             }
         }
     } else if (targetStats) {
         // The target exists but the source is null, so the target should be deleted.
-        if (targetStats.isDirectory()) {
-            log('rmdir  ' + targetPath + (copyAll ? '' : ' (no source)'));
-            shell.rm('-rf', targetFullPath);
-        } else {
-            log('delete ' + targetPath + (copyAll ? '' : ' (no source)'));
-            shell.rm('-f', targetFullPath);
-        }
+        log('delete ' + targetPath + (copyAll ? '' : ' (no source)'));
+        fs.removeSync(targetFullPath);
         updated = true;
     }
 
@@ -150,7 +137,7 @@ function updatePathInternal (sourcePath, targetPath, options, log) {
         // Create the target's parent directory if it doesn't exist.
         var parentDir = path.dirname(targetFullPath);
         if (!fs.existsSync(parentDir)) {
-            shell.mkdir('-p', parentDir);
+            fs.ensureDirSync(parentDir);
         }
     }
 
diff --git a/src/PlatformJson.js b/src/PlatformJson.js
index 86dace3..9cfb185 100644
--- a/src/PlatformJson.js
+++ b/src/PlatformJson.js
@@ -14,9 +14,8 @@
  *
 */
 
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
-var shelljs = require('shelljs');
 var mungeutil = require('./ConfigChanges/munge-util');
 var pluginMappernto = require('cordova-registry-mapper').newToOld;
 var pluginMapperotn = require('cordova-registry-mapper').oldToNew;
@@ -37,8 +36,7 @@ PlatformJson.load = function (plugins_dir, platform) {
 };
 
 PlatformJson.prototype.save = function () {
-    shelljs.mkdir('-p', path.dirname(this.filePath));
-    fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 2), 'utf-8');
+    fs.outputJsonSync(this.filePath, this.root, {spaces: 2});
 };
 
 /**
@@ -211,9 +209,7 @@ PlatformJson.prototype.generateMetadata = function () {
  * @return {PlatformJson} PlatformJson instance
  */
 PlatformJson.prototype.generateAndSaveMetadata = function (destination) {
-    var meta = this.generateMetadata();
-    shelljs.mkdir('-p', path.dirname(destination));
-    fs.writeFileSync(destination, meta, 'utf-8');
+    fs.outputFileSync(destination, this.generateMetadata());
 
     return this;
 };
diff --git a/src/PluginInfo/PluginInfo.js b/src/PluginInfo/PluginInfo.js
index 7e9754d..428df9d 100644
--- a/src/PluginInfo/PluginInfo.js
+++ b/src/PluginInfo/PluginInfo.js
@@ -26,7 +26,7 @@ TODO (kamrik): refactor this to not use sync functions and return promises.
 */
 
 var path = require('path');
-var fs = require('fs');
+var fs = require('fs-extra');
 var xml_helpers = require('../util/xml-helpers');
 var CordovaError = require('../CordovaError/CordovaError');
 
diff --git a/src/PluginInfo/PluginInfoProvider.js b/src/PluginInfo/PluginInfoProvider.js
index 5d3f329..403167d 100644
--- a/src/PluginInfo/PluginInfoProvider.js
+++ b/src/PluginInfo/PluginInfoProvider.js
@@ -19,7 +19,7 @@
 
 /* jshint sub:true, laxcomma:true, laxbreak:true */
 
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 var PluginInfo = require('./PluginInfo');
 var events = require('../events');
diff --git a/src/PluginManager.js b/src/PluginManager.js
index b875d0e..5a018de 100644
--- a/src/PluginManager.js
+++ b/src/PluginManager.js
@@ -18,7 +18,7 @@
 */
 
 var Q = require('q');
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 
 var ActionStack = require('./ActionStack');
diff --git a/src/superspawn.js b/src/superspawn.js
index 99e3057..eae552c 100644
--- a/src/superspawn.js
+++ b/src/superspawn.js
@@ -18,11 +18,11 @@
 */
 
 var child_process = require('child_process');
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 var _ = require('underscore');
 var Q = require('q');
-var shell = require('shelljs');
+var which = require('which');
 var events = require('./events');
 var iswin32 = process.platform === 'win32';
 
@@ -35,7 +35,7 @@ function resolveWindowsExe (cmd) {
     if (isValidExe(cmd)) {
         return cmd;
     }
-    cmd = String(shell.which(cmd) || cmd);
+    cmd = which.sync(cmd, { nothrow: true }) || cmd;
     if (!isValidExe(cmd)) {
         winExtensions.some(function (ext) {
             if (fs.existsSync(cmd + ext)) {
diff --git a/src/util/xml-helpers.js b/src/util/xml-helpers.js
index ca65bfa..b8a03e9 100644
--- a/src/util/xml-helpers.js
+++ b/src/util/xml-helpers.js
@@ -21,7 +21,7 @@
  * contains XML utility functions, some of which are specific to elementtree
  */
 
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 var _ = require('underscore');
 var et = require('elementtree');

-- 
To stop receiving notification emails like this one, please contact
raphinesse@apache.org.

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org