2016-04-28 20:50:03 -04:00
|
|
|
import {spawn} from 'child_process';
|
2016-06-20 18:25:55 -04:00
|
|
|
import {platform} from 'os';
|
|
|
|
import {normalize} from 'path';
|
2016-04-28 20:50:03 -04:00
|
|
|
import {resolve} from 'url';
|
|
|
|
|
|
|
|
enum State {
|
2016-05-02 01:50:37 -04:00
|
|
|
idle,
|
|
|
|
waiting,
|
|
|
|
error
|
2016-04-28 20:50:03 -04:00
|
|
|
}
|
|
|
|
|
2016-06-20 18:25:55 -04:00
|
|
|
export const TSC = normalize('node_modules/.bin/tsc') + (/^win/.test(platform()) ? '.cmd' : '');
|
2016-01-22 13:51:16 -05:00
|
|
|
export type Command = (stdIn: any, stdErr: any) => Promise<number>;
|
2016-04-28 20:50:03 -04:00
|
|
|
|
|
|
|
export class TscWatch {
|
2016-05-02 01:50:37 -04:00
|
|
|
private tsconfig: string;
|
2016-05-26 13:45:37 -04:00
|
|
|
private start: string|RegExp;
|
|
|
|
private error: string|RegExp;
|
|
|
|
private complete: string|RegExp;
|
|
|
|
private onStartCmds: Array<string[]|Command>;
|
|
|
|
private onChangeCmds: Array<string[]|Command>;
|
2016-05-02 01:50:37 -04:00
|
|
|
private state: State;
|
|
|
|
private triggered: Promise<number> = null;
|
2016-04-28 20:50:03 -04:00
|
|
|
private runOnce: boolean = false;
|
|
|
|
|
2016-05-02 01:50:37 -04:00
|
|
|
constructor({tsconfig, start, error, complete, onStartCmds = null, onChangeCmds = null}: {
|
|
|
|
tsconfig: string,
|
2016-05-26 13:45:37 -04:00
|
|
|
error: string|RegExp,
|
2016-05-02 01:50:37 -04:00
|
|
|
start: string,
|
2016-05-26 13:45:37 -04:00
|
|
|
complete: string, onStartCmds?: Array<string[]|Command>, onChangeCmds?: Array<string[]|Command>
|
2016-05-02 01:50:37 -04:00
|
|
|
}) {
|
2016-04-28 20:50:03 -04:00
|
|
|
console.log('Watching:', tsconfig, 'in', process.cwd());
|
|
|
|
this.tsconfig = tsconfig;
|
|
|
|
this.start = start;
|
|
|
|
this.error = error;
|
|
|
|
this.complete = complete;
|
|
|
|
this.onStartCmds = onStartCmds || [];
|
|
|
|
this.onChangeCmds = onChangeCmds || [];
|
|
|
|
}
|
|
|
|
|
|
|
|
watch() {
|
2016-06-16 18:46:08 -04:00
|
|
|
var args = [TSC, '--emitDecoratorMetadata', '--project', this.tsconfig];
|
2016-04-28 20:50:03 -04:00
|
|
|
if (!this.runOnce) args.push('--watch');
|
2016-05-02 01:50:37 -04:00
|
|
|
var tsc =
|
|
|
|
this.runCmd(args, {}, (d) => this.consumeLine(d, false), (d) => this.consumeLine(d, true));
|
2016-04-28 20:50:03 -04:00
|
|
|
if (this.runOnce) {
|
|
|
|
tsc.then(() => this.triggerCmds(), code => process.exit(code));
|
|
|
|
}
|
|
|
|
this.state = State.waiting;
|
2016-01-22 13:51:16 -05:00
|
|
|
this.onStartCmds.forEach((cmd) => this.runCmd(cmd, null, () => null, () => null));
|
2016-04-28 20:50:03 -04:00
|
|
|
}
|
|
|
|
|
2016-05-26 13:45:37 -04:00
|
|
|
private runCmd(
|
|
|
|
argsOrCmd: string[]|Command, env?: {[k: string]: string}, stdOut = pipeStdOut,
|
|
|
|
stdErr = pipeStdErr): Promise<number> {
|
2016-04-28 20:50:03 -04:00
|
|
|
if (typeof argsOrCmd == 'function') {
|
|
|
|
return (argsOrCmd as Command)(stdErr, stdOut);
|
|
|
|
} else if (argsOrCmd instanceof Array) {
|
|
|
|
var args = argsOrCmd as Array<string>;
|
|
|
|
return <any>new Promise((resolve, reject) => {
|
2016-05-02 01:50:37 -04:00
|
|
|
var [cmd, ...options] = args;
|
|
|
|
console.log('=====>', cmd, options.join(' '));
|
|
|
|
var childProcess = spawn(cmd, options, {stdio: 'pipe'});
|
|
|
|
childProcess.stdout.on('data', stdOut);
|
|
|
|
childProcess.stderr.on('data', stdErr);
|
|
|
|
var onExit = () => childProcess.kill();
|
|
|
|
childProcess.on('close', (code: number) => {
|
|
|
|
process.removeListener('exit', onExit);
|
|
|
|
console.log('EXIT:', code, '<=====', args.join(' '));
|
|
|
|
code ? reject(code) : resolve(code);
|
|
|
|
});
|
|
|
|
process.on('exit', onExit);
|
|
|
|
})
|
|
|
|
.catch(reportError);
|
2016-04-28 20:50:03 -04:00
|
|
|
} else {
|
|
|
|
throw new Error('Expecting function or an array of strings...');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
run() {
|
|
|
|
this.runOnce = true;
|
|
|
|
this.watch();
|
|
|
|
}
|
|
|
|
|
2016-05-04 23:01:17 -04:00
|
|
|
runCmdsOnly() {
|
|
|
|
this.runOnce = true;
|
|
|
|
this.triggerCmds();
|
|
|
|
}
|
|
|
|
|
2016-05-02 01:50:37 -04:00
|
|
|
consumeLine(buffer: Buffer, isStdError: boolean) {
|
2016-04-28 20:50:03 -04:00
|
|
|
var line = '' + buffer;
|
|
|
|
if (contains(line, this.start)) {
|
|
|
|
console.log('==============================================================================');
|
|
|
|
stdOut(buffer, isStdError);
|
|
|
|
this.state = State.waiting;
|
|
|
|
} else if (contains(line, this.error)) {
|
|
|
|
stdOut(buffer, isStdError);
|
|
|
|
this.state = State.error;
|
|
|
|
} else if (contains(line, this.complete)) {
|
|
|
|
stdOut(buffer, isStdError);
|
|
|
|
console.log('------------------------------------------------------------------------------');
|
|
|
|
if (this.state == State.error) {
|
|
|
|
console.log('Errors found.... (response not triggered)');
|
|
|
|
if (this.runOnce) process.exit(1);
|
|
|
|
this.state = State.idle;
|
|
|
|
} else {
|
|
|
|
if (this.triggered) {
|
2016-05-26 13:45:37 -04:00
|
|
|
this.triggered.then(
|
|
|
|
() => this.triggerCmds(),
|
|
|
|
(e) => {console.log('Error while running commands....', e)});
|
2016-04-28 20:50:03 -04:00
|
|
|
} else {
|
|
|
|
this.triggerCmds();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stdOut(buffer, isStdError);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
triggerCmds() {
|
2016-05-20 19:06:52 -04:00
|
|
|
var cmdPromise: Promise<number> = Promise.resolve(0);
|
2016-05-02 01:50:37 -04:00
|
|
|
this.onChangeCmds.forEach((cmd: string[] | Command) => {cmdPromise = cmdPromise.then(() => {
|
|
|
|
return this.runCmd(<string[]>cmd);
|
|
|
|
})});
|
2016-04-28 20:50:03 -04:00
|
|
|
cmdPromise.then(() => this.triggered = null, (code) => {
|
|
|
|
if (this.runOnce) {
|
|
|
|
if (typeof code != 'number') {
|
|
|
|
console.error('Error occurred while executing commands', code);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
process.exit(code);
|
2016-05-02 01:50:37 -04:00
|
|
|
} else {
|
2016-04-28 20:50:03 -04:00
|
|
|
this.triggered = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.triggered = cmdPromise;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-02 01:50:37 -04:00
|
|
|
function stdOut(data: Buffer, isStdError: boolean) {
|
2016-04-28 20:50:03 -04:00
|
|
|
if (isStdError) {
|
|
|
|
process.stderr.write(data);
|
|
|
|
} else {
|
|
|
|
process.stdout.write(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-02 01:50:37 -04:00
|
|
|
function contains(line: string, text: string | RegExp): boolean {
|
2016-04-28 20:50:03 -04:00
|
|
|
if (typeof text == 'string') {
|
|
|
|
return line.indexOf(text as string) != -1;
|
|
|
|
} else if (text instanceof RegExp) {
|
|
|
|
return (text as RegExp).test(line);
|
|
|
|
} else {
|
|
|
|
throw new Error('Unknown: ' + text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-22 13:51:16 -05:00
|
|
|
export function reportError(e: any) {
|
2016-04-28 20:50:03 -04:00
|
|
|
if (e.message && e.stack) {
|
|
|
|
console.error(e.message);
|
|
|
|
console.error(e.stack);
|
|
|
|
} else {
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
// process.exit(1);
|
|
|
|
return Promise.reject(e);
|
|
|
|
}
|
|
|
|
|
2016-01-22 13:51:16 -05:00
|
|
|
function pipeStdOut(d: any) {
|
2016-05-02 01:50:37 -04:00
|
|
|
process.stdout.write(d);
|
|
|
|
}
|
2016-01-22 13:51:16 -05:00
|
|
|
function pipeStdErr(d: any) {
|
2016-05-02 01:50:37 -04:00
|
|
|
process.stderr.write(d);
|
|
|
|
}
|