| 
									
										
										
										
											2016-10-23 22:37:15 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  | /* tslint:disable:no-console  */ | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  | import {spawn} from 'child_process'; | 
					
						
							| 
									
										
										
										
											2016-06-21 00:25:55 +02:00
										 |  |  | import {platform} from 'os'; | 
					
						
							|  |  |  | import {normalize} from 'path'; | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | enum State { | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |   idle, | 
					
						
							|  |  |  |   waiting, | 
					
						
							|  |  |  |   error | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-21 00:25:55 +02:00
										 |  |  | export const TSC = normalize('node_modules/.bin/tsc') + (/^win/.test(platform()) ? '.cmd' : ''); | 
					
						
							| 
									
										
										
										
											2016-01-22 10:51:16 -08:00
										 |  |  | export type Command = (stdIn: any, stdErr: any) => Promise<number>; | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export class TscWatch { | 
					
						
							| 
									
										
										
										
											2017-08-10 14:46:42 -07:00
										 |  |  |   private tsconfig: string[]; | 
					
						
							| 
									
										
										
										
											2016-05-26 10:45:37 -07: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-01 22:50:37 -07:00
										 |  |  |   private state: State; | 
					
						
							|  |  |  |   private triggered: Promise<number> = null; | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |   private runOnce: boolean = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |   constructor({tsconfig, start, error, complete, onStartCmds = null, onChangeCmds = null}: { | 
					
						
							| 
									
										
										
										
											2017-08-10 14:46:42 -07:00
										 |  |  |     tsconfig: string | string[], | 
					
						
							| 
									
										
										
										
											2016-05-26 10:45:37 -07:00
										 |  |  |     error: string|RegExp, | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |     start: string, | 
					
						
							| 
									
										
										
										
											2016-05-26 10:45:37 -07:00
										 |  |  |     complete: string, onStartCmds?: Array<string[]|Command>, onChangeCmds?: Array<string[]|Command> | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |   }) { | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |     console.log('Watching:', tsconfig, 'in', process.cwd()); | 
					
						
							| 
									
										
										
										
											2017-08-10 14:46:42 -07:00
										 |  |  |     this.tsconfig = Array.isArray(tsconfig) ? tsconfig : [tsconfig]; | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |     this.start = start; | 
					
						
							|  |  |  |     this.error = error; | 
					
						
							|  |  |  |     this.complete = complete; | 
					
						
							|  |  |  |     this.onStartCmds = onStartCmds || []; | 
					
						
							|  |  |  |     this.onChangeCmds = onChangeCmds || []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   watch() { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |     const tsc = | 
					
						
							| 
									
										
										
										
											2017-08-10 14:46:42 -07:00
										 |  |  |         Promise | 
					
						
							|  |  |  |             .all(this.tsconfig.map(tsconfig => { | 
					
						
							|  |  |  |               const args = [TSC, '--emitDecoratorMetadata', '--project', tsconfig]; | 
					
						
							|  |  |  |               if (!this.runOnce) args.push('--watch'); | 
					
						
							|  |  |  |               return this.runCmd( | 
					
						
							|  |  |  |                   args, {}, (d) => this.consumeLine(d, false), (d) => this.consumeLine(d, true)); | 
					
						
							|  |  |  |             })) | 
					
						
							|  |  |  |             .then( | 
					
						
							|  |  |  |                 exitCodes => | 
					
						
							|  |  |  |                     exitCodes.reduce((prevValue, currValue) => Math.max(prevValue, currValue), 0)); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |     if (this.runOnce) { | 
					
						
							|  |  |  |       tsc.then(() => this.triggerCmds(), code => process.exit(code)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this.state = State.waiting; | 
					
						
							| 
									
										
										
										
											2016-01-22 10:51:16 -08:00
										 |  |  |     this.onStartCmds.forEach((cmd) => this.runCmd(cmd, null, () => null, () => null)); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-26 10:45:37 -07:00
										 |  |  |   private runCmd( | 
					
						
							|  |  |  |       argsOrCmd: string[]|Command, env?: {[k: string]: string}, stdOut = pipeStdOut, | 
					
						
							|  |  |  |       stdErr = pipeStdErr): Promise<number> { | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |     if (typeof argsOrCmd == 'function') { | 
					
						
							|  |  |  |       return (argsOrCmd as Command)(stdErr, stdOut); | 
					
						
							|  |  |  |     } else if (argsOrCmd instanceof Array) { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |       const args = argsOrCmd as Array<string>; | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |       return <any>new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |                const [cmd, ...options] = args; | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |                console.log('=====>', cmd, options.join(' ')); | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |                const childProcess = spawn(cmd, options, {stdio: 'pipe'}); | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |                childProcess.stdout.on('data', stdOut); | 
					
						
							|  |  |  |                childProcess.stderr.on('data', stdErr); | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |                const onExit = () => childProcess.kill(); | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |                childProcess.on('close', (code: number) => { | 
					
						
							|  |  |  |                  process.removeListener('exit', onExit); | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |                  console.log('EXIT:', code, '<=====', args.join(' ')); | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |                  code ? reject(code) : resolve(code); | 
					
						
							|  |  |  |                }); | 
					
						
							|  |  |  |                process.on('exit', onExit); | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |           .catch(reportError); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       throw new Error('Expecting function or an array of strings...'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   run() { | 
					
						
							|  |  |  |     this.runOnce = true; | 
					
						
							|  |  |  |     this.watch(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-04 20:01:17 -07:00
										 |  |  |   runCmdsOnly() { | 
					
						
							|  |  |  |     this.runOnce = true; | 
					
						
							|  |  |  |     this.triggerCmds(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |   consumeLine(buffer: Buffer, isStdError: boolean) { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |     const line = '' + buffer; | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |     if (contains(line, this.start)) { | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |       console.log('=============================================================================='); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |       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); | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |       console.log('------------------------------------------------------------------------------'); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |       if (this.state == State.error) { | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |         console.log('Errors found.... (response not triggered)'); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |         if (this.runOnce) process.exit(1); | 
					
						
							|  |  |  |         this.state = State.idle; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         if (this.triggered) { | 
					
						
							| 
									
										
										
										
											2016-05-26 10:45:37 -07:00
										 |  |  |           this.triggered.then( | 
					
						
							| 
									
										
										
										
											2016-11-23 13:49:57 -08:00
										 |  |  |               () => this.triggerCmds(), (e) => console.log('Error while running commands....', e)); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |         } else { | 
					
						
							|  |  |  |           this.triggerCmds(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       stdOut(buffer, isStdError); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   triggerCmds() { | 
					
						
							| 
									
										
										
										
											2016-11-12 14:08:58 +01:00
										 |  |  |     let cmdPromise: Promise<number> = Promise.resolve(0); | 
					
						
							| 
									
										
										
										
											2016-10-23 22:52:57 +02:00
										 |  |  |     this.onChangeCmds.forEach( | 
					
						
							|  |  |  |         (cmd: string[] | Command) => cmdPromise = | 
					
						
							|  |  |  |             cmdPromise.then(() => this.runCmd(<string[]>cmd))); | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07: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-01 22:50:37 -07:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |         this.triggered = null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     this.triggered = cmdPromise; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  | function stdOut(data: Buffer, isStdError: boolean) { | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07:00
										 |  |  |   if (isStdError) { | 
					
						
							|  |  |  |     process.stderr.write(data); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     process.stdout.write(data); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  | function contains(line: string, text: string | RegExp): boolean { | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07: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 10:51:16 -08:00
										 |  |  | export function reportError(e: any) { | 
					
						
							| 
									
										
										
										
											2016-04-28 17:50:03 -07: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 10:51:16 -08:00
										 |  |  | function pipeStdOut(d: any) { | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |   process.stdout.write(d); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-22 10:51:16 -08:00
										 |  |  | function pipeStdErr(d: any) { | 
					
						
							| 
									
										
										
										
											2016-05-01 22:50:37 -07:00
										 |  |  |   process.stderr.write(d); | 
					
						
							|  |  |  | } |