Refactor fetch code
- Package installation was factored out to prepare for the next commit
- Verbose logging was altered
- Unit tests use less mocks and test subjects directly
Functionality should be completely unchanged.
diff --git a/index.js b/index.js
index d968711..4fd3a21 100644
--- a/index.js
+++ b/index.js
@@ -34,72 +34,75 @@
*
*/
module.exports = function (target, dest, opts = {}) {
- var fetchArgs = opts.link ? ['link'] : ['install'];
- var nodeModulesDir = dest;
-
- // check if npm is installed
- return module.exports.isNpmInstalled()
+ return Q()
.then(function () {
- if (dest && target) {
- // add target to fetchArgs Array
- fetchArgs.push(target);
-
- // append node_modules to nodeModulesDir if it doesn't come included
- if (path.basename(dest) !== 'node_modules') {
- nodeModulesDir = path.resolve(path.join(dest, 'node_modules'));
- }
- // create node_modules if it doesn't exist
- fs.ensureDirSync(nodeModulesDir);
- } else throw new CordovaError('Need to supply a target and destination');
-
- // set the directory where npm install will be run
- opts.cwd = dest;
-
- // npm should use production by default when install is npm run
- if ((opts.production) || (opts.production === undefined)) {
- fetchArgs.push('--production');
- opts.production = true;
+ if (!dest || !target) {
+ throw new CordovaError('Need to supply a target and destination');
}
-
- // if user added --save flag, pass it to npm install command
- if (opts.save_exact) {
- events.emit('verbose', 'saving exact');
- fetchArgs.push('--save-exact');
- } else if (opts.save) {
- events.emit('verbose', 'saving');
- fetchArgs.push('--save');
- } else {
- fetchArgs.push('--no-save');
- }
+ // Create dest if it doesn't exist yet
+ fs.ensureDirSync(dest);
})
- .then(function () {
- // install new module
- return superspawn.spawn('npm', fetchArgs, opts);
- })
- .then(extractPackageName)
- .then(pkgName => path.resolve(nodeModulesDir, pkgName))
+ .then(_ => installPackage(target, dest, opts))
.catch(function (err) {
throw new CordovaError(err);
});
};
-function extractPackageName (npmInstallOutput) {
+// Installs the package specified by target and returns the installation path
+function installPackage (target, dest, opts) {
+ return isNpmInstalled()
+ .then(_ => npmArgs(target, opts))
+ .then(args => {
+ events.emit('verbose', `fetch: Installing ${target} to ${dest}`);
+ return superspawn.spawn('npm', args, { cwd: dest });
+ })
+ .then(getTargetPackageSpecFromNpmInstallOutput)
+ .then(spec => pathToInstalledPackage(spec, dest));
+}
+
+function npmArgs (target, userOptions) {
+ const opts = Object.assign({ production: true }, userOptions);
+
+ const operation = opts.link ? 'link' : 'install';
+ const args = [operation, target];
+
+ if (opts.production) {
+ args.push('--production');
+ }
+ if (opts.save_exact) {
+ args.push('--save-exact');
+ } else if (opts.save) {
+ args.push('--save');
+ } else {
+ args.push('--no-save');
+ }
+ return args;
+}
+
+function getTargetPackageSpecFromNpmInstallOutput (npmInstallOutput) {
const lines = npmInstallOutput.split('\n');
- let spec;
if (lines[0].startsWith('+ ')) {
// npm >= 5
- spec = lines[0].slice(2);
+ return lines[0].slice(2);
} else if (lines[1].startsWith('└─') || lines[1].startsWith('`-')) {
// 3 <= npm <= 4
- spec = lines[1].slice(4);
+ return lines[1].slice(4).split(' ')[0];
} else {
throw new CordovaError('Could not determine package name from output:\n' + npmInstallOutput);
}
+}
+function pathToInstalledPackage (spec, dest) {
// Strip version from spec
const parts = spec.split('@');
const isScoped = parts.length > 1 && parts[0] === '';
- return isScoped ? '@' + parts[1] : parts[0];
+ const pkgName = isScoped ? '@' + parts[1] : parts[0];
+
+ // append node_modules to nodeModulesDir if it doesn't come included
+ const nodeModulesDir = path.basename(dest) === 'node_modules' ?
+ dest : path.resolve(path.join(dest, 'node_modules'));
+
+ return path.resolve(nodeModulesDir, pkgName);
}
/*