refactor: modernize code & update README (#82)

* refactor: transform var to let/const
* refactor: consolidate cordova-common vars
* refactor: transform arrow functions & arrow returns
* refactor: transform template strings
* doc(README): update & formatting
* chore(npm): update package repo & bugs url
* chore: apply suggestions
* chore: revert promise chain flattening

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
diff --git a/README.md b/README.md
index 4ccee34..718b950 100644
--- a/README.md
+++ b/README.md
@@ -19,9 +19,10 @@
 #
 -->
 
+[![Node CI](https://github.com/apache/cordova-fetch/workflows/Node%20CI/badge.svg?branch=master)](https://github.com/apache/cordova-fetch/actions?query=branch%3Amaster)
 [![NPM](https://nodei.co/npm/cordova-fetch.png)](https://nodei.co/npm/cordova-fetch/)
 
-# cordova-fetch [![Travis Badge]][Travis] [![AppVeyor Badge]][AppVeyor]
+# cordova-fetch
 
 This package can be used to install and uninstall Node.js packages using npm.
 
@@ -29,7 +30,16 @@
 
 ### `fetch`
 
-Installs a module from npm, a git url or the local file system. Returns a `Promise` resolving to the absolute path to the installed package.
+Installs a module from:
+
+* `npm` registry
+* `git` url
+* `tarball`
+  * url
+  * file on local file system
+* `folder` path on local system
+
+Returns a `Promise` resolving to the absolute path of the installed package.
 
 ```js
 const fetch = require('cordova-fetch');
@@ -39,7 +49,7 @@
 });
 ```
 
-#### Parameters
+#### Fetch Parameters
 
 Parameter | Description
 -|-
@@ -47,7 +57,7 @@
 `dest` | Location where to install the package
 `opts` | Additional options (optional)
 
-##### Options
+##### Fetch Options
 
 Option | Default | Description
 -|-|-
@@ -55,7 +65,7 @@
 
 ### `uninstall`
 
-Uninstalls a package from given directory. Returns a `Promise` that resolves when removal has finished
+Uninstalls a package from the given directory. Returns a `Promise` that resolves when removal has finished.
 
 ```js
 const { uninstall } = require('cordova-fetch');
@@ -65,7 +75,7 @@
 });
 ```
 
-#### Parameters
+#### Uninstall Parameters
 
 Parameter | Description
 -|-
@@ -73,15 +83,8 @@
 `dest` | Location from where to uninstall the package
 `opts` | An Object with additional options
 
-##### Options
+##### Uninstall Options
 
 Option | Default | Description
 -|-|-
 `save` | `false` | Removes dependency from `package.json` iff `true`
-
-
-[Travis Badge]: https://travis-ci.org/apache/cordova-fetch.svg?branch=master
-[Travis]: https://travis-ci.org/apache/cordova-fetch
-
-[AppVeyor Badge]: https://ci.appveyor.com/api/projects/status/6xv212nihtcnbsov?svg=true
-[AppVeyor]: https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-fetch/branch/master
diff --git a/index.js b/index.js
index b17d3dc..5a09342 100644
--- a/index.js
+++ b/index.js
@@ -17,11 +17,9 @@
 
 const pify = require('pify');
 const which = pify(require('which'));
-var superspawn = require('cordova-common').superspawn;
-var events = require('cordova-common').events;
-var path = require('path');
-var fs = require('fs-extra');
-var CordovaError = require('cordova-common').CordovaError;
+const path = require('path');
+const fs = require('fs-extra');
+const { CordovaError, events, superspawn } = require('cordova-common');
 const resolve = pify(require('resolve'), { multiArgs: true });
 const npa = require('npm-package-arg');
 const semver = require('semver');
@@ -38,7 +36,7 @@
  */
 module.exports = function (target, dest, opts = {}) {
     return Promise.resolve()
-        .then(function () {
+        .then(() => {
             if (!dest || !target) {
                 throw new CordovaError('Need to supply a target and destination');
             }
@@ -49,7 +47,7 @@
             return pathToInstalledPackage(target, dest)
                 .catch(_ => installPackage(target, dest, opts));
         })
-        .catch(function (err) {
+        .catch(err => {
             throw new CordovaError(err);
         });
 };
@@ -90,7 +88,7 @@
     const packageInfoLine = npmInstallOutput.split('\n')
         .find(line => line.startsWith('+ '));
     if (!packageInfoLine) {
-        throw new CordovaError('Could not determine package name from output:\n' + npmInstallOutput);
+        throw new CordovaError(`Could not determine package name from output:\n${npmInstallOutput}`);
     }
     return packageInfoLine.slice(2);
 }
@@ -152,13 +150,13 @@
  *
  * @return {Promise<string>}    Resolves when removal has finished
  */
-module.exports.uninstall = function (target, dest, opts) {
-    var fetchArgs = ['uninstall'];
+module.exports.uninstall = (target, dest, opts) => {
+    const fetchArgs = ['uninstall'];
     opts = opts || {};
 
     // check if npm is installed on the system
     return isNpmInstalled()
-        .then(function () {
+        .then(() => {
             if (dest && target) {
                 // add target to fetchArgs Array
                 fetchArgs.push(target);
@@ -178,7 +176,7 @@
             // from package.json if --save was used.
             return superspawn.spawn('npm', fetchArgs, opts);
         })
-        .catch(function (err) {
+        .catch(err => {
             throw new CordovaError(err);
         });
 };
diff --git a/package.json b/package.json
index 090d5fb..f8286fd 100644
--- a/package.json
+++ b/package.json
@@ -3,10 +3,8 @@
   "version": "3.0.0-dev",
   "description": "Apache Cordova fetch module. Fetches from git and npm.",
   "main": "index.js",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/apache/cordova-fetch"
-  },
+  "repository": "github:apache/cordova-fetch",
+  "bugs": "https://github.com/apache/cordova-fetch/issues",
   "keywords": [
     "cordova",
     "fetch",
@@ -16,10 +14,6 @@
   ],
   "author": "Apache Software Foundation",
   "license": "Apache-2.0",
-  "bugs": {
-    "url": "https://issues.apache.org/jira/browse/CB",
-    "email": "dev@cordova.apache.org"
-  },
   "dependencies": {
     "cordova-common": "^4.0.0",
     "fs-extra": "^9.0.0",
diff --git a/spec/fetch-unit.spec.js b/spec/fetch-unit.spec.js
index 99922c3..e96f1e6 100644
--- a/spec/fetch-unit.spec.js
+++ b/spec/fetch-unit.spec.js
@@ -20,17 +20,17 @@
 const rewire = require('rewire');
 const { tmpDir: getTmpDir } = require('./helpers.js');
 
-describe('fetch', function () {
+describe('fetch', () => {
     let fetch, installPackage;
 
-    beforeEach(function () {
+    beforeEach(() => {
         fetch = rewire('..');
         installPackage = jasmine.createSpy()
             .and.returnValue(Promise.resolve('/foo'));
         fetch.__set__({ fs: { ensureDirSync: _ => _ }, installPackage });
     });
 
-    it('should return path to installed package', function () {
+    it('should return path to installed package', () => {
         fetch.__set__({ pathToInstalledPackage: _ => Promise.resolve('/foo') });
 
         return fetch('foo', 'bar').then(result => {
@@ -39,7 +39,7 @@
         });
     });
 
-    it('should install package if not found', function () {
+    it('should install package if not found', () => {
         fetch.__set__({ pathToInstalledPackage: _ => Promise.reject(new Error()) });
 
         return fetch('foo', 'bar').then(result => {
@@ -49,26 +49,26 @@
     });
 });
 
-describe('npmArgs', function () {
+describe('npmArgs', () => {
     const fetch = rewire('..');
     const npmArgs = fetch.__get__('npmArgs');
 
-    it('should handle missing options', function () {
+    it('should handle missing options', () => {
         npmArgs('platform');
     });
 
-    it('when save_exact is true, save-exact flag should be passed through to npm', function () {
-        var opts = { cwd: 'some/path', save_exact: true };
+    it('when save_exact is true, save-exact flag should be passed through to npm', () => {
+        const opts = { cwd: 'some/path', save_exact: true };
         expect(npmArgs('platform', opts)).toContain('--save-exact');
     });
 
-    it('when save is true, save-dev flag should be passed through to npm', function () {
-        var opts = { cwd: 'some/path', save: true };
+    it('when save is true, save-dev flag should be passed through to npm', () => {
+        const opts = { cwd: 'some/path', save: true };
         expect(npmArgs('platform', opts)).toContain('--save-dev');
     });
 
-    it('when save is false, no-save flag should be passed through to npm', function () {
-        var opts = { cwd: 'some/path', save: false };
+    it('when save is false, no-save flag should be passed through to npm', () => {
+        const opts = { cwd: 'some/path', save: false };
         expect(npmArgs('platform', opts)).toContain('--no-save');
     });
 });
@@ -93,7 +93,7 @@
     it('should gracefully handle if could not determine the package name from output', () => {
         expect(() => {
             getTargetPackageSpecFromNpmInstallOutput(wrongOutputSample);
-        }).toThrow(new Error('Could not determine package name from output:\n' + wrongOutputSample));
+        }).toThrow(new Error(`Could not determine package name from output:\n${wrongOutputSample}`));
     });
 });
 
diff --git a/spec/fetch.spec.js b/spec/fetch.spec.js
index 6e2adab..3aca6d9 100644
--- a/spec/fetch.spec.js
+++ b/spec/fetch.spec.js
@@ -15,23 +15,23 @@
     under the License.
 */
 
-var fetch = require('..');
-var uninstall = fetch.uninstall;
+const fetch = require('..');
+const uninstall = fetch.uninstall;
 
-var path = require('path');
-var fs = require('fs-extra');
-var fileUrl = require('file-url');
-var helpers = require('./helpers.js');
+const path = require('path');
+const fs = require('fs-extra');
+const fileUrl = require('file-url');
+const helpers = require('./helpers.js');
 
-var tmpDir, opts;
+let tmpDir, opts;
 
-beforeEach(function () {
+beforeEach(() => {
     opts = {};
     tmpDir = helpers.tmpDir();
     process.chdir(tmpDir);
 });
 
-afterEach(function () {
+afterEach(() => {
     process.chdir(__dirname); // Needed to rm the dir on Windows.
     fs.removeSync(tmpDir);
 });
@@ -60,8 +60,8 @@
     expect(rootPJ.devDependencies).toEqual(deps);
 }
 
-describe('fetch/uninstall tests via npm & git', function () {
-    it('should fetch and uninstall a cordova platform via npm & git', function () {
+describe('fetch/uninstall tests via npm & git', () => {
+    it('should fetch and uninstall a cordova platform via npm & git', () => {
         return Promise.resolve()
             .then(_ => fetchAndMatch('cordova-android'))
             .then(_ => uninstall('cordova-android', tmpDir, opts))
@@ -72,19 +72,19 @@
             .then(_ => expectNotToBeInstalled('cordova-browser'));
     }, 60000);
 
-    it('should fetch a scoped plugin from npm', function () {
+    it('should fetch a scoped plugin from npm', () => {
         return fetchAndMatch('@stevegill/cordova-plugin-device');
     }, 30000);
 });
 
-describe('fetch/uninstall with --save', function () {
-    beforeEach(function () {
+describe('fetch/uninstall with --save', () => {
+    beforeEach(() => {
         opts = { save: true };
         // copy package.json from spec directory to tmpDir
         fs.copySync(path.join(__dirname, 'testpkg.json'), 'package.json');
     });
 
-    it('should fetch and uninstall a cordova platform via npm & git tags/branches', function () {
+    it('should fetch and uninstall a cordova platform via npm & git tags/branches', () => {
         return Promise.resolve()
             // npm tag
             .then(_ => fetchAndMatch('cordova-android@8.1.0', {
@@ -115,7 +115,7 @@
             .then(_ => uninstall('cordova-android', tmpDir, opts));
     }, 150000);
 
-    it('should fetch and uninstall a cordova plugin via git commit sha', function () {
+    it('should fetch and uninstall a cordova plugin via git commit sha', () => {
         const URL = 'https://github.com/apache/cordova-plugin-contacts.git#7db612115755c2be73a98dda76ff4c5fd9d8a575';
         return Promise.resolve()
             .then(_ => fetchAndMatch(URL, {
@@ -129,32 +129,32 @@
     }, 30000);
 });
 
-describe('fetching already installed packages', function () {
-    beforeEach(function () {
+describe('fetching already installed packages', () => {
+    beforeEach(() => {
         fs.copySync(path.join(__dirname, 'support'), 'support');
     });
 
-    it('should return package path for registry packages', function () {
+    it('should return package path for registry packages', () => {
         return Promise.resolve()
             .then(_ => fetchAndMatch('cordova-plugin-device'))
             .then(_ => fetchAndMatch('cordova-plugin-device'));
     }, 40000);
 
-    it('should return package path if git repo name differs from plugin id', function () {
-        const TARGET = 'git+' + fileUrl(path.resolve(__dirname, 'support/repo-name-neq-plugin-id.git'));
+    it('should return package path if git repo name differs from plugin id', () => {
+        const TARGET = `git+${fileUrl(path.resolve(__dirname, 'support/repo-name-neq-plugin-id.git'))}`;
         return Promise.resolve()
             .then(_ => fetchAndMatch(TARGET, { name: 'test-plugin' }))
             .then(_ => fetchAndMatch(TARGET, { name: 'test-plugin' }));
     }, 40000);
 
-    it('should return package path if using a relative path', function () {
+    it('should return package path if using a relative path', () => {
         const TARGET = 'file:support/dummy-local-plugin';
         return Promise.resolve()
             .then(_ => fetchAndMatch(TARGET, { name: 'test-plugin' }))
             .then(_ => fetchAndMatch(TARGET, { name: 'test-plugin' }));
     }, 60000);
 
-    it('should return package path for git+http variants', function () {
+    it('should return package path for git+http variants', () => {
         return Promise.resolve()
             .then(_ => fetchAndMatch('github:apache/cordova-plugin-device', { name: 'cordova-plugin-device' }))
             .then(_ => fetchAndMatch('https://github.com/apache/cordova-plugin-device', { name: 'cordova-plugin-device' }))
@@ -162,22 +162,22 @@
     }, 60000);
 });
 
-describe('negative tests', function () {
-    it('should fail fetching a module that does not exist on npm', function () {
+describe('negative tests', () => {
+    it('should fail fetching a module that does not exist on npm', () => {
         return fetch('NOTAMODULE', tmpDir, opts)
-            .then(function (result) {
+            .then(result => {
                 fail('Expected promise to be rejected');
-            }, function (err) {
+            }, err => {
                 expect(err.message.code).toBe(1);
                 expect(err).toBeDefined();
             });
     }, 30000);
 
-    it('should fail fetching a giturl which contains a subdirectory', function () {
+    it('should fail fetching a giturl which contains a subdirectory', () => {
         return fetch('https://github.com/apache/cordova-plugins.git#:keyboard', tmpDir, opts)
-            .then(function (result) {
+            .then(result => {
                 fail('Expected promise to be rejected');
-            }, function (err) {
+            }, err => {
                 expect(err.message.code).toBe(1);
                 expect(err).toBeDefined();
             });
@@ -201,7 +201,7 @@
         fetchTarget = fileUrl(path.resolve('support/dummy-local-plugin'));
     });
 
-    it('should still install to given destination', function () {
+    it('should still install to given destination', () => {
         const expectedInstallPath = path.join(fetchDestination, 'node_modules/test-plugin');
 
         return fetch(fetchTarget, fetchDestination).then(pkgInstallPath => {
diff --git a/spec/helpers.js b/spec/helpers.js
index f6e7248..7d288c1 100644
--- a/spec/helpers.js
+++ b/spec/helpers.js
@@ -17,11 +17,9 @@
  under the License.
  */
 
-var path = require('path');
-var fs = require('fs-extra');
-var os = require('os');
+const path = require('path');
+const fs = require('fs-extra');
+const os = require('os');
 
 const tmpDirTemplate = path.join(os.tmpdir(), 'cordova-fetch-tests-');
-module.exports.tmpDir = function () {
-    return fs.mkdtempSync(tmpDirTemplate);
-};
+module.exports.tmpDir = () => fs.mkdtempSync(tmpDirTemplate);