Skip to content

Commit

Permalink
Updated rendering!
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim Buck committed Aug 6, 2016
1 parent 57394bc commit 9f16b44
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 168 deletions.
18 changes: 1 addition & 17 deletions .vscode/launch.json
Expand Up @@ -5,7 +5,7 @@
"name": "Launch",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/index.js",
"program": "${workspaceRoot}/dist/debug.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",
Expand All @@ -17,22 +17,6 @@
"env": {
"NODE_ENV": "development"
},
"externalConsole": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/dist"
}, {
"name": "AVA",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/ava/cli.js",
"stopOnEntry": true,
"args": ["--serial"],
"cwd": "${workspaceRoot}",
"preLaunchTask": null,
"runtimeExecutable": null,
"env": {
"NODE_ENV": "development"
},
"externalConsole": true,
"sourceMaps": true,
"outDir": "${workspaceRoot}/dist"
Expand Down
25 changes: 7 additions & 18 deletions README.md
Expand Up @@ -8,31 +8,20 @@ A simple tool for running multiple projects at once.

This module will recursively scan the current directory, looking for supported project types. After selecting which projects you'd like to run, it will execute them all simultaneously, perfect for those multi-tiered projects!

## Example:
## Installation:

```sh
# Run select .NET Core 1.0 projects...
pb

# Run select node projects...
md node

# Run any selection of any projects...
md
npm i -g @jimmyboh/playbook
```

```ts
import * as fs from 'fs';
import {Playbook} from '@jimmyboh/playbook';
## Usage:

// tbd...
let pb = new Playbook();
```
_Coming Soon!_

## Features:
- Auto-scan for projects.
- Intelligently runs each project type.
- Remembers runtime configurations for rapid usage.
- Auto-scans filesystem for projects.
- Supports multiple project types (`dotnet`, `node`, `npm` tasks, and growing).
- Interactive REPL for advanced editing, or direct commands for quick access.

## Contribute

Expand Down
75 changes: 46 additions & 29 deletions src/bin/pb.ts
Expand Up @@ -7,7 +7,7 @@ const ansiEscapes = require('ansi-escapes');

import {Playbook, Project, Play} from '../';
import {first} from '../services/utils';
import {ProcessDisplay} from '../services/process-display';
import {ProcessManager} from '../services/process-manager';

interface Answers {
[key: string]: any;
Expand All @@ -16,6 +16,7 @@ interface Answers {
const app = require('vorpal')();

const pb = new Playbook();
let procManager: ProcessManager;

//#region Commands

Expand All @@ -29,8 +30,8 @@ const autocompletePlays = {
};

app
.command('list [playName]', 'Shows available plays')
.alias('ls').alias('show')
.command('list [playName]', 'Shows available plays currently regsitered.')
.alias('ls', 'show')
.autocomplete(autocompletePlays)
.action(function (args: ParsedArgs) {
return showPlays.call(this, args)
Expand All @@ -39,67 +40,79 @@ app
});
});

let lastCreatedPlay: Play;
app
.command('new [playName]', 'Creates a new play.')
.command('new [playName]', 'Create a new play with a collection of projects.')
.alias('create')
.action(function (args: ParsedArgs) {
let action: Promise<Play>;

lastCreatedPlay = null;
return inputPlayName.call(this, args)
.then((playName: string) => {
return pb.create(playName, process.cwd())
return pb.create(playName, process.cwd()).then(play => lastCreatedPlay = play);
})
.then(editPlay.bind(this))
.then(() => {
lastCreatedPlay = null;
})
.catch((err: Error) => {
this.log('An error occured while creating...');
this.log(chalk.bgRed.white('An error occured while creating! Please try again.'));
return deletePlay(lastCreatedPlay).then(() => lastCreatedPlay = null);
});
})
.cancel(() => {
if (lastCreatedPlay) return deletePlay(lastCreatedPlay).then(() => lastCreatedPlay = null);
});

app
.command('edit [playName]', 'Edit an existing play.')
.alias('update').alias('change')
.command('edit [playName]', 'Edit the projects assigned to an existing play.')
.alias('update', 'change')
.autocomplete(autocompletePlays)
.action(function (args: ParsedArgs) {

return selectPlay.call(this, args)
.then(editPlay.bind(this))
.catch((err: Error) => {
this.log('An error occured while editing...');
this.log(chalk.bgRed.white('An error occured while editing...'));
});
});

app
.command('delete [playName]', 'Delete an existing play.')
.alias('del').alias('rm')
.alias('del', 'remove', 'rm')
.autocomplete(autocompletePlays)
.action(function (args: ParsedArgs) {

return selectPlay.call(this, args)
.then(deletePlay.bind(this))
.catch((err: Error) => {
this.log('An error occured while deleting...');
this.log(chalk.bgRed.white('An error occured while deleting...'));
});
});


let procDisplay: ProcessDisplay;
app
.command('run [playName]', 'Executes a play.')
.alias('exec').alias('start')
.command('run [playName]', 'Run a play!')
.alias('exec', 'start')
.autocomplete(autocompletePlays)
.action(function (args: ParsedArgs) {

return selectPlay.call(this, args)
.then(runPlay.bind(this))
.catch((err: Error) => {
this.log('An error occured while running...');
this.log(chalk.bgRed.white('An error occured while running... %o', err));
});
})
.cancel(function () {

procDisplay.cancel();
procManager.cancel();
});

// app
// .command('clear', 'Resets the console to a blank slate.')
// .alias('cls')
// .action(function(args: ParsedArgs){
// this.log('\x1B[2J\x1B[0f');
// });

app
.delimiter('playbook~$')
.show()
Expand All @@ -120,14 +133,17 @@ function showPlays(args: ParsedArgs): Promise<void>{
play.projects.forEach(project => {
this.log(` ${project.name}`);
});
}).catch(err => {
this.log(chalk.red(`Play "${playName}" not found!`));
return showPlays.call(this, {});
});
}

