Skip to content

Commit

Permalink
feat: support remove command (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
DiamondYuan authored and popomore committed Feb 19, 2019
1 parent b45883a commit 76762aa
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 10 deletions.
25 changes: 23 additions & 2 deletions lib/base_command.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const runscript = require('runscript');
const through = require('through2');
const giturl = require('giturl');
const Cache = require('./cache');

const PROMPT = Symbol('prompt');

const configDir = path.join(process.env.HOME, '.projj');
const configPath = path.join(configDir, 'config.json');
Expand Down Expand Up @@ -82,7 +82,7 @@ class Command extends BaseCommand {
message: 'Set base directory:',
default: defaults.base,
};
const { base } = yield inquirer.prompt([ question ]);
const { base } = yield this.prompt([ question ]);
this.config = resolveConfig({ base }, defaults);
yield fs.writeFile(configPath, JSON.stringify(this.config, null, 2));
}
Expand Down Expand Up @@ -113,6 +113,27 @@ class Command extends BaseCommand {
yield this.runScript(hook, opt);
}

* prompt(questions) {
if (!this[PROMPT]) {
// create a self contained inquirer module.
this[PROMPT] = inquirer.createPromptModule();
const promptMapping = this[PROMPT].prompts;
for (const key of Object.keys(promptMapping)) {
const Clz = promptMapping[key];
// extend origin prompt instance to emit event
promptMapping[key] = class CustomPrompt extends Clz {
/* istanbul ignore next */
static get name() { return Clz.name; }
run() {
process.send && process.send({ type: 'prompt', name: this.opt.name });
process.emit('message', { type: 'prompt', name: this.opt.name });
return super.run();
}
};
}
}
return this[PROMPT](questions);
}
* runScript(cmd, opt) {
const stdout = through();
stdout.pipe(this.childLogger.stdout, { end: false });
Expand Down
5 changes: 2 additions & 3 deletions lib/command/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

const path = require('path');
const chalk = require('chalk');
const inquirer = require('inquirer');
const clipboardy = require('clipboardy');
const utils = require('../utils');
const BaseCommand = require('../base_command');

class FindCommand extends BaseCommand {

* _run(cwd, [ repo ]) {
const keys = Object.keys(yield this.cache.get());
if (!repo) {
this.logger.error('Please specify the repo name:');
this.childLogger.error(chalk.white('For example:'), chalk.green('projj find', chalk.yellow('example')));
return;
}
const keys = Object.keys(yield this.cache.get());
let matched = keys.filter(key => key.endsWith(repo.replace(/^\/?/, '/')));
if (!matched.length) matched = keys.filter(key => key.indexOf(repo) >= 0);

Expand Down Expand Up @@ -45,7 +44,7 @@ class FindCommand extends BaseCommand {
}

* choose(choices) {
return yield inquirer.prompt({
return yield this.prompt({
name: 'key',
type: 'list',
message: 'Please select the correct repo',
Expand Down
87 changes: 87 additions & 0 deletions lib/command/remove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';

const path = require('path');
const chalk = require('chalk');
const BaseCommand = require('../base_command');
const fs = require('mz/fs');
const rimraf = require('mz-modules/rimraf');

class RemoveCommand extends BaseCommand {

* _run(cwd, [ repo ]) {
if (!repo) {
this.logger.error('Please specify the repo name:');
this.childLogger.error(chalk.white('For example:'), chalk.green('projj remove', chalk.yellow('example')));
return;
}
const keys = Object.keys(yield this.cache.get());
let matched = keys.filter(key => key.endsWith(repo.replace(/^\/?/, '/')));
if (!matched.length) matched = keys.filter(key => key.indexOf(repo) >= 0);
if (!matched.length) {
this.logger.error('Can not find repo %s', chalk.yellow(repo));
return;
}
let key;
if (matched.length === 1) {
key = matched[0];
} else {
const res = yield this.choose(matched);
key = res.key;
}
this.logger.info('Do you want to remove the repository', chalk.green(key));
this.logger.info(chalk.red('Removed repository cannot be restored!'));
const s = key.split('/');
const res = yield this.confirm(`${s[1]}/${s[2]}`);
if (res) {
const dir = path.join(this.config.base, key);
yield rimraf(dir);
const parent = path.dirname(dir);
if ((yield fs.readdir(parent)).length === 0) {
yield rimraf(parent);
}
yield this.cache.remove(key);
yield this.cache.dump();
this.logger.info('Successfully remove repository', chalk.green(key));
} else {
this.logger.info('Cancel remove repository ', chalk.green(key));
}
}


* confirm(repoName) {
const res = yield this.prompt({
message: `Please type in the name of the repository to confirm. ${chalk.green(repoName)} \n`,
name: 'userInput',
});
if (res.userInput === repoName) {
return true;
}
const continueRes = yield this.prompt({
type: 'confirm',
message: 'Do you want to continue?',
name: 'continueToEnter',
default: false,
});
if (continueRes.continueToEnter) {
return yield this.confirm(repoName);
}
return false;
}


* choose(choices) {
return yield this.prompt({
name: 'key',
type: 'list',
message: 'Please select the correct repo',
choices,
});
}

help() {
return 'Remove repository';
}

}

module.exports = RemoveCommand;
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"ora": "^1.1.0",
"runscript": "^1.2.1",
"through2": "^2.0.3",
"zlogger": "^1.1.0"
"zlogger": "^1.1.0",
"mz-modules":"^1.0.0"
},
"devDependencies": {
"autod": "^2.7.1",
Expand All @@ -22,9 +23,7 @@
"egg-ci": "^1.2.0",
"eslint": "^3.16.1",
"eslint-config-egg": "^3.2.0",
"mm": "^2.1.0",
"mz-modules": "^1.0.0",
"rimraf": "^2.5.4"
"mm": "^2.1.0"
},
"engines": {
"node": ">=6.0.0"
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/remove/.projj/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"base": "../temp"
}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion test/projj_find.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('test/projj_find.test.js', () => {
.end(done);
});

it('should find tow matchs file with egg-core', done => {
it('should find two matching file with egg-core', done => {
const home = path.join(fixtures, 'find');
mm(process.env, 'HOME', home);
coffee.fork(binfile, [ 'find', 'egg-core' ])
Expand Down
135 changes: 135 additions & 0 deletions test/projj_remove.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
'use strict';

const path = require('path');
const coffee = require('coffee');
const mm = require('mm');
const rimraf = require('mz-modules/rimraf');
const binfile = path.join(__dirname, '../bin/projj.js');
const fixtures = path.join(__dirname, 'fixtures');
const fs = require('mz/fs');
const home = path.join(fixtures, 'remove');
const runscript = require('runscript');
const assert = require('assert');
const projects = path.join(home, 'projects');
const tempProject = path.join(home, 'temp');
const catchPath = path.join(home, '.projj/cache.json');

describe('test/projj_remove.test.js', () => {
beforeEach(function* () {
mm(process.env, 'HOME', home);
const content = JSON.stringify({
'github.com/popomore/projj': {},
'github.com/eggjs/egg': {},
'github.com/eggjs/egg-core': {},
'github.com/eggjs/autod-egg': {},
'gitlab.com/eggjs/egg': {},
'github.com/DiamondYuan/yuque': {},
});
yield runscript(`cp -r ${projects} ${tempProject}`);
yield fs.writeFile(catchPath, content);
});

afterEach(() => {
afterEach(() => rimraf(tempProject));
afterEach(() => rimraf(catchPath));
});


it('should to prompt if the input is empty', done => {
coffee.fork(binfile, [ 'remove', '' ])
.expect('stderr', new RegExp('Please specify the repo name:'))
.expect('stderr', new RegExp('For example: projj remove example'))
.expect('code', 0)
.end(done);
});

it('if there are other files in the folder, the folder will not be deleted.', done => {
coffee.fork(binfile, [ 'remove', 'yuque' ])
.waitForPrompt()
.expect('stdout', new RegExp('Do you want to remove the repository github.com/DiamondYuan/yuque'))
.expect('stdout', new RegExp('Removed repository cannot be restored!'))
.expect('stdout', new RegExp('Please type in the name of the repository to confirm. DiamondYuan/yuque'))
.write('DiamondYuan/yuque\n')
.expect('stdout', new RegExp('Successfully remove repository github.com/DiamondYuan/yuque'))
.expect('code', 0)
.end(err => {
assert.ifError(err);
assert(fs.existsSync(path.join(tempProject, 'github.com/DiamondYuan')));
done();
});
});

it('if no other files are in the folder, the folder will be deleted.', done => {
const folder = path.join(tempProject, 'github.com/popomore');
coffee.fork(binfile, [ 'remove', 'projj' ])
.expect('stdout', new RegExp('Do you want to remove the repository github.com/popomore/projj'))
.expect('stdout', new RegExp('Removed repository cannot be restored!'))
.expect('stdout', new RegExp('Please type in the name of the repository to confirm. popomore/projj'))
.write('popomore/projj')
.expect('code', 0)
.end(err => {
assert.ifError(err);
assert(fs.existsSync(folder) === false);
done();
});
});

it('should update cache that do not exist', done => {
coffee.fork(binfile, [ 'remove', 'autod-egg' ])
.expect('stdout', new RegExp('Do you want to remove the repository github.com/eggjs/autod-egg'))
.expect('stdout', new RegExp('Removed repository cannot be restored!'))
.expect('stdout', new RegExp('Please type in the name of the repository to confirm. eggjs/autod-egg'))
.write('eggjs/autod-egg')
.expect('code', 0)
.end(err => {
assert.ifError(err);
const cache = fs.readFileSync(catchPath);
assert(JSON.parse(cache.toString())['github.com/eggjs/autod-egg'] === undefined);
done();
});
});

it('could retry if the input is incorrect', done => {
coffee.fork(binfile, [ 'remove', 'autod-egg' ])
.waitForPrompt()
.expect('stdout', new RegExp('Do you want to remove the repository github.com/eggjs/autod-egg'))
.expect('stdout', new RegExp('Removed repository cannot be restored!'))
.expect('stdout', new RegExp('Please type in the name of the repository to confirm. eggjs/autod-egg'))
.write('eggjs/egg\n')
.expect('stdout', new RegExp('Do you want to continue'))
.write('Y\n')
.write('eggjs/autod-egg')
.expect('code', 0)
.end(done);
});

it('could cancel if the input is incorrect', done => {
coffee.fork(binfile, [ 'remove', 'autod-egg' ])
.waitForPrompt()
.expect('stdout', new RegExp('Do you want to remove the repository github.com/eggjs/autod-egg'))
.expect('stdout', new RegExp('Removed repository cannot be restored!'))
.expect('stdout', new RegExp('Please type in the name of the repository to confirm. eggjs/autod-egg'))
.write('eggjs/egg\n')
.expect('stdout', new RegExp('Do you want to continue'))
.write('\n')
.expect('stdout', new RegExp('Cancel remove repository {2}github.com/eggjs/autod-egg'))
.expect('code', 0)
.end(done);
});

it('should find two matching file with egg', done => {
coffee.fork(binfile, [ 'remove', 'egg' ])
.expect('stdout', new RegExp('Please select the correct repo'))
.write('\n')
.expect('stdout', new RegExp('Do you want to remove the repository github.com/eggjs/egg'))
.expect('code', 0)
.end(done);
});

it('should find nothing with eggggg', done => {
coffee.fork(binfile, [ 'remove', 'eggggg' ])
.expect('stderr', new RegExp('Can not find repo eggggg'))
.expect('code', 0)
.end(done);
});
});

0 comments on commit 76762aa

Please sign in to comment.