From 7103c00977880c106977e69ddcc33c15fabf3b07 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 00:06:05 +0100 Subject: [PATCH 01/15] Moderate refactor: Now uses Promises. Moved and improved tests. Moved files into lib folder. Added eslint and Changelog. --- .eslintrc | 31 ++++ CHANGELOG.md | 6 + index.js | 85 +-------- lib/do-npm-command.js | 84 +++++++++ errors.js => lib/errors.js | 15 +- reduceStream.js => lib/reduce-stream.js | 31 ++-- package.json | 17 +- test/basic.test.js | 164 ---------------- tests/acceptance/basic-test.js | 237 ++++++++++++++++++++++++ tests/runner.js | 50 +++++ tests/unit/eslint-test.js | 14 ++ 11 files changed, 466 insertions(+), 268 deletions(-) create mode 100644 .eslintrc create mode 100644 CHANGELOG.md create mode 100644 lib/do-npm-command.js rename errors.js => lib/errors.js (77%) rename reduceStream.js => lib/reduce-stream.js (63%) delete mode 100644 test/basic.test.js create mode 100644 tests/acceptance/basic-test.js create mode 100644 tests/runner.js create mode 100644 tests/unit/eslint-test.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8a76a46 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,31 @@ +{ + "parser": "babel-eslint", + "rules" : { + "strict" : 0, + "no-underscore-dangle" : 0, + "no-multi-spaces" : 0, + "key-spacing" : 0, + "no-shadow" : 0, + "consistent-return" : 2, + "no-use-before-define" : 2, + "new-parens" : 2, + "no-cond-assign" : 2, + "space-after-keywords" : 2, + "space-infix-ops" : 2, + "comma-dangle" : [2, "never"], + "no-multiple-empty-lines" : [2, {"max": 2}], + "quotes" : [2, "single"], + "eqeqeq" : [2, "smart"], + "wrap-iife" : [2, "outside"], + "indent" : [2, 2], + "brace-style" : [2, "1tbs"], + "spaced-line-comment" : [2, "always", {"exceptions":["-","+"]}], + "space-before-function-parentheses": [2, { + "anonymous" : "always", + "named" : "never" + }] + }, + "env": { + "node": true + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e764dd8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# enpeem Changelog + +### master +* [BREAKING ENHANCEMENT] Now requires at least Node 0.12.x or io.js 1.x.x +* [BREAKING ENHANCEMENT] All functions now return Promises. +* [ENHANCEMENT] Added eslint and improved tests diff --git a/index.js b/index.js index a0599cb..9af2be5 100644 --- a/index.js +++ b/index.js @@ -2,10 +2,7 @@ * Module dependencies */ -var concat = require('./reduceStream').concat; -var exec = require('child_process').exec; -var Err = require('./errors'); - +var doNpmCommand = require('./lib/do-npm-command'); module.exports = { @@ -17,7 +14,7 @@ module.exports = { * @param {Object} options * @param {Object|Function} cb */ - install: function(options, cb) { + install: function (options) { return doNpmCommand({ npmCommand: 'install', cmdArgs: options.dependencies, @@ -26,9 +23,9 @@ module.exports = { loglevel: options.loglevel || undefined, save: options.save || false, 'save-dev': options.saveDev || false, - prefix: options.prefix || undefined, + prefix: options.prefix || undefined } - }, cb); + }); }, /** @@ -36,7 +33,7 @@ module.exports = { * @param {Function} cb [description] * @return {[type]} [description] */ - update: function(options, cb) { + update: function (options) { return doNpmCommand({ npmCommand: 'update', path: options.path || '', @@ -44,76 +41,8 @@ module.exports = { cmdOptions: { production: options.production || false, loglevel: options.loglevel || undefined, - prefix: options.prefix || undefined, + prefix: options.prefix || undefined } - }, cb); + }); } }; - - - -function doNpmCommand(options, cb) { - cb = cb || function() {}; - - if (!options.npmCommand) { - return cb(new Error('`npmCommand` option is required')); - } - - // Defaults - options.cmdOptions = options.cmdOptions || {}; - options.cmdArgs = options.cmdArgs || []; - - // Check to make sure npm CLI is accessible - var NPM_V_OUTPUT = /^[0-9]+\.[0-9]+\.[0-9]+/; - var stdout$npm_v = exec('npm -v').stdout; - concat(stdout$npm_v, function(err, result) { - if (err) return cb(err); - if (typeof result !== 'string' || - !result.match(NPM_V_OUTPUT)) { - return cb(Err.cantFindNpm(result)); - } - - // Build command to execute - var cmd = ''; - cmd += 'npm ' + options.npmCommand + ' '; - cmd += options.cmdArgs.join(' '); - cmd += ' '; - - // if ('save' in options && options.save) { - // cmd += '--save '; - // } - // - // if ('saveDev' in options && options.saveDev) { - // cmd += '--save-dev '; - // } - // - // if (options.path.length > 0) { - // cmd += '--prefix ' + options.path + ' '; - // } - - for (var key in options.cmdOptions) { - // Skip undefined options - if (options.cmdOptions[key] !== undefined) { - cmd += '--' + key + '=' + options.cmdOptions[key] + ' '; - } - } - cmd += ''; - - // DRY: - // console.log('WOULD HAVE RUN::'); - // console.log(cmd); - - // Spin up child process - var npm = exec(cmd); - var stderr$npm = npm.stderr; - var stdout$npm = npm.stdout; - - // Watch in case anything goes wrong - stderr$npm.pipe(process.stderr); - - // When finished, trigger success cb - npm.on('exit', function onSuccess() { - cb(); - }); - }); -} diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js new file mode 100644 index 0000000..52a01f6 --- /dev/null +++ b/lib/do-npm-command.js @@ -0,0 +1,84 @@ +var exec = require('child_process').exec; + +var concat = require('./reduce-stream').concat; +var Err = require('./errors'); + +module.exports = function doNpmCommand(options) { + + if (!options.npmCommand) { + return new Promise(function (resolve, reject) { + reject(new Error('`npmCommand` option is required')); + }); + } + + // Defaults + options.cmdOptions = options.cmdOptions || {}; + options.cmdArgs = options.cmdArgs || []; + + // Check to make sure npm CLI is accessible + var NPM_V_OUTPUT = /^[0-9]+\.[0-9]+\.[0-9]+/; + var stdout$npmV = exec('npm -v').stdout; + + var promise = new Promise(function (resolve, reject) { + + concat(stdout$npmV, function (err, result) { + if (err) { + reject(err); + } + if (typeof result !== 'string' || + !result.match(NPM_V_OUTPUT)) { + return reject(Err.cantFindNpm(result)); + } + + // Build command to execute + var cmd = ''; + cmd += 'npm ' + options.npmCommand + ' '; + cmd += options.cmdArgs.join(' '); + cmd += ' '; + + // if ('save' in options && options.save) { + // cmd += '--save '; + // } + // + // if ('saveDev' in options && options.saveDev) { + // cmd += '--save-dev '; + // } + // + // if (options.path.length > 0) { + // cmd += '--prefix ' + options.path + ' '; + // } + + for (var key in options.cmdOptions) { + // Skip undefined options + if (options.cmdOptions[key] !== undefined) { + cmd += '--' + key + '=' + options.cmdOptions[key] + ' '; + } + } + cmd += ''; + + // DRY: + // console.log('WOULD HAVE RUN::'); + // console.log(cmd); + + // Spin up child process + var npm = exec(cmd); + var stderr$npm = npm.stderr; + // var stdout$npm = npm.stdout; + + // Watch in case anything goes wrong + stderr$npm.pipe(process.stderr); + + // When finished, trigger success cb + npm.on('exit', function onSuccess(exitCode) { + resolve(exitCode); + }); + + npm.on('error', function onError(error) { + reject(error); + }); + }); + + }); + + return promise; +}; diff --git a/errors.js b/lib/errors.js similarity index 77% rename from errors.js rename to lib/errors.js index 2ce2829..9718692 100644 --- a/errors.js +++ b/lib/errors.js @@ -1,21 +1,18 @@ +/*eslint indent:1*/ /** - * Module errors - * @type {Object} - */ +* Module errors +* @type {Object} +*/ module.exports = { - cantFindNpm: function ( consoleOutputFrom_npm_v ) { + cantFindNpm: function (/*consoleOutputFromNpmV*/) { return new Error( 'Couldn\'t install dependencies because `npm` could not be found. ' + '(Is it in your $PATH?)' + '\n' + - 'Anyways, you\'ll probably have to install them yourself with `npm install`.'+ - - '' + 'Anyways, you\'ll probably have to install them yourself with `npm install`.' // TODO: bring this stuff back // (would need to reduce both stderr and stdout stream to get nice output for the error msg) - // // '\n'+ // 'Here\'s what I got when I tried `npm -v`:' + '\n' + consoleOutputFrom_npm_v - // ); } }; diff --git a/reduceStream.js b/lib/reduce-stream.js similarity index 63% rename from reduceStream.js rename to lib/reduce-stream.js index 52d63fd..1d897bf 100644 --- a/reduceStream.js +++ b/lib/reduce-stream.js @@ -1,28 +1,28 @@ +/*eslint indent:1*/ + /** * Module dependencies */ -var exec = require('child_process').exec - , reduce = require('stream-reduce'); - +var reduce = require('stream-reduce'); /** * Reduce a stream of data into a single value. - * + * * @param {[type]} stream$ [description] * @param {Function} reducer [reducer object] * @param {Function} cb [description] * @return {[type]} [description] */ -function _reduceStream (stream$, reducer, cb) { +function _reduceStream(stream$, reducer, cb) { stream$ - .pipe(reduce(reducer.fn, reducer.memo)) - .once('data', function (reduction) { - cb(null, reduction); - }) - .once('error', cb); + .pipe(reduce(reducer.fn, reducer.memo)) + .once('data', function (reduction) { + cb(null, reduction); + }) + .once('error', cb); } @@ -31,10 +31,11 @@ function _reduceStream (stream$, reducer, cb) { * @param {Object} reducer [reducer definition] * @return {Function} */ -function _createReducer( reducer ) { - return function (stream$, cb) { +function _createReducer(reducer) { + var fn = function (stream$, cb) { _reduceStream(stream$, reducer, cb); }; + return fn; } @@ -46,14 +47,14 @@ module.exports = { /** * concat - * + * * Reduce a stream's emissions into a single string. */ concat: _createReducer({ - fn: function concat (memo, data) { + fn: function concat(memo, data) { return memo + data; }, memo: '' }) - + }; diff --git a/package.json b/package.json index 958b661..42c0137 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Lightweight wrapper for accessing npm programmatically (alternative to adding `npm` as a dependency)", "main": "index.js", "scripts": { - "test": "mocha -t 15000" + "test": "node tests/runner", + "autotest": "mocha --watch --reporter spec tests/**/*Test.js" }, "repository": { "type": "git", @@ -19,6 +20,10 @@ "offline", "cache" ], + "engines": { + "node": ">=0.12", + "iojs": ">=1.0.0" + }, "author": "Mike McNeil", "license": "MIT", "bugs": { @@ -29,6 +34,14 @@ "stream-reduce": "1.0.3" }, "devDependencies": { - "fs-extra": "~0.16.3" + "babel": "^4.7.16", + "babel-eslint": "^2.0.2", + "chai": "^2.2.0", + "chalk": "^1.0.0", + "fs-extra": "~0.16.3", + "glob": "^5.0.3", + "mocha": "^2.2.1", + "mocha-eslint": "^0.1.3", + "tmp-sync": "^1.0.1" } } diff --git a/test/basic.test.js b/test/basic.test.js deleted file mode 100644 index 6a6a4f3..0000000 --- a/test/basic.test.js +++ /dev/null @@ -1,164 +0,0 @@ -var assert = require ('assert'); -var fs = require('fs-extra'); -var path = require('path'); -var exec = require('child_process').exec; -var npm = require('../'); - -describe('basic usage', function () { - - function rimrafTestStuff (cb) { - this.pathToDep = path.resolve( - __dirname, - '../node_modules/lodash'); - - fs.remove(this.pathToDep, cb); - } - before(rimrafTestStuff); - after(rimrafTestStuff); - - it('should be requirable', function () { - this.npm = require('../'); - assert(this.npm); - }); - - it('should not crash', function (cb) { - this.npm.install({ - dependencies: ['lodash'], - production: true, - loglevel: 'silent' - }, cb); - }); - - it('should actually install things', function (cb) { - fs.lstat(this.pathToDep, cb); - }); - - -}); - - -describe('with more options', function (){ - - function rimrafTestStuff (cb) { - this.pathToDep = path.resolve( - __dirname, - '../node_modules/mocha'); - - fs.remove(this.pathToDep, cb); - } - before(rimrafTestStuff); - after(rimrafTestStuff); - - it('should not crash', function(cb) { - npm.install({ - dependencies: ['mocha'], - loglevel: 'silent', - production: true, - // 'min-cache': 999999999 - }, cb); - }); - - it('should actually install things', function (cb) { - fs.lstat(this.pathToDep, cb); - }); -}); - - -describe('with save option', function (){ - - function rimrafTestStuff (cb) { - this.pathToDep = path.resolve( - __dirname, - '../node_modules/mocha'); - this.pathToPackage = path.resolve( - __dirname, '../package.json' - ); - - //fs.remove(this.pathToDep, cb); - exec('npm rm mocha --save', cb); - } - before(rimrafTestStuff); - after(rimrafTestStuff); - - it('should not crash', function(cb) { - npm.install({ - dependencies: ['mocha'], - loglevel: 'silent', - production: false, - save: true - // 'min-cache': 999999999 - }, cb); - }); - - it('should actually install things', function (cb) { - //require does not work because of caching - var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).dependencies; - assert('mocha' in dependencies); - fs.lstat(this.pathToDep, cb); - }); -}); - - -describe('with save-dev option', function (){ - - function rimrafTestStuff (cb) { - this.pathToDep = path.resolve( - __dirname, - '../node_modules/string'); - this.pathToPackage = path.resolve( - __dirname, '../package.json' - ); - - //fs.remove(this.pathToDep, cb); - exec('npm rm string --save-dev', cb); - } - before(rimrafTestStuff); - after(rimrafTestStuff); - - it('should not crash', function(cb) { - npm.install({ - dependencies: ['string'], - loglevel: 'silent', - production: false, - saveDev: true, - // 'min-cache': 999999999 - }, cb); - }); - - it('should actually install things', function (cb) { - //require does not work because of caching - var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).devDependencies; - assert('string' in devDependencies); - fs.lstat(this.pathToDep, cb); - }); -}); - - -describe('with path option', function (){ - - function rimrafTestStuff (cb) { - this.pathToDep = path.resolve( - __dirname, - '../new-folder/node_modules/string'); - - fs.removeSync('new-folder'); - exec('npm rm string --save-dev', cb); - } - before(rimrafTestStuff); - after(rimrafTestStuff); - - it('should not crash', function(cb) { - npm.install({ - dependencies: ['string'], - loglevel: 'silent', - production: false, - prefix: 'new-folder', - // 'min-cache': 999999999 - }, cb); - }); - - it('should actually install things', function (cb) { - //require does not work because of caching - fs.lstat(this.pathToDep, cb); - }); -}); diff --git a/tests/acceptance/basic-test.js b/tests/acceptance/basic-test.js new file mode 100644 index 0000000..76d0f88 --- /dev/null +++ b/tests/acceptance/basic-test.js @@ -0,0 +1,237 @@ +/*eslint-env node, mocha, es6 */ +/*eslint indent:1*/ + +var path = require('path'); +var fs = require('fs-extra'); +var { expect } = require('chai'); +var tmp = require('tmp-sync'); +var npm = require('../../'); + +var root = process.cwd(); +var tmproot = path.join(root, 'tmp'); + +function getPathToDep(npmModule, customRoot) { + customRoot = customRoot || path.join('..', '..'); + + fs.copySync('package.json', path.join(customRoot, 'package.json')); + + var pathToDep = path.resolve( + __dirname, + path.join(customRoot, 'node_modules', npmModule)); + return pathToDep; +} + +describe('basic usage', function () { + var tmpdir; + var dependency = 'lodash'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should be requirable', function () { + expect(npm).to.exist; //eslint-disable-line no-unused-expressions + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + production: true, + loglevel: 'silent' + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(false, error).to.not.equal(false); + } + }); + + it('should actually install things', function () { + //lstatSync checks if a path exists + try { + var result = fs.lstatSync(this.pathToDep); + expect(result).to.be.an('object'); + } catch (error) { + expect(false, error).to.not.equal(false); + } + }); + + +}); + + +describe('with more options', function (){ + var tmpdir; + var dependency = 'mocha'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + loglevel: 'silent', + production: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + try { + var result = fs.lstatSync(this.pathToDep); + expect(result).to.be.an('object'); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); +}); + + +describe('with save option', function () { + var tmpdir; + var dependency = 'mocha'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: ['mocha'], + loglevel: 'silent', + production: false, + save: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + //require does not work because of caching + var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).dependencies; + expect(dependencies).to.include.keys('mocha'); + expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); + }); +}); + + +describe('with save-dev option', function () { + var tmpdir; + var dependency = 'string'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + loglevel: 'silent', + production: false, + saveDev: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + //require does not work because of caching + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).devDependencies; + expect(devDependencies).to.include.keys('string'); + expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); + }); +}); + + +describe('with prefix/custom path option', function () { + var tmpdir; + var prefixFolder = 'new-folder'; + var dependency = 'string'; + + before(function () { + tmpdir = tmp.in(tmproot); + // tmpdir = path.join(tmpdir, prefixFolder); + this.pathToDep = getPathToDep(dependency, path.join(tmpdir, prefixFolder)); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + loglevel: 'silent', + production: false, + prefix: prefixFolder + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + try { + var result = fs.lstatSync(this.pathToDep); + expect(result).to.be.an('object'); + } catch (error) { + expect(false, error).to.not.equal(false); + } + }); +}); diff --git a/tests/runner.js b/tests/runner.js new file mode 100644 index 0000000..120a6be --- /dev/null +++ b/tests/runner.js @@ -0,0 +1,50 @@ +/*eslint no-process-exit:0*/ +'use strict'; + +var glob = require('glob'); +var Mocha = require('mocha'); +var chalk = require('chalk'); + + +require('babel/register')({ + experimental: true, + loose: true +}); + +var mocha = new Mocha({ + // For some reason, tests take a long time on Windows (or at least AppVeyor) + timeout: (process.platform === 'win32') ? 90000 : 18000, + reporter: 'spec' +}); + +// Determine which tests to run based on argument passed to runner +var arg = process.argv[2]; +var root = ''; +var files = '/**/*-test.js'; +if (!arg) { + var root = 'tests/{unit,acceptance}'; +} else if (arg === 'lint') { + var root = 'tests/unit/'; + files = 'eslint-test.js'; +} else { + var root = 'tests/{unit,acceptance}'; +} + +function addFiles(mocha, files) { + glob.sync(root + files).forEach(mocha.addFile.bind(mocha)); +} + +addFiles(mocha, files); + +mocha.run(function (failures) { + process.on('exit', function () { + if (failures === 1) { + console.log(chalk.red('1 Failing Test')); + } else if (failures > 1) { + console.log(chalk.red(failures, 'Failing Tests')); + } else if (failures === 0) { + console.log(chalk.green('All Tests Passed!')); + } + process.exit(failures); + }); +}); diff --git a/tests/unit/eslint-test.js b/tests/unit/eslint-test.js new file mode 100644 index 0000000..48a5efe --- /dev/null +++ b/tests/unit/eslint-test.js @@ -0,0 +1,14 @@ +/*eslint-env node, mocha, es6 */ + +var lint = require('mocha-eslint'); + + +var paths = [ + 'index.js', + 'lib', + 'tests' +]; +var options = {}; +options.formatter = 'stylish'; + +lint(paths, options); From f74a96544a18774721d7913f21ae68fb6a552e23 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 00:29:42 +0100 Subject: [PATCH 02/15] Added options for new commands. Added docs and Markus Padourek to authors. --- index.js | 8 +++++++- lib/do-npm-command.js | 23 +++++++++++++++++++++++ package.json | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 9af2be5..efd4ced 100644 --- a/index.js +++ b/index.js @@ -18,6 +18,9 @@ module.exports = { return doNpmCommand({ npmCommand: 'install', cmdArgs: options.dependencies, + skip: options.skip || false, + dryRun: options.dryRun || false, + cmdPrefix: options.cmdPrefix || false, cmdOptions: { production: options.production || false, loglevel: options.loglevel || undefined, @@ -36,8 +39,11 @@ module.exports = { update: function (options) { return doNpmCommand({ npmCommand: 'update', - path: options.path || '', cmdArgs: [], + path: options.path || '', + skip: options.skip || false, + dryRun: options.dryRun || false, + cmdPrefix: options.cmdPrefix || false, cmdOptions: { production: options.production || false, loglevel: options.loglevel || undefined, diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index 52a01f6..ccc13f0 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -3,6 +3,29 @@ var exec = require('child_process').exec; var concat = require('./reduce-stream').concat; var Err = require('./errors'); +/** +* Run an npm command with the given options +* +* @method doNpmCommand +* @param {Object} options +* @param {String} options.npmCommand A command that npm recognizes. E.g. 'install' +* @param {Array} options.cmdArgs the arguments after the npmCommand. E.g. packagenames +* @param {Boolean} options.skip If true the function immediately returns. Useful for testing. +* @param {Boolean} options.dryRun If true, this adds the package to the package.json +* in the current folder or the ptions.npmCommand.prefix folder without installing +* @param {String} options.cmdPrefix Add a custom prefix to the command such as 'docker-compose run server' +* @param {Object} options.cmdOptions Has npm command options +* @param {Boolean} options.cmdOptions.production Turns on the --production flag, +* which is uesed to only install dependencies and excluding devDependencies +* @param {String} options.cmdOptions.loglevel Changes how much the command logs. E.g. 'silent' +* @param {Boolean} options.cmdOptions.save Turns on the --save flag, which saves +* the given dependency as 'dependencies' +* @param {Boolean} options.cmdOptions['save-dev'] Turning on the --save-dev flag, which +* saves the given dependency as 'devDependencies' +* @param {String} options.cmdOptions.prefix Adds the --prefix flag which lets npm install +* the dependecy into a custom path +* @return {Promise} Returns a Promise +*/ module.exports = function doNpmCommand(options) { if (!options.npmCommand) { diff --git a/package.json b/package.json index 42c0137..b18712d 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "node": ">=0.12", "iojs": ">=1.0.0" }, - "author": "Mike McNeil", + "author": "Mike McNeil, Markus Padourek", "license": "MIT", "bugs": { "url": "https://github.com/mikermcneil/enpeem/issues" From 1c67836f06bef4926d32c2a98796515762853522 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 00:38:56 +0100 Subject: [PATCH 03/15] Added unit tests for skip command and if no npmCommand is provided. --- lib/do-npm-command.js | 22 ++++++++++------------ tests/unit/do-npm-command-test.js | 25 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 tests/unit/do-npm-command-test.js diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index ccc13f0..2d2d4b4 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -28,6 +28,12 @@ var Err = require('./errors'); */ module.exports = function doNpmCommand(options) { + if (options.skip === true) { + return new Promise(function (resolve, reject) { + resolve(0); + }); + } + if (!options.npmCommand) { return new Promise(function (resolve, reject) { reject(new Error('`npmCommand` option is required')); @@ -59,18 +65,6 @@ module.exports = function doNpmCommand(options) { cmd += options.cmdArgs.join(' '); cmd += ' '; - // if ('save' in options && options.save) { - // cmd += '--save '; - // } - // - // if ('saveDev' in options && options.saveDev) { - // cmd += '--save-dev '; - // } - // - // if (options.path.length > 0) { - // cmd += '--prefix ' + options.path + ' '; - // } - for (var key in options.cmdOptions) { // Skip undefined options if (options.cmdOptions[key] !== undefined) { @@ -79,6 +73,10 @@ module.exports = function doNpmCommand(options) { } cmd += ''; + if (options.cmdPrefix) { + cmd = cmdPrefix + ' ' + cmd; + } + // DRY: // console.log('WOULD HAVE RUN::'); // console.log(cmd); diff --git a/tests/unit/do-npm-command-test.js b/tests/unit/do-npm-command-test.js new file mode 100644 index 0000000..d0cce37 --- /dev/null +++ b/tests/unit/do-npm-command-test.js @@ -0,0 +1,25 @@ +/*eslint-env node, mocha, es6 */ + +var { expect } = require('chai'); + +var doNpmCommand = require('../../lib/do-npm-command'); + +describe('doNpmCommand', function () { + it('should exit with 0 on skip', async function () { + try { + var exitCode = await doNpmCommand({ skip: true}); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should error if no npmCommand is provided', async function () { + try { + var exitCode = await doNpmCommand(); + expect(false, 'Should have errored.').to.not.exist; //eslint-disable-line no-unused-expressions + } catch (error) { + expect(error.message).to.equal('`npmCommand` option is required'); + } + }); +}); From cc9939ff2792a13c8417fc89137afd15c431d691 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 00:44:02 +0100 Subject: [PATCH 04/15] Fixed unit tests. --- lib/do-npm-command.js | 8 +++++--- tests/unit/do-npm-command-test.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index 2d2d4b4..a200590 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -28,8 +28,10 @@ var Err = require('./errors'); */ module.exports = function doNpmCommand(options) { - if (options.skip === true) { - return new Promise(function (resolve, reject) { + options = options || {}; + + if ('skip' in options && options.skip === true) { + return new Promise(function (resolve) { resolve(0); }); } @@ -74,7 +76,7 @@ module.exports = function doNpmCommand(options) { cmd += ''; if (options.cmdPrefix) { - cmd = cmdPrefix + ' ' + cmd; + cmd = options.cmdPrefix + ' ' + cmd; } // DRY: diff --git a/tests/unit/do-npm-command-test.js b/tests/unit/do-npm-command-test.js index d0cce37..b27391b 100644 --- a/tests/unit/do-npm-command-test.js +++ b/tests/unit/do-npm-command-test.js @@ -17,7 +17,7 @@ describe('doNpmCommand', function () { it('should error if no npmCommand is provided', async function () { try { var exitCode = await doNpmCommand(); - expect(false, 'Should have errored.').to.not.exist; //eslint-disable-line no-unused-expressions + expect(exitCode, 'Should have errored.').to.not.exist; //eslint-disable-line no-unused-expressions } catch (error) { expect(error.message).to.equal('`npmCommand` option is required'); } From bab5351dc0e21759e143da7b104e1229699e28c2 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 00:59:41 +0100 Subject: [PATCH 05/15] Simplified npm check by using which.sync --- lib/do-npm-command.js | 87 +++++++++++++++++++++---------------------- lib/errors.js | 6 +-- lib/reduce-stream.js | 60 ----------------------------- package.json | 2 +- 4 files changed, 44 insertions(+), 111 deletions(-) delete mode 100644 lib/reduce-stream.js diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index a200590..4813e0d 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -1,6 +1,6 @@ -var exec = require('child_process').exec; +var { exec } = require('child_process'); +var whichSync = require('which').sync; -var concat = require('./reduce-stream').concat; var Err = require('./errors'); /** @@ -47,61 +47,58 @@ module.exports = function doNpmCommand(options) { options.cmdArgs = options.cmdArgs || []; // Check to make sure npm CLI is accessible - var NPM_V_OUTPUT = /^[0-9]+\.[0-9]+\.[0-9]+/; - var stdout$npmV = exec('npm -v').stdout; + var npmVersion; + try { + whichSync('npm'); + } catch (error) { + return new Promise(function (resolve, reject) { + reject(Err.cantFindNpm()); + }); + } var promise = new Promise(function (resolve, reject) { - concat(stdout$npmV, function (err, result) { - if (err) { - reject(err); - } - if (typeof result !== 'string' || - !result.match(NPM_V_OUTPUT)) { - return reject(Err.cantFindNpm(result)); - } + // Build command to execute + var cmd = ''; + cmd += 'npm ' + options.npmCommand + ' '; + cmd += options.cmdArgs.join(' '); + cmd += ' '; - // Build command to execute - var cmd = ''; - cmd += 'npm ' + options.npmCommand + ' '; - cmd += options.cmdArgs.join(' '); - cmd += ' '; - - for (var key in options.cmdOptions) { - // Skip undefined options - if (options.cmdOptions[key] !== undefined) { - cmd += '--' + key + '=' + options.cmdOptions[key] + ' '; - } + for (var key in options.cmdOptions) { + // Skip undefined options + if (options.cmdOptions[key] !== undefined) { + cmd += '--' + key + '=' + options.cmdOptions[key] + ' '; } - cmd += ''; + } + cmd += ''; - if (options.cmdPrefix) { - cmd = options.cmdPrefix + ' ' + cmd; - } - - // DRY: - // console.log('WOULD HAVE RUN::'); - // console.log(cmd); + if (options.cmdPrefix) { + cmd = options.cmdPrefix + ' ' + cmd; + } - // Spin up child process - var npm = exec(cmd); - var stderr$npm = npm.stderr; - // var stdout$npm = npm.stdout; + // DRY: + // console.log('WOULD HAVE RUN::'); + // console.log(cmd); - // Watch in case anything goes wrong - stderr$npm.pipe(process.stderr); + // Spin up child process + var npm = exec(cmd); + var stderr$npm = npm.stderr; + // var stdout$npm = npm.stdout; - // When finished, trigger success cb - npm.on('exit', function onSuccess(exitCode) { - resolve(exitCode); - }); + // Watch in case anything goes wrong + stderr$npm.pipe(process.stderr); - npm.on('error', function onError(error) { - reject(error); - }); + // When finished, trigger success cb + npm.on('exit', function onSuccess(exitCode) { + resolve(exitCode); }); + npm.on('error', function onError(error) { + reject(error); + }); }); - return promise; +}); + +return promise; }; diff --git a/lib/errors.js b/lib/errors.js index 9718692..0d9af9d 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -4,15 +4,11 @@ * @type {Object} */ module.exports = { - cantFindNpm: function (/*consoleOutputFromNpmV*/) { + cantFindNpm: function () { return new Error( 'Couldn\'t install dependencies because `npm` could not be found. ' + '(Is it in your $PATH?)' + '\n' + 'Anyways, you\'ll probably have to install them yourself with `npm install`.' - // TODO: bring this stuff back - // (would need to reduce both stderr and stdout stream to get nice output for the error msg) - // '\n'+ - // 'Here\'s what I got when I tried `npm -v`:' + '\n' + consoleOutputFrom_npm_v ); } }; diff --git a/lib/reduce-stream.js b/lib/reduce-stream.js deleted file mode 100644 index 1d897bf..0000000 --- a/lib/reduce-stream.js +++ /dev/null @@ -1,60 +0,0 @@ -/*eslint indent:1*/ - -/** - * Module dependencies - */ - -var reduce = require('stream-reduce'); - - -/** - * Reduce a stream of data into a single value. - * - * @param {[type]} stream$ [description] - * @param {Function} reducer [reducer object] - * @param {Function} cb [description] - * @return {[type]} [description] - */ -function _reduceStream(stream$, reducer, cb) { - - stream$ - .pipe(reduce(reducer.fn, reducer.memo)) - .once('data', function (reduction) { - cb(null, reduction); - }) - .once('error', cb); -} - - -/** - * Returns a reduction function. - * @param {Object} reducer [reducer definition] - * @return {Function} - */ -function _createReducer(reducer) { - var fn = function (stream$, cb) { - _reduceStream(stream$, reducer, cb); - }; - return fn; -} - - -/** - * Available reduction methods - * @type {Object} - */ -module.exports = { - - /** - * concat - * - * Reduce a stream's emissions into a single string. - */ - concat: _createReducer({ - fn: function concat(memo, data) { - return memo + data; - }, - memo: '' - }) - -}; diff --git a/package.json b/package.json index b18712d..9da5a5f 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/mikermcneil/enpeem", "dependencies": { - "stream-reduce": "1.0.3" + "which": "^1.0.9" }, "devDependencies": { "babel": "^4.7.16", From 5f66f066ba443003ddbf6b0d3f5e1087502728cf Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 02:03:25 +0100 Subject: [PATCH 06/15] Added dry-run mmodule and unit tests. --- lib/do-npm-command.js | 16 +++++-- lib/install-dry-run.js | 54 +++++++++++++++++++++ package.json | 2 +- tests/runner.js | 8 ++-- tests/unit/install-dry-run-test.js | 76 ++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 lib/install-dry-run.js create mode 100644 tests/unit/install-dry-run-test.js diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index 4813e0d..b45f60c 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -2,6 +2,7 @@ var { exec } = require('child_process'); var whichSync = require('which').sync; var Err = require('./errors'); +var installDryRun = require('./install-dry-run'); /** * Run an npm command with the given options @@ -46,8 +47,17 @@ module.exports = function doNpmCommand(options) { options.cmdOptions = options.cmdOptions || {}; options.cmdArgs = options.cmdArgs || []; + if ('dryRun' in options && options.dryRun === true) { + if (options.npmCommand === 'install') { + installDryRun(options.cmdArgs, options.cmdOptions); + } else { + return new Promise(function (resolve) { + resolve(0); + }); + } + } + // Check to make sure npm CLI is accessible - var npmVersion; try { whichSync('npm'); } catch (error) { @@ -98,7 +108,5 @@ module.exports = function doNpmCommand(options) { }); }); -}); - -return promise; + return promise; }; diff --git a/lib/install-dry-run.js b/lib/install-dry-run.js new file mode 100644 index 0000000..66a586e --- /dev/null +++ b/lib/install-dry-run.js @@ -0,0 +1,54 @@ +var path = require('path'); +var fs = require('fs'); + +/** +* 'Simulates' and install command by just adding the package to the package.json +* is --save, or --save-dev is provided. +* +* @method installDryRun +* @param {Object} options +* @param {Array} dependencies the arguments after the npmCommand. E.g. packagenames +* @param {Object} cmdOptions Has npm command options +* @param {Boolean} cmdOptions.save Turns on the --save flag, which saves +* the given dependency as 'dependencies' +* @param {Boolean} cmdOptions['save-dev'] Turning on the --save-dev flag, which +* saves the given dependency as 'devDependencies' +* @param {String} cmdOptions.prefix Adds the --prefix flag which lets npm install +* the dependecy into a custom path +* @return {Boolean} +*/ + +module.exports = function installDryRun(dependencies, cmdOptions) { + cmdOptions = cmdOptions || {}; + + if (cmdOptions.save || cmdOptions['save-dev']) { + var prefix = cmdOptions.prefix || ''; + var fileName = path.join(process.cwd(), prefix, 'package.json'); + var packageInfo = fs.readFileSync(fileName); + var content = JSON.parse(packageInfo); + var packageToSave = ''; + var versionToSave = ''; + + for (var i = 0; i < dependencies.length; i++) { + var atPos = dependencies[i].indexOf('@'); + if (atPos > -1) { + //cut out the @ and use the semVer instead of the * + packageToSave = dependencies[i].slice(0, atPos); + versionToSave = dependencies[i].slice(atPos + 1); + } else { + packageToSave = dependencies[i]; + versionToSave = '*'; + } + + if (cmdOptions.save) { + content.dependencies[packageToSave] = versionToSave; + } else if (cmdOptions['save-dev']) { + content.devDependencies[packageToSave] = versionToSave; + } + } + + fs.writeFileSync(fileName, JSON.stringify(content, null, 2)); + return true; + } + return true; +}; diff --git a/package.json b/package.json index 9da5a5f..e0f2eff 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "babel-eslint": "^2.0.2", "chai": "^2.2.0", "chalk": "^1.0.0", - "fs-extra": "~0.16.3", + "fs-extra": "^0.18.0", "glob": "^5.0.3", "mocha": "^2.2.1", "mocha-eslint": "^0.1.3", diff --git a/tests/runner.js b/tests/runner.js index 120a6be..6f2b857 100644 --- a/tests/runner.js +++ b/tests/runner.js @@ -22,12 +22,14 @@ var arg = process.argv[2]; var root = ''; var files = '/**/*-test.js'; if (!arg) { - var root = 'tests/{unit,acceptance}'; + root = 'tests/{unit,acceptance}'; } else if (arg === 'lint') { - var root = 'tests/unit/'; + root = 'tests/unit/'; files = 'eslint-test.js'; +} else if (arg === 'unit') { + root = 'tests/unit'; } else { - var root = 'tests/{unit,acceptance}'; + root = 'tests/{unit,acceptance}'; } function addFiles(mocha, files) { diff --git a/tests/unit/install-dry-run-test.js b/tests/unit/install-dry-run-test.js new file mode 100644 index 0000000..4efd9f5 --- /dev/null +++ b/tests/unit/install-dry-run-test.js @@ -0,0 +1,76 @@ +/*eslint-env node, mocha, es6 */ + +var path = require('path'); +var fs = require('fs-extra'); +var { expect } = require('chai'); +var tmp = require('tmp-sync'); + +var installDryRun = require('../../lib/install-dry-run'); + +var root = process.cwd(); +var tmproot = path.join(root, 'tmp'); + +describe('installDryRun', function () { + var tmpdir; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToPackage = path.join(tmpdir, 'package.json'); + fs.copySync('package.json', this.pathToPackage); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should write to dependencies with save option', async function () { + var result = installDryRun(['string', 'lodash'], { save: true }); + expect(result).to.equal(true); + + var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).dependencies; + expect(dependencies).to.include.keys(['string', 'lodash']); + expect(dependencies).to.include({ 'string': '*', 'lodash': '*' }); + }); + + it('should write to devDependencies with saveDev option', async function () { + installDryRun(['string'], { 'save-dev': true }); + + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; + expect(devDependencies).to.include.keys('string'); + expect(devDependencies).to.include({ 'string': '*' }); + }); + + it('should save with the right version', async function () { + installDryRun(['string@3.1.1', 'lodash@^3.6.0'], { 'save-dev': true }); + + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; + expect(devDependencies).to.include.keys(['string', 'lodash']); + expect(devDependencies).to.include({ 'string': '3.1.1', 'lodash': '^3.6.0' }); + }); + + it('should save to prefixed folder', async function () { + var prefixFolder = 'new-folder'; + + var pathToPackage = path.join(tmpdir, prefixFolder, 'package.json'); + fs.copySync('package.json', pathToPackage); + + installDryRun(['string@3.1.1', 'sane-cli'], { save: true, prefix: prefixFolder }); + + var dependencies = JSON.parse(fs.readFileSync(pathToPackage)).dependencies; + expect(dependencies).to.include({ 'string': '3.1.1', 'sane-cli': '*' }); + }); + + it('should do nothing without save or saveDev option', async function () { + var result = installDryRun(['sails']); + + expect(result).to.equal(true); + + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; + expect(devDependencies).to.not.include.keys('sails'); + + var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).dependencies; + expect(dependencies).to.not.include.keys('sails'); + }); +}); From 17a4e5ab96809d25f4b9c44863e1f5e4c8851669 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 02:08:48 +0100 Subject: [PATCH 07/15] Added dry-run mmodule and unit tests. --- tests/acceptance/basic-test.js | 50 ++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/tests/acceptance/basic-test.js b/tests/acceptance/basic-test.js index 76d0f88..20e1e5a 100644 --- a/tests/acceptance/basic-test.js +++ b/tests/acceptance/basic-test.js @@ -9,6 +9,7 @@ var npm = require('../../'); var root = process.cwd(); var tmproot = path.join(root, 'tmp'); +var tmpdir; function getPathToDep(npmModule, customRoot) { customRoot = customRoot || path.join('..', '..'); @@ -22,7 +23,6 @@ function getPathToDep(npmModule, customRoot) { } describe('basic usage', function () { - var tmpdir; var dependency = 'lodash'; before(function () { @@ -63,12 +63,10 @@ describe('basic usage', function () { } }); - }); describe('with more options', function (){ - var tmpdir; var dependency = 'mocha'; before(function () { @@ -108,7 +106,6 @@ describe('with more options', function (){ describe('with save option', function () { - var tmpdir; var dependency = 'mocha'; before(function () { @@ -150,7 +147,6 @@ describe('with save option', function () { describe('with save-dev option', function () { - var tmpdir; var dependency = 'string'; before(function () { @@ -192,7 +188,6 @@ describe('with save-dev option', function () { describe('with prefix/custom path option', function () { - var tmpdir; var prefixFolder = 'new-folder'; var dependency = 'string'; @@ -235,3 +230,46 @@ describe('with prefix/custom path option', function () { } }); }); + +describe('with dryRun option', function () { + var dependencies = ['sane-cli@^0.0.24', 'koa']; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: dependencies, + loglevel: 'silent', + production: false, + saveDev: true, + dryRun: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should save to package.json but not install things', function () { + //require does not work because of caching + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; + expect(devDependencies).to.include({ 'sane-cli': '^0.0.24', 'koa': '*' }); + console.log(fs.lstatSync(this.pathToDep)); + // expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); + }); + +}); From ea5de85bdd3dbb9db7b8facbb1e297f7c205e4de Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 02:30:21 +0100 Subject: [PATCH 08/15] Added acceptance test for dryRun. --- .gitignore | 1 + README.md | 10 +++++++--- lib/do-npm-command.js | 9 ++++++++- lib/install-dry-run.js | 12 ++++++++++-- tests/acceptance/basic-test.js | 10 +++++++--- tests/runner.js | 2 ++ 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index a72b52e..67f944f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ results npm-debug.log node_modules +tmp diff --git a/README.md b/README.md index c3f9749..95a9842 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ $ npm install enpeem --save var npm = require('enpeem'); ``` +For all supported options look into the `index.js` `install` and `update` function. + #### npm install ```javascript @@ -31,7 +33,11 @@ npm.install({ 'sails@0.10.1', 'sails-disk@git://github.com/balderdashy/sails-disk.git#associations', 'lodash' - ] + ], + prefix: 'custom/path/to/install', + saveDev: true, //--save-dev flag + //saves package to package.json without installing. Only works with save/saveDev option + dryRun: true, loglevel: 'silent', 'cache-min': 999999999 }, function (err) { /* ... */ }); @@ -45,5 +51,3 @@ npm.update({ loglevel: 'silent' }, function (err) { /* ... */ }); ``` - - diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index b45f60c..38621b1 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -49,7 +49,14 @@ module.exports = function doNpmCommand(options) { if ('dryRun' in options && options.dryRun === true) { if (options.npmCommand === 'install') { - installDryRun(options.cmdArgs, options.cmdOptions); + var result = installDryRun(options.cmdArgs, options.cmdOptions); + return new Promise(function (resolve, reject) { + if (result === true) { + resolve(0); + } else { + reject(result); + } + }); } else { return new Promise(function (resolve) { resolve(0); diff --git a/lib/install-dry-run.js b/lib/install-dry-run.js index 66a586e..62384fe 100644 --- a/lib/install-dry-run.js +++ b/lib/install-dry-run.js @@ -24,7 +24,11 @@ module.exports = function installDryRun(dependencies, cmdOptions) { if (cmdOptions.save || cmdOptions['save-dev']) { var prefix = cmdOptions.prefix || ''; var fileName = path.join(process.cwd(), prefix, 'package.json'); - var packageInfo = fs.readFileSync(fileName); + try { + var packageInfo = fs.readFileSync(fileName); + } catch (error) { + return error; + } var content = JSON.parse(packageInfo); var packageToSave = ''; var versionToSave = ''; @@ -47,7 +51,11 @@ module.exports = function installDryRun(dependencies, cmdOptions) { } } - fs.writeFileSync(fileName, JSON.stringify(content, null, 2)); + try { + fs.writeFileSync(fileName, JSON.stringify(content, null, 2)); + } catch (error) { + return error; + } return true; } return true; diff --git a/tests/acceptance/basic-test.js b/tests/acceptance/basic-test.js index 20e1e5a..a4c5932 100644 --- a/tests/acceptance/basic-test.js +++ b/tests/acceptance/basic-test.js @@ -236,7 +236,7 @@ describe('with dryRun option', function () { before(function () { tmpdir = tmp.in(tmproot); - this.pathToDep = getPathToDep(dependency, tmpdir); + this.pathToDep = getPathToDep(dependencies[1], tmpdir); this.pathToPackage = path.resolve( __dirname, tmpdir, 'package.json' ); @@ -268,8 +268,12 @@ describe('with dryRun option', function () { //require does not work because of caching var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; expect(devDependencies).to.include({ 'sane-cli': '^0.0.24', 'koa': '*' }); - console.log(fs.lstatSync(this.pathToDep)); - // expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); + try { + var result = fs.lstatSync(this.pathToDep); + expect(result, 'The module folder should not exist.').to.not.exist; //eslint-disable-line no-unused-expressions + } catch (error) { + expect(error.message).to.include('no such file or directory'); + } }); }); diff --git a/tests/runner.js b/tests/runner.js index 6f2b857..1daf127 100644 --- a/tests/runner.js +++ b/tests/runner.js @@ -28,6 +28,8 @@ if (!arg) { files = 'eslint-test.js'; } else if (arg === 'unit') { root = 'tests/unit'; +} else if (arg === 'acceptance') { + root = 'tests/acceptance'; } else { root = 'tests/{unit,acceptance}'; } From d8b8398aca6e03626159873a3a11df05a1bff7dd Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 02:31:50 +0100 Subject: [PATCH 09/15] Clarified readme. Fixes #3. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 95a9842..f46a0a0 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ $ npm install enpeem --save var npm = require('enpeem'); ``` -For all supported options look into the `index.js` `install` and `update` function. +For all supported options look into the `index.js` file, specifically the `install` and `update` function. #### npm install From aaf66443fc7e26a736f9a2a8c59c1666ecf4d490 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Wed, 1 Apr 2015 02:37:12 +0100 Subject: [PATCH 10/15] Updated changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e764dd8..849f077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,4 @@ * [BREAKING ENHANCEMENT] Now requires at least Node 0.12.x or io.js 1.x.x * [BREAKING ENHANCEMENT] All functions now return Promises. * [ENHANCEMENT] Added eslint and improved tests +* [FEATURE] Added new `skip`, `dryRun` and `cmdPrefix` option. From 0118f6b3a15115a44952bff11169a1910f91e75a Mon Sep 17 00:00:00 2001 From: Globegitter Date: Thu, 2 Apr 2015 21:58:47 +0100 Subject: [PATCH 11/15] Fixed ES6 syntax. Improved readme and added cmdPrefix test. --- README.md | 18 +- lib/do-npm-command.js | 8 +- tests/acceptance/basic-test.js | 497 ++++++++++++++++++--------------- 3 files changed, 288 insertions(+), 235 deletions(-) diff --git a/README.md b/README.md index f46a0a0..0cebf51 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ For all supported options look into the `index.js` file, specifically the `insta #### npm install -```javascript +```js +// Returns a promise npm.install({ dependencies: [ 'sails@0.10.1', @@ -40,14 +41,23 @@ npm.install({ dryRun: true, loglevel: 'silent', 'cache-min': 999999999 -}, function (err) { /* ... */ }); +}).then(function (exitCode){ + //do stuff +}, function (error) { + //do stuff with the error +}); ``` #### npm update -```javascript +```js +// Returns a promise npm.update({ loglevel: 'silent' -}, function (err) { /* ... */ }); +}).then(function (exitCode){ + //do stuff +}, function (error) { + //do stuff with the error +}); ``` diff --git a/lib/do-npm-command.js b/lib/do-npm-command.js index 38621b1..a676848 100644 --- a/lib/do-npm-command.js +++ b/lib/do-npm-command.js @@ -1,4 +1,4 @@ -var { exec } = require('child_process'); +var exec = require('child_process').exec; var whichSync = require('which').sync; var Err = require('./errors'); @@ -107,7 +107,11 @@ module.exports = function doNpmCommand(options) { // When finished, trigger success cb npm.on('exit', function onSuccess(exitCode) { - resolve(exitCode); + if (exitCode === 0) { + resolve(exitCode); + } else { + reject(new Error('The command failed due to exit-code ' + exitCode)); + } }); npm.on('error', function onError(error) { diff --git a/tests/acceptance/basic-test.js b/tests/acceptance/basic-test.js index a4c5932..009f026 100644 --- a/tests/acceptance/basic-test.js +++ b/tests/acceptance/basic-test.js @@ -22,224 +22,271 @@ function getPathToDep(npmModule, customRoot) { return pathToDep; } -describe('basic usage', function () { - var dependency = 'lodash'; - - before(function () { - tmpdir = tmp.in(tmproot); - this.pathToDep = getPathToDep(dependency, tmpdir); - process.chdir(tmpdir); - }); - - after(function () { - process.chdir(root); - fs.removeSync(tmproot); - }); - - it('should be requirable', function () { - expect(npm).to.exist; //eslint-disable-line no-unused-expressions - }); - - it('should not crash', async function () { - try { - var exitCode = await npm.install({ - dependencies: [dependency], - production: true, - loglevel: 'silent' - }); - expect(exitCode).to.equal(0); - } catch (error) { - expect(false, error).to.not.equal(false); - } - }); - - it('should actually install things', function () { - //lstatSync checks if a path exists - try { - var result = fs.lstatSync(this.pathToDep); - expect(result).to.be.an('object'); - } catch (error) { - expect(false, error).to.not.equal(false); - } - }); - -}); - - -describe('with more options', function (){ - var dependency = 'mocha'; - - before(function () { - tmpdir = tmp.in(tmproot); - this.pathToDep = getPathToDep(dependency, tmpdir); - process.chdir(tmpdir); - }); - - after(function () { - process.chdir(root); - fs.removeSync(tmproot); - }); - - it('should not crash', async function () { - try { - var exitCode = await npm.install({ - dependencies: [dependency], - loglevel: 'silent', - production: true - // 'min-cache': 999999999 - }); - expect(exitCode).to.equal(0); - } catch (error) { - expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions - } - }); - - it('should actually install things', function () { - try { - var result = fs.lstatSync(this.pathToDep); - expect(result).to.be.an('object'); - } catch (error) { - expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions - } - }); -}); - - -describe('with save option', function () { - var dependency = 'mocha'; - - before(function () { - tmpdir = tmp.in(tmproot); - this.pathToDep = getPathToDep(dependency, tmpdir); - this.pathToPackage = path.resolve( - __dirname, tmpdir, 'package.json' - ); - process.chdir(tmpdir); - }); - - after(function () { - process.chdir(root); - fs.removeSync(tmproot); - }); - - it('should not crash', async function () { - try { - var exitCode = await npm.install({ - dependencies: ['mocha'], - loglevel: 'silent', - production: false, - save: true - // 'min-cache': 999999999 - }); - expect(exitCode).to.equal(0); - } catch (error) { - expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions - } - }); - - it('should actually install things', function () { - //require does not work because of caching - var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).dependencies; - expect(dependencies).to.include.keys('mocha'); - expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); - }); -}); - - -describe('with save-dev option', function () { - var dependency = 'string'; - - before(function () { - tmpdir = tmp.in(tmproot); - this.pathToDep = getPathToDep(dependency, tmpdir); - this.pathToPackage = path.resolve( - __dirname, tmpdir, 'package.json' - ); - process.chdir(tmpdir); - }); - - after(function () { - process.chdir(root); - fs.removeSync(tmproot); - }); - - it('should not crash', async function () { - try { - var exitCode = await npm.install({ - dependencies: [dependency], - loglevel: 'silent', - production: false, - saveDev: true - // 'min-cache': 999999999 - }); - expect(exitCode).to.equal(0); - } catch (error) { - expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions - } - }); - - it('should actually install things', function () { - //require does not work because of caching - var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).devDependencies; - expect(devDependencies).to.include.keys('string'); - expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); - }); -}); - - -describe('with prefix/custom path option', function () { - var prefixFolder = 'new-folder'; - var dependency = 'string'; - - before(function () { - tmpdir = tmp.in(tmproot); - // tmpdir = path.join(tmpdir, prefixFolder); - this.pathToDep = getPathToDep(dependency, path.join(tmpdir, prefixFolder)); - this.pathToPackage = path.resolve( - __dirname, tmpdir, 'package.json' - ); - process.chdir(tmpdir); - }); - - after(function () { - process.chdir(root); - fs.removeSync(tmproot); - }); - - it('should not crash', async function () { - try { - var exitCode = await npm.install({ - dependencies: [dependency], - loglevel: 'silent', - production: false, - prefix: prefixFolder - // 'min-cache': 999999999 - }); - expect(exitCode).to.equal(0); - } catch (error) { - expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions - } - }); - - it('should actually install things', function () { - try { - var result = fs.lstatSync(this.pathToDep); - expect(result).to.be.an('object'); - } catch (error) { - expect(false, error).to.not.equal(false); - } - }); -}); - -describe('with dryRun option', function () { +// describe('basic usage', function () { +// var dependency = 'lodash'; +// +// before(function () { +// tmpdir = tmp.in(tmproot); +// this.pathToDep = getPathToDep(dependency, tmpdir); +// process.chdir(tmpdir); +// }); +// +// after(function () { +// process.chdir(root); +// fs.removeSync(tmproot); +// }); +// +// it('should be requirable', function () { +// expect(npm).to.exist; //eslint-disable-line no-unused-expressions +// }); +// +// it('should not crash', async function () { +// try { +// var exitCode = await npm.install({ +// dependencies: [dependency], +// production: true, +// loglevel: 'silent' +// }); +// expect(exitCode).to.equal(0); +// } catch (error) { +// expect(false, error).to.not.equal(false); +// } +// }); +// +// it('should actually install things', function () { +// //lstatSync checks if a path exists +// try { +// var result = fs.lstatSync(this.pathToDep); +// expect(result).to.be.an('object'); +// } catch (error) { +// expect(false, error).to.not.equal(false); +// } +// }); +// +// }); +// +// +// describe('with more options', function (){ +// var dependency = 'mocha'; +// +// before(function () { +// tmpdir = tmp.in(tmproot); +// this.pathToDep = getPathToDep(dependency, tmpdir); +// process.chdir(tmpdir); +// }); +// +// after(function () { +// process.chdir(root); +// fs.removeSync(tmproot); +// }); +// +// it('should not crash', async function () { +// try { +// var exitCode = await npm.install({ +// dependencies: [dependency], +// loglevel: 'silent', +// production: true +// // 'min-cache': 999999999 +// }); +// expect(exitCode).to.equal(0); +// } catch (error) { +// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions +// } +// }); +// +// it('should actually install things', function () { +// try { +// var result = fs.lstatSync(this.pathToDep); +// expect(result).to.be.an('object'); +// } catch (error) { +// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions +// } +// }); +// }); +// +// +// describe('with save option', function () { +// var dependency = 'mocha'; +// +// before(function () { +// tmpdir = tmp.in(tmproot); +// this.pathToDep = getPathToDep(dependency, tmpdir); +// this.pathToPackage = path.resolve( +// __dirname, tmpdir, 'package.json' +// ); +// process.chdir(tmpdir); +// }); +// +// after(function () { +// process.chdir(root); +// fs.removeSync(tmproot); +// }); +// +// it('should not crash', async function () { +// try { +// var exitCode = await npm.install({ +// dependencies: ['mocha'], +// loglevel: 'silent', +// production: false, +// save: true +// // 'min-cache': 999999999 +// }); +// expect(exitCode).to.equal(0); +// } catch (error) { +// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions +// } +// }); +// +// it('should actually install things', function () { +// //require does not work because of caching +// var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).dependencies; +// expect(dependencies).to.include.keys('mocha'); +// expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); +// }); +// }); +// +// +// describe('with save-dev option', function () { +// var dependency = 'string'; +// +// before(function () { +// tmpdir = tmp.in(tmproot); +// this.pathToDep = getPathToDep(dependency, tmpdir); +// this.pathToPackage = path.resolve( +// __dirname, tmpdir, 'package.json' +// ); +// process.chdir(tmpdir); +// }); +// +// after(function () { +// process.chdir(root); +// fs.removeSync(tmproot); +// }); +// +// it('should not crash', async function () { +// try { +// var exitCode = await npm.install({ +// dependencies: [dependency], +// loglevel: 'silent', +// production: false, +// saveDev: true +// // 'min-cache': 999999999 +// }); +// expect(exitCode).to.equal(0); +// } catch (error) { +// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions +// } +// }); +// +// it('should actually install things', function () { +// //require does not work because of caching +// var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).devDependencies; +// expect(devDependencies).to.include.keys('string'); +// expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); +// }); +// }); +// +// +// describe('with prefix/custom path option', function () { +// var prefixFolder = 'new-folder'; +// var dependency = 'string'; +// +// before(function () { +// tmpdir = tmp.in(tmproot); +// // tmpdir = path.join(tmpdir, prefixFolder); +// this.pathToDep = getPathToDep(dependency, path.join(tmpdir, prefixFolder)); +// this.pathToPackage = path.resolve( +// __dirname, tmpdir, 'package.json' +// ); +// process.chdir(tmpdir); +// }); +// +// after(function () { +// process.chdir(root); +// fs.removeSync(tmproot); +// }); +// +// it('should not crash', async function () { +// try { +// var exitCode = await npm.install({ +// dependencies: [dependency], +// loglevel: 'silent', +// production: false, +// prefix: prefixFolder +// // 'min-cache': 999999999 +// }); +// expect(exitCode).to.equal(0); +// } catch (error) { +// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions +// } +// }); +// +// it('should actually install things', function () { +// try { +// var result = fs.lstatSync(this.pathToDep); +// expect(result).to.be.an('object'); +// } catch (error) { +// expect(false, error).to.not.equal(false); +// } +// }); +// }); +// +// describe('with dryRun option', function () { +// var dependencies = ['sane-cli@^0.0.24', 'koa']; +// +// before(function () { +// tmpdir = tmp.in(tmproot); +// this.pathToDep = getPathToDep(dependencies[1], tmpdir); +// this.pathToPackage = path.resolve( +// __dirname, tmpdir, 'package.json' +// ); +// process.chdir(tmpdir); +// }); +// +// after(function () { +// process.chdir(root); +// fs.removeSync(tmproot); +// }); +// +// it('should not crash', async function () { +// try { +// var exitCode = await npm.install({ +// dependencies: dependencies, +// loglevel: 'silent', +// production: false, +// saveDev: true, +// dryRun: true +// // 'min-cache': 999999999 +// }); +// expect(exitCode).to.equal(0); +// } catch (error) { +// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions +// } +// }); +// +// it('should save to package.json but not install things', function () { +// //require does not work because of caching +// var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; +// expect(devDependencies).to.include({ 'sane-cli': '^0.0.24', 'koa': '*' }); +// try { +// var result = fs.lstatSync(this.pathToDep); +// expect(result, 'The module folder should not exist.').to.not.exist; //eslint-disable-line no-unused-expressions +// } catch (error) { +// expect(error.message).to.include('no such file or directory'); +// } +// }); +// +// }); + +describe('with cmdPrefix option', function () { var dependencies = ['sane-cli@^0.0.24', 'koa']; before(function () { tmpdir = tmp.in(tmproot); this.pathToDep = getPathToDep(dependencies[1], tmpdir); - this.pathToPackage = path.resolve( - __dirname, tmpdir, 'package.json' - ); + // this.pathToPackage = path.resolve( + // __dirname, tmpdir, 'package.json' + // ); process.chdir(tmpdir); }); @@ -248,31 +295,23 @@ describe('with dryRun option', function () { fs.removeSync(tmproot); }); - it('should not crash', async function () { + it('should error since fictional-docker-compose is not installed', async function () { + //Note this is using fictional-docker-compose instead of docker-compse + //to make sure this test fails consistently on any machine + //For more info about docker-compose see: https://docs.docker.com/compose/ try { var exitCode = await npm.install({ dependencies: dependencies, loglevel: 'silent', production: false, saveDev: true, - dryRun: true + cmdPrefix: 'fictional-docker-compose run server' // 'min-cache': 999999999 }); - expect(exitCode).to.equal(0); - } catch (error) { - expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions - } - }); - - it('should save to package.json but not install things', function () { - //require does not work because of caching - var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; - expect(devDependencies).to.include({ 'sane-cli': '^0.0.24', 'koa': '*' }); - try { - var result = fs.lstatSync(this.pathToDep); - expect(result, 'The module folder should not exist.').to.not.exist; //eslint-disable-line no-unused-expressions + expect(exitCode).to.not.equal(127); + expect(false, 'The command should have failed').to.not.exist; //eslint-disable-line no-unused-expressions } catch (error) { - expect(error.message).to.include('no such file or directory'); + expect(error.message).to.equal('The command failed due to exit-code 127'); //eslint-disable-line no-unused-expressions } }); From 8dfbae15a8fae039c800d9ad24f33800959a2e24 Mon Sep 17 00:00:00 2001 From: Globegitter Date: Thu, 2 Apr 2015 22:11:01 +0100 Subject: [PATCH 12/15] Babel is now only transpiling files in the tests folder. --- tests/runner.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/runner.js b/tests/runner.js index 1daf127..9d561dd 100644 --- a/tests/runner.js +++ b/tests/runner.js @@ -8,7 +8,8 @@ var chalk = require('chalk'); require('babel/register')({ experimental: true, - loose: true + loose: true, + only: /tests/ }); var mocha = new Mocha({ From 5411e472e4a88c0763ccb4c8ceb09fa9d69735af Mon Sep 17 00:00:00 2001 From: Globegitter Date: Fri, 3 Apr 2015 14:42:41 +0100 Subject: [PATCH 13/15] Reactivated all tests. --- tests/acceptance/basic-test.js | 510 ++++++++++++++++----------------- 1 file changed, 255 insertions(+), 255 deletions(-) diff --git a/tests/acceptance/basic-test.js b/tests/acceptance/basic-test.js index 009f026..12dd11f 100644 --- a/tests/acceptance/basic-test.js +++ b/tests/acceptance/basic-test.js @@ -22,261 +22,261 @@ function getPathToDep(npmModule, customRoot) { return pathToDep; } -// describe('basic usage', function () { -// var dependency = 'lodash'; -// -// before(function () { -// tmpdir = tmp.in(tmproot); -// this.pathToDep = getPathToDep(dependency, tmpdir); -// process.chdir(tmpdir); -// }); -// -// after(function () { -// process.chdir(root); -// fs.removeSync(tmproot); -// }); -// -// it('should be requirable', function () { -// expect(npm).to.exist; //eslint-disable-line no-unused-expressions -// }); -// -// it('should not crash', async function () { -// try { -// var exitCode = await npm.install({ -// dependencies: [dependency], -// production: true, -// loglevel: 'silent' -// }); -// expect(exitCode).to.equal(0); -// } catch (error) { -// expect(false, error).to.not.equal(false); -// } -// }); -// -// it('should actually install things', function () { -// //lstatSync checks if a path exists -// try { -// var result = fs.lstatSync(this.pathToDep); -// expect(result).to.be.an('object'); -// } catch (error) { -// expect(false, error).to.not.equal(false); -// } -// }); -// -// }); -// -// -// describe('with more options', function (){ -// var dependency = 'mocha'; -// -// before(function () { -// tmpdir = tmp.in(tmproot); -// this.pathToDep = getPathToDep(dependency, tmpdir); -// process.chdir(tmpdir); -// }); -// -// after(function () { -// process.chdir(root); -// fs.removeSync(tmproot); -// }); -// -// it('should not crash', async function () { -// try { -// var exitCode = await npm.install({ -// dependencies: [dependency], -// loglevel: 'silent', -// production: true -// // 'min-cache': 999999999 -// }); -// expect(exitCode).to.equal(0); -// } catch (error) { -// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions -// } -// }); -// -// it('should actually install things', function () { -// try { -// var result = fs.lstatSync(this.pathToDep); -// expect(result).to.be.an('object'); -// } catch (error) { -// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions -// } -// }); -// }); -// -// -// describe('with save option', function () { -// var dependency = 'mocha'; -// -// before(function () { -// tmpdir = tmp.in(tmproot); -// this.pathToDep = getPathToDep(dependency, tmpdir); -// this.pathToPackage = path.resolve( -// __dirname, tmpdir, 'package.json' -// ); -// process.chdir(tmpdir); -// }); -// -// after(function () { -// process.chdir(root); -// fs.removeSync(tmproot); -// }); -// -// it('should not crash', async function () { -// try { -// var exitCode = await npm.install({ -// dependencies: ['mocha'], -// loglevel: 'silent', -// production: false, -// save: true -// // 'min-cache': 999999999 -// }); -// expect(exitCode).to.equal(0); -// } catch (error) { -// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions -// } -// }); -// -// it('should actually install things', function () { -// //require does not work because of caching -// var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).dependencies; -// expect(dependencies).to.include.keys('mocha'); -// expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); -// }); -// }); -// -// -// describe('with save-dev option', function () { -// var dependency = 'string'; -// -// before(function () { -// tmpdir = tmp.in(tmproot); -// this.pathToDep = getPathToDep(dependency, tmpdir); -// this.pathToPackage = path.resolve( -// __dirname, tmpdir, 'package.json' -// ); -// process.chdir(tmpdir); -// }); -// -// after(function () { -// process.chdir(root); -// fs.removeSync(tmproot); -// }); -// -// it('should not crash', async function () { -// try { -// var exitCode = await npm.install({ -// dependencies: [dependency], -// loglevel: 'silent', -// production: false, -// saveDev: true -// // 'min-cache': 999999999 -// }); -// expect(exitCode).to.equal(0); -// } catch (error) { -// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions -// } -// }); -// -// it('should actually install things', function () { -// //require does not work because of caching -// var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).devDependencies; -// expect(devDependencies).to.include.keys('string'); -// expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); -// }); -// }); -// -// -// describe('with prefix/custom path option', function () { -// var prefixFolder = 'new-folder'; -// var dependency = 'string'; -// -// before(function () { -// tmpdir = tmp.in(tmproot); -// // tmpdir = path.join(tmpdir, prefixFolder); -// this.pathToDep = getPathToDep(dependency, path.join(tmpdir, prefixFolder)); -// this.pathToPackage = path.resolve( -// __dirname, tmpdir, 'package.json' -// ); -// process.chdir(tmpdir); -// }); -// -// after(function () { -// process.chdir(root); -// fs.removeSync(tmproot); -// }); -// -// it('should not crash', async function () { -// try { -// var exitCode = await npm.install({ -// dependencies: [dependency], -// loglevel: 'silent', -// production: false, -// prefix: prefixFolder -// // 'min-cache': 999999999 -// }); -// expect(exitCode).to.equal(0); -// } catch (error) { -// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions -// } -// }); -// -// it('should actually install things', function () { -// try { -// var result = fs.lstatSync(this.pathToDep); -// expect(result).to.be.an('object'); -// } catch (error) { -// expect(false, error).to.not.equal(false); -// } -// }); -// }); -// -// describe('with dryRun option', function () { -// var dependencies = ['sane-cli@^0.0.24', 'koa']; -// -// before(function () { -// tmpdir = tmp.in(tmproot); -// this.pathToDep = getPathToDep(dependencies[1], tmpdir); -// this.pathToPackage = path.resolve( -// __dirname, tmpdir, 'package.json' -// ); -// process.chdir(tmpdir); -// }); -// -// after(function () { -// process.chdir(root); -// fs.removeSync(tmproot); -// }); -// -// it('should not crash', async function () { -// try { -// var exitCode = await npm.install({ -// dependencies: dependencies, -// loglevel: 'silent', -// production: false, -// saveDev: true, -// dryRun: true -// // 'min-cache': 999999999 -// }); -// expect(exitCode).to.equal(0); -// } catch (error) { -// expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions -// } -// }); -// -// it('should save to package.json but not install things', function () { -// //require does not work because of caching -// var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; -// expect(devDependencies).to.include({ 'sane-cli': '^0.0.24', 'koa': '*' }); -// try { -// var result = fs.lstatSync(this.pathToDep); -// expect(result, 'The module folder should not exist.').to.not.exist; //eslint-disable-line no-unused-expressions -// } catch (error) { -// expect(error.message).to.include('no such file or directory'); -// } -// }); -// -// }); +describe('basic usage', function () { + var dependency = 'lodash'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should be requirable', function () { + expect(npm).to.exist; //eslint-disable-line no-unused-expressions + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + production: true, + loglevel: 'silent' + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(false, error).to.not.equal(false); + } + }); + + it('should actually install things', function () { + //lstatSync checks if a path exists + try { + var result = fs.lstatSync(this.pathToDep); + expect(result).to.be.an('object'); + } catch (error) { + expect(false, error).to.not.equal(false); + } + }); + +}); + + +describe('with more options', function (){ + var dependency = 'mocha'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + loglevel: 'silent', + production: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + try { + var result = fs.lstatSync(this.pathToDep); + expect(result).to.be.an('object'); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); +}); + + +describe('with save option', function () { + var dependency = 'mocha'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: ['mocha'], + loglevel: 'silent', + production: false, + save: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + //require does not work because of caching + var dependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).dependencies; + expect(dependencies).to.include.keys('mocha'); + expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); + }); +}); + + +describe('with save-dev option', function () { + var dependency = 'string'; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependency, tmpdir); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + loglevel: 'silent', + production: false, + saveDev: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + //require does not work because of caching + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage), { encoding: 'utf8'}).devDependencies; + expect(devDependencies).to.include.keys('string'); + expect(fs.lstatSync(this.pathToDep)).to.be.an('object'); + }); +}); + + +describe('with prefix/custom path option', function () { + var prefixFolder = 'new-folder'; + var dependency = 'string'; + + before(function () { + tmpdir = tmp.in(tmproot); + // tmpdir = path.join(tmpdir, prefixFolder); + this.pathToDep = getPathToDep(dependency, path.join(tmpdir, prefixFolder)); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: [dependency], + loglevel: 'silent', + production: false, + prefix: prefixFolder + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should actually install things', function () { + try { + var result = fs.lstatSync(this.pathToDep); + expect(result).to.be.an('object'); + } catch (error) { + expect(false, error).to.not.equal(false); + } + }); +}); + +describe('with dryRun option', function () { + var dependencies = ['sane-cli@^0.0.24', 'koa']; + + before(function () { + tmpdir = tmp.in(tmproot); + this.pathToDep = getPathToDep(dependencies[1], tmpdir); + this.pathToPackage = path.resolve( + __dirname, tmpdir, 'package.json' + ); + process.chdir(tmpdir); + }); + + after(function () { + process.chdir(root); + fs.removeSync(tmproot); + }); + + it('should not crash', async function () { + try { + var exitCode = await npm.install({ + dependencies: dependencies, + loglevel: 'silent', + production: false, + saveDev: true, + dryRun: true + // 'min-cache': 999999999 + }); + expect(exitCode).to.equal(0); + } catch (error) { + expect(error, error).to.not.exist; //eslint-disable-line no-unused-expressions + } + }); + + it('should save to package.json but not install things', function () { + //require does not work because of caching + var devDependencies = JSON.parse(fs.readFileSync(this.pathToPackage)).devDependencies; + expect(devDependencies).to.include({ 'sane-cli': '^0.0.24', 'koa': '*' }); + try { + var result = fs.lstatSync(this.pathToDep); + expect(result, 'The module folder should not exist.').to.not.exist; //eslint-disable-line no-unused-expressions + } catch (error) { + expect(error.message).to.include('no such file or directory'); + } + }); + +}); describe('with cmdPrefix option', function () { var dependencies = ['sane-cli@^0.0.24', 'koa']; From c30e8a50ac3395624f6ff965050d1d6227aed9ea Mon Sep 17 00:00:00 2001 From: Globegitter Date: Fri, 3 Apr 2015 18:37:49 +0100 Subject: [PATCH 14/15] Added a separate .eslintrc for the tests folder using babel-eslint. --- .eslintrc | 1 - lib/install-dry-run.js | 2 +- tests/.eslintrc | 6 ++++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/.eslintrc diff --git a/.eslintrc b/.eslintrc index 8a76a46..1720304 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,4 @@ { - "parser": "babel-eslint", "rules" : { "strict" : 0, "no-underscore-dangle" : 0, diff --git a/lib/install-dry-run.js b/lib/install-dry-run.js index 62384fe..07389b8 100644 --- a/lib/install-dry-run.js +++ b/lib/install-dry-run.js @@ -36,7 +36,7 @@ module.exports = function installDryRun(dependencies, cmdOptions) { for (var i = 0; i < dependencies.length; i++) { var atPos = dependencies[i].indexOf('@'); if (atPos > -1) { - //cut out the @ and use the semVer instead of the * + // cut out the @ and use the semVer instead of the * packageToSave = dependencies[i].slice(0, atPos); versionToSave = dependencies[i].slice(atPos + 1); } else { diff --git a/tests/.eslintrc b/tests/.eslintrc new file mode 100644 index 0000000..3d94166 --- /dev/null +++ b/tests/.eslintrc @@ -0,0 +1,6 @@ +{ + "parser": "babel-eslint", + "env": { + "es6": true + } +} From df04fd43e84710d35ef00b8da13a507a83bf411d Mon Sep 17 00:00:00 2001 From: Markus Padourek Date: Tue, 14 Apr 2015 18:58:24 +0100 Subject: [PATCH 15/15] Minor fix for dryRun install --- lib/install-dry-run.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/install-dry-run.js b/lib/install-dry-run.js index 07389b8..61077cc 100644 --- a/lib/install-dry-run.js +++ b/lib/install-dry-run.js @@ -45,8 +45,10 @@ module.exports = function installDryRun(dependencies, cmdOptions) { } if (cmdOptions.save) { + content.dependencies = content.dependencies || { }; content.dependencies[packageToSave] = versionToSave; } else if (cmdOptions['save-dev']) { + content.devDependencies = content.devDependencies || { }; content.devDependencies[packageToSave] = versionToSave; } }