return pb
.getAll()
.then((plays: Play[]) => {
if (plays.length === 0) {
this.log(`No plays found! (Try 'playbook new' to create one)`)
this.log(`No plays found! (Try 'new' to create one)`)
} else {
plays.forEach(play => {
this.log(` ${play.toString()}`);
Expand All @@ -149,7 +165,7 @@ function inputPlayName(args: ParsedArgs): Promise<string> {
{
type: 'input',
name: answerName,
message: 'What is the name of this play?',
message: 'What is the name of this play? ',
default: args['name'],
}
]).then((answers: Answers) => {
Expand All @@ -173,8 +189,10 @@ function selectPlay(args: ParsedArgs): Promise<Play> {
{
type: 'list',
name: answerName,
message: 'Which play would you like?',
choices: plays.map(play => play.name)
message: 'Which play would you like? ',
choices: plays.map(play => {
return {name: play.toString(), value: play.name}
})
}
]).then((answers: Answers): Promise<Play> => {
playName = <string>answers[answerName];
Expand Down Expand Up @@ -202,7 +220,7 @@ function editPlay(play: Play): Promise<void> {
{
type: 'checkbox',
name: answerName,
message: 'Which projects should be included?',
message: 'Which projects should be included? ',
choices,
default: defaults
}
Expand All @@ -227,7 +245,7 @@ function deletePlay(play: Play): Promise<void>{
{
type: 'confirm',
name: answerName,
message: 'Are you sure you want to delete this play?',
message: `Are you sure you want to delete "${play.toString()}"? `,
}
]).then((answers: Answers) => {
let yes = <boolean>answers[answerName];
Expand All @@ -240,12 +258,11 @@ function deletePlay(play: Play): Promise<void>{

function runPlay(play: Play): Promise<void>{
return new Promise<void>((resolve, reject) => {
let processes = play.run();
procDisplay = new ProcessDisplay(processes);
procManager = play.run();

return procDisplay.render((text) => {
return procManager.render((text) => {
app.ui.redraw(text);
}, 200);
}, 100);
});
}

Expand Down
15 changes: 15 additions & 0 deletions src/debug.ts
@@ -0,0 +1,15 @@

import {Playbook} from './services/playbook';
import {ProcessManager} from './services/process-manager';

let pb = new Playbook();
let procManager: ProcessManager;

pb.get('main-app').then(play => {
procManager = play.run();

return procManager.render((text) => {
process.stdout.write('\x1B[2J\x1B[0f');
process.stdout.write(text);
}, 400);
});
9 changes: 7 additions & 2 deletions src/handlers/node.ts
@@ -1,5 +1,5 @@
import * as fs from 'fs';
import {basename, dirname} from 'path';
import {basename, dirname, join} from 'path';
import * as pify from 'pify';
const $fs = pify(fs);

Expand Down Expand Up @@ -31,7 +31,12 @@ export const nodeHandler: ProjectHandler = {
packageJson.title = packageJson.title || basename(cwd);
packageJson.main = packageJson.main || 'index.js';

let projects: Project[] = [new NodeProject(cwd, packageJson)];
let projects: Project[] = [];

if(fs.existsSync(join(cwd, packageJson.main)))
{
projects.push(new NodeProject(cwd, packageJson));
}

if (packageJson.scripts) {
Object.keys(packageJson.scripts)
Expand Down
17 changes: 12 additions & 5 deletions src/models/play.ts
Expand Up @@ -2,6 +2,7 @@
import {ChildProcess, exec} from 'child_process';

import {IProject, Project} from './project';
import {ProcessManager} from '../services/process-manager';

export interface IPlay
{
Expand All @@ -28,16 +29,22 @@ export class Play implements IPlay
this.projects = (data.projects || []).map(proj => new Project(proj));
}

public run(): Lookup<ChildProcess>
public run(): ProcessManager
{
let procs: Lookup<ChildProcess> = {};
this.projects.forEach(proj => {
procs[proj.name] = exec(`${proj.command} ${proj.args.join(' ')}`, { cwd: proj.cwd });
let projs = this.projects.map(proj => {
proj.currentProcess = exec(`${proj.command} ${proj.args.join(' ')}`, { cwd: proj.cwd });
return proj;
});

return procs;
return new ProcessManager(projs);
}


/**
* Prints out the name and count of projects.
*
* @returns {string}
*/
public toString(): string {
return `${this.name} (${this.projects.length})`;
}
Expand Down
5 changes: 5 additions & 0 deletions src/models/project.ts
Expand Up @@ -29,6 +29,8 @@ export interface IProject
command?: string;

args?: string[];

currentProcess?: ChildProcess;
}

export class Project implements IProject
Expand All @@ -41,10 +43,13 @@ export class Project implements IProject

public args: string[];

public currentProcess: ChildProcess;

constructor(opts?: IProject) {
this.name = opts && opts.name;
this.cwd = opts && opts.cwd;
this.command = opts && opts.command;
this.args = (opts && opts.args) || [];
this.currentProcess = opts && opts.currentProcess;
}
}

0 comments on commit 9f16b44

Please sign in to comment.