Skip to content

Commit

Permalink
feat: add --local option for use (#14)
Browse files Browse the repository at this point in the history
* feat: add --local option for use

Writes to a `.npmrc` in the current directory

* chore(readme): update usage docs for `use`

* chore: migrate to jest

* fix: add `rm` argument cli

* fix: update errors and messages
  • Loading branch information
trs authored Jul 13, 2018
1 parent 1a2a7cc commit 45533e2
Show file tree
Hide file tree
Showing 15 changed files with 144 additions and 92 deletions.
4 changes: 1 addition & 3 deletions .config/eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
"extends": "eslint:recommended",
"env": {
"node": true,
"mocha": true,
"jest": true,
"es6": true
},
"plugins": [
"mocha",
"node"
],
"rules": {
"mocha/no-exclusive-tests": 2,
"no-warning-comments": [
1,
{
Expand Down
2 changes: 0 additions & 2 deletions .config/mocha.opts

This file was deleted.

11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

If you've ever needed to publish to npm with multiple users, this tool is for you.

`npmum` stores tokens under a username alias. This allows you to easily switch between users by changing the token in `~/.npmrc` based on the username alias.
`npmum` stores tokens under a username alias. This allows you to easily switch between users by changing the token in `.npmrc` based on the username alias.

## Install

Expand All @@ -43,7 +43,7 @@ $ npm install npmum -g

### Add

Prompts you for a token to add under the username alias.
Prompts you for a token to add under the username alias.
You can also provide the token via `-t`/`--token`.

```
Expand All @@ -52,12 +52,15 @@ $ npmum add <user>[ --token <token>]

### Use

Use the token for provided username alias.
Use the token for provided username alias.
You can specify the local of the `.npmrc` with `-p`/`--path`. This defaults to `~/.npmrc`.
`--local` with write the a `.npmrc` in the current directory.

```
$ npmum use <user>
$ npmum use <user>[ --path <path>, --local]
```


### Remove

Remove a user token from the config.
Expand Down
8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
],
"bin": "bin/npmum.js",
"scripts": {
"test": "NODE_ENV=test mocha --opts ./.config/mocha.opts",
"test:watch": "npm run test -- -w",
"test": "NODE_ENV=test jest test/**/*.spec.js",
"test:watch": "npm run test -- --watch",
"lint": "eslint -c ./.config/eslintrc.json \"src/**/*.js\" \"test/**/*.js\" \"bin/**/*.js\"",
"lint:fix": "eslint --fix -c ./.config/eslintrc.json \"src/**/*.js\" \"test/**/*.js\" \"bin/**/*.js\"",
"commitmsg": "validate-commit-msg"
Expand All @@ -33,13 +33,11 @@
"npm": ">3"
},
"devDependencies": {
"chai": "^4.1.2",
"condition-circle": "^1.5.0",
"eslint": "^4.11.0",
"eslint-plugin-mocha": "^4.11.0",
"eslint-plugin-node": "^5.2.1",
"husky": "^0.14.3",
"mocha": "^4.0.1",
"jest": "^23.4.0",
"mock-stdin": "^0.3.1",
"semantic-release": "^15.4.1",
"sinon": "^4.1.2",
Expand Down
3 changes: 2 additions & 1 deletion src/commands/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ function handle(name, options = {}) {
return Promise.resolve(options.token)
.then(token => {
const user = storage.getUser(name);
if (user) throw new Error('User already exists.');
if (user) throw new errors.UserAlreadyExists(name);
if (token) return token;
return prompt('Token:');
})
.then(token => storage.addUser(name, {token: token.trim()}))
.then(() => console.log(`User added: ${name}`) || true)
.catch(errors.handle);
}

Expand Down
3 changes: 2 additions & 1 deletion src/commands/ls.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ function handle() {
const currentUser = storage.getCurrentUser();

if (!Object.keys(users).length) {
console.log('No users added yet!');
console.log('No user tokens added.');
console.log('Use `npmum add <name>` to add some.');
return false;
}

Expand Down
10 changes: 7 additions & 3 deletions src/commands/rm.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
const storage = require('../storage');
const errors = require('../errors');

const rm = {
handle
};

function handle(name) {
const success = storage.removeUser(name);
if (!success) console.log('User does not exist.');
return success;
if (!storage.removeUser(name)) {
throw new errors.UserNotFound(name);
}

console.log(`User removed: ${name}`);
return true;
}

module.exports = rm;
22 changes: 18 additions & 4 deletions src/commands/use.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
const fs = require('fs');
const nodePath = require('path');
const storage = require('../storage');
const errors = require('../errors');

const NPMRC = '.npmrc';

const use = {
handle,

_readNpmrc,
_writeNpmrc
_writeNpmrc,
_resolveNpmrcPath
};

function _readNpmrc(path) {
Expand All @@ -28,13 +32,23 @@ function _writeNpmrc(path, text) {
});
}

function _resolveNpmrcPath(options) {
let path = `${require('os').homedir()}/${NPMRC}`;
if (options.local) path = process.cwd();
if (options.path) path = options.path;

if (nodePath.basename(path) === NPMRC) return path;

return nodePath.join(path, nodePath.sep, NPMRC);
}

function handle(name, options = {}) {
const user = storage.getUser(name);
const npmrcPath = options.path || `${require('os').homedir()}/.npmrc`;
const npmrcPath = _resolveNpmrcPath(options);

return Promise.resolve()
.then(() => {
if (!user) throw new errors.UserNotFound();
if (!user) throw new errors.UserNotFound(user);
if (!user.token) throw new errors.InvalidUserToken();

if (!fs.existsSync(npmrcPath)) return '';
Expand All @@ -53,7 +67,7 @@ function handle(name, options = {}) {
.then(text => use._writeNpmrc(npmrcPath, text))
.then(() => {
storage.setCurrentUser(name);
console.log(`Set npm login user: "${name}".`);
console.log(`Using npm user token: "${name}"`);
return true;
})
.catch(errors.handle);
Expand Down
10 changes: 5 additions & 5 deletions src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ function handle(err) {
}

class UserNotFound extends Error {
constructor() {
super('User not found');
constructor(name) {
super(`User not found: ${name}`);
this.code = 404;
}
}

class UserAlreadyExists extends Error {
constructor() {
super('User already exists');
constructor(name) {
super(`User already exists: ${name}`);
this.code = 400;
}
}

class InvalidUserToken extends Error {
constructor() {
super('User has a non-existant or invalid token');
super('User has an invalid token');
this.code = 500;
}
}
Expand Down
22 changes: 20 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,33 @@ function setup(argv) {
.description('List user tokens and the current user')
.action(ls.handle);

program.command('rm')
program.command('rm <name>')
.description('Remove user token by name')
.action(rm.handle);

program.command('use <name>')
.description('Use user token by name')
.option('-p, --path <path>', 'Specify path to .npmrc')
.option('-p, --path <path>', 'Specify path to a directory to write the .npmrc')
.option('--local', 'Write to .npmrc in the current directory')
.action(use.handle);

program.on('--help', () => {
console.log('');
console.log(' Examples:');
console.log('');
console.log(' $ npmum add my_user_name');
console.log(' $ npmum add my_user_name --token 12345');
console.log('');
console.log(' $ npmum ls');
console.log('');
console.log(' $ npmum use my_user_name');
console.log(' $ npmum use my_user_name -p ~/my/cool/project/');
console.log(' $ npmum use my_user_name --local');
console.log('');
console.log(' $ npmum rm my_user_name');
console.log('');
});

program.parse(argv);

if (!process.argv.slice(2).length) program.outputHelp();
Expand Down
33 changes: 16 additions & 17 deletions test/commands/add.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
const {expect} = require('chai');
const sinon = require('sinon');
const mockStdin = require('mock-stdin');

const add = require('../../src/commands/add');
const storage = require('../../src/storage');

describe('add', function () {
describe('add', () => {
let sandbox;
let stdin;

Expand All @@ -18,7 +17,7 @@ describe('add', function () {
sandbox.restore();
});

it('prompts for a token', () => {
test('prompts for a token', () => {
sandbox.stub(storage, 'getUser').returns(undefined);
sandbox.stub(storage, 'addUser').returns(true);

Expand All @@ -28,44 +27,44 @@ describe('add', function () {

return add.handle('test')
.then(success => {
expect(success).to.equal(true);
expect(storage.addUser.firstCall.args[0]).to.equal('test');
expect(storage.addUser.firstCall.args[1]).to.eql({token: '123'});
expect(success).toBe(true);
expect(storage.addUser.firstCall.args[0]).toBe('test');
expect(storage.addUser.firstCall.args[1]).toEqual({token: '123'});
});
});

it('allows token param', () => {
test('allows token param', () => {
sandbox.stub(storage, 'getUser').returns(undefined);
sandbox.stub(storage, 'addUser').returns(true);

return add.handle('test', {token: '123'})
.then(success => {
expect(success).to.equal(true);
expect(storage.addUser.firstCall.args[0]).to.equal('test');
expect(storage.addUser.firstCall.args[1]).to.eql({token: '123'});
expect(success).toBe(true);
expect(storage.addUser.firstCall.args[0]).toBe('test');
expect(storage.addUser.firstCall.args[1]).toEqual({token: '123'});
});
});

it('trims token', () => {
test('trims token', () => {
sandbox.stub(storage, 'getUser').returns(undefined);
sandbox.stub(storage, 'addUser').returns(true);

return add.handle('test', {token: ' 123 '})
.then(success => {
expect(success).to.equal(true);
expect(storage.addUser.firstCall.args[0]).to.equal('test');
expect(storage.addUser.firstCall.args[1]).to.eql({token: '123'});
expect(success).toBe(true);
expect(storage.addUser.firstCall.args[0]).toBe('test');
expect(storage.addUser.firstCall.args[1]).toEqual({token: '123'});
});
});

it('fails if user exists', () => {
test('fails if user exists', () => {
sandbox.stub(storage, 'getUser').returns({});
sandbox.spy(storage, 'addUser');

return add.handle('test')
.then(success => {
expect(success).to.equal(false);
expect(storage.addUser.called).to.equal(false);
expect(success).toBe(false);
expect(storage.addUser.called).toBe(false);
});
});
});
17 changes: 8 additions & 9 deletions test/commands/ls.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const {expect} = require('chai');
const sinon = require('sinon');

const ls = require('../../src/commands/ls');
const storage = require('../../src/storage');

describe('ls', function () {
describe('ls', () => {
let sandbox;

beforeEach(() => {
Expand All @@ -16,14 +15,14 @@ describe('ls', function () {
sandbox.restore();
});

describe('_mapUserTokens', function () {
it('truncates user tokens', () => {
describe('_mapUserTokens', () => {
test('truncates user tokens', () => {
const users = {
test: {token: '123'},
other: {token: '1234567890'}
};
const mappedUsers = ls._mapUserTokens(users);
expect(mappedUsers).to.eql([{
expect(mappedUsers).toEqual([{
name: 'test',
token: '123',
current: ''
Expand All @@ -35,20 +34,20 @@ describe('ls', function () {
});
});

it('does not loop through empty array', () => {
test('does not loop through empty array', () => {
sandbox.stub(storage, 'getUsers').returns({});

const success = ls.handle();
expect(success).to.equal(false);
expect(success).toBe(false);
});

it('writes column list of tokens', () => {
test('writes column list of tokens', () => {
sandbox.stub(storage, 'getUsers').returns({
test: {token: '123'},
other: {token: '1234567890'}
});

const success = ls.handle();
expect(success).to.equal(true);
expect(success).toBe(true);
});
});
Loading

0 comments on commit 45533e2

Please sign in to comment.