Skip to content

Commit

Permalink
breaking: Rework commands API
Browse files Browse the repository at this point in the history
  • Loading branch information
geowarin committed Jun 26, 2016
1 parent 1fcdead commit 678f942
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 64 deletions.
70 changes: 48 additions & 22 deletions lib/commands/Commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,68 @@
const chalk = require('chalk');
const debug = require('../utils/debug');

class Commands {
class CommandBuilder {

constructor() {
this.commands = {};
constructor (entry, modificationMode = false) {
this.entry = entry;
this.modificationMode = modificationMode;
}

summary (summary) {
this.entry.summary = summary;
return this;
}

before (beforeFn) {
this.entry.before = (this.entry.before || []).concat(beforeFn);
return this;
}

apply (commandFn) {
if (this.modificationMode) {
throw new Error(`Cannot redeclare command "${this.entry.name}"`)
}
this.entry.command = commandFn;
return this;
}
}

class Commands {

addCommandHelp(name, help) {
const commandEntry = this.commands[name] || {};
Object.assign(commandEntry, {help: help});
this.commands[name] = commandEntry;
constructor () {
this.commands = {};
}

addPreCommand(name, precommand) {
const commandEntry = this.commands[name] || {};
commandEntry.precommands = (commandEntry.precommands || []).concat(precommand);
this.commands[name] = commandEntry;
add (commandName) {
if (this.commands[commandName]) {
throw new Error(`Command "${commandName}" already exists`)
}
const commandEntry = {};
this.commands[commandName] = commandEntry;
commandEntry.name = commandName;
return new CommandBuilder(commandEntry);
}

addCommand(name, command) {
const commandEntry = this.commands[name] || {};
if (commandEntry.command) {
throw new Error('Command "' + name + '" already exists');
modify (commandName) {
if (!this.commands[commandName]) {
throw new Error(`Command ${commandName} does not exist`)
}
Object.assign(commandEntry, {command: command});
this.commands[name] = commandEntry;
return new CommandBuilder(this.commands[commandName], true);
}

runCommand(name, context, args) {
run (name, context, args) {
const commandEntry = this.commands[name];
if (!commandEntry || !commandEntry.command) {
if (!commandEntry || !commandEntry.command) {
throw new Error('Command not found: ' + name);
}
(commandEntry.precommands || []).forEach(precommand => precommand(context, args));
(commandEntry.before || []).forEach(precommand => precommand(context, args));
commandEntry.command(context, args);
}

exists (commandName) {
return Boolean(this.commands[commandName]);
}

showHelp (tarecVersion) {

debug.log(`Tarec ${chalk.blue(tarecVersion)}`);
Expand All @@ -47,8 +73,8 @@ class Commands {
Object.keys(availableCommands).forEach(commandName => {
debug.log(" * " + chalk.blue(commandName));
const command = availableCommands[commandName];
if (command.help) {
debug.log(" " + command.help)
if (command.summary) {
debug.log(" " + command.summary)
}
});
debug.log(`\nType ${chalk.blue('tarec <command> --help')} for more information on a specific command`);
Expand Down
72 changes: 39 additions & 33 deletions lib/commands/addBuiltInCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,45 @@
const chalk = require('chalk');

module.exports = function addBuiltInCommands (commands) {
commands.addCommandHelp('build', `Generate your bundled application in ${chalk.magenta('/dist')}`);
commands.addPreCommand('build', (context, args) => {
process.env['NODE_ENV'] = 'production';
context.webpackConfig = require('../webpack/webpack.prod.config')(context);
if (args.nominify !== true) {
const webpack = require('webpack');
context.webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
unused: true,
dead_code: true,
warnings: false,
screw_ie8: true
}
}))
}
});
commands.addCommand('build', (context, args) => {
require('../commands/clean')(context);
require('../commands/build')(context, args);
});
commands
.add('build')
.summary(`Generate your bundled application in ${chalk.magenta('/dist')}`)
.before((context, args) => {
process.env['NODE_ENV'] = 'production';
context.webpackConfig = require('../webpack/webpack.prod.config')(context);
if (args.nominify !== true) {
const webpack = require('webpack');
context.webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
unused: true,
dead_code: true,
warnings: false,
screw_ie8: true
}
}))
}
})
.apply((context, args) => {
require('../commands/clean')(context);
require('../commands/build')(context, args);
});

commands.addCommandHelp('start', `Creates a dev server on port ${chalk.magenta('3000')}`);
commands.addPreCommand('start', (context, args) => {
process.env['NODE_ENV'] = 'development';
context.serverPort = args.p || args.port || 3000;
context.webpackConfig = require('../webpack/webpack.dev.config')(context);
});
commands.addCommand('start', (context, args) => {
require('../commands/start')(context, args);
});
commands
.add('start')
.summary(`Creates a dev server on port ${chalk.magenta('3000')}`)
.before((context, args) => {
process.env['NODE_ENV'] = 'development';
context.serverPort = args.p || args.port || 3000;
context.webpackConfig = require('../webpack/webpack.dev.config')(context);
})
.apply((context, args) => {
require('../commands/start')(context, args);
});

commands.addCommandHelp('init', 'Generates a simple application in the current directory');
commands.addCommand('init', (context, args) => {
require('../commands/init')(context, args);
});
commands
.add('init')
.summary('Generates a simple application in the current directory')
.apply((context, args) => {
require('../commands/init')(context, args);
});
};
2 changes: 1 addition & 1 deletion lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function tarec(projectDir, args) {
}

try {
commands.runCommand(commandName, context, commandArgs);
commands.run(commandName, context, commandArgs);
} catch (e) {
debug.error(chalk.red(e.message));
debug.error(e.stack);
Expand Down
5 changes: 2 additions & 3 deletions lib/plugins/addConfiguredPlugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ function resolveOrUndefined(basedir, pluginPath) {
module.exports = function addConfiguredPlugins (plugins, context, commands) {

const operations = {
addCommandHelp: commands.addCommandHelp.bind(commands),
addCommand: commands.addCommand.bind(commands),
addPreCommand: commands.addPreCommand.bind(commands),
modify: commands.modify.bind(commands),
add: commands.add.bind(commands),
resolve: (module) => require(module)
};
plugins.forEach(pluginPath => {
Expand Down
65 changes: 60 additions & 5 deletions test/Command.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,85 @@

const expect = require('expect');
const Commands = require('../lib/commands/Commands');
const addBuiltInCommands = require('../lib/commands/addBuiltInCommands');

const test = require('ava');
const debug = require('../lib/utils/debug');

test('should add built-in commands', t => {
const commands = new Commands();
addBuiltInCommands(commands);

t.true(commands.exists('init'));
t.true(commands.exists('build'));
t.true(commands.exists('start'));
});

test('should call the command', t => {

let hasBeenCalled = false;
const commands = new Commands();
commands.addCommand('testCommand', () => hasBeenCalled = true);
commands.runCommand('testCommand');
const beforeSpy = expect.createSpy();

commands
.add('testCommand')
.before(beforeSpy)
.before(beforeSpy)
.apply(() => hasBeenCalled = true);

commands.run('testCommand');
t.true(hasBeenCalled, 'testCommand should have been called');
t.is(beforeSpy.calls.length, 2);
});

test('Cannot redeclare existing command', t => {

const commands = new Commands();
commands
.add('start')
.apply(() => {});

t.throws(
() => { commands.add('start') },
'Command "start" already exists'
);
t.throws(
() => { commands.modify('start').apply(() => {}) },
'Cannot redeclare command "start"'
);
});

test('Can add behavior to existing command', t => {

const beforeSpy = expect.createSpy();
const commands = new Commands();
commands
.add('start')
.apply(() => { });

commands
.modify('start')
.before(beforeSpy);

commands.run('start');
expect(beforeSpy).toHaveBeenCalled();
});

test('should display help', t => {

const commands = new Commands();

debug.capture();
commands.addCommandHelp('cmd', 'cmdHelp');
commands
.add('cmd')
.summary('Cmd Summary');

commands.showHelp('v0');
t.deepEqual(debug.capturedMessages, [
'Tarec v0',
'Available commands:',
'* cmd',
'cmdHelp',
'Cmd Summary',
'Type tarec <command> --help for more information on a specific command'
]);
debug.endCapture();
Expand Down

0 comments on commit 678f942

Please sign in to comment.