@@ -12,10 +12,13 @@ const spawn = require('child_process').spawn;
1212 * @param {String } script - full script string, like `git clone https://github.com/node-modules/runscript.git`
1313 * @param {Object } [options] - spawn options
1414 * @see https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
15+ * @param {Object } [extraOptions] - extra options for running
16+ * - {Number} [extraOptions.timeout] - child process running timeout
1517 * @return {Object } stdio object, will contains stdio.stdout and stdio.stderr buffer.
1618 */
17- module . exports = function runScript ( script , options ) {
19+ module . exports = function runScript ( script , options , extraOptions ) {
1820 return new Promise ( ( resolve , reject ) => {
21+ extraOptions = extraOptions || { } ;
1922 options = options || { } ;
2023 options . env = options . env || Object . create ( process . env ) ;
2124 options . cwd = options . cwd || process . cwd ( ) ;
@@ -38,12 +41,16 @@ module.exports = function runScript(script, options) {
3841 }
3942 }
4043
41- debug ( '%s %s %s, %j' , sh , shFlag , script , options ) ;
44+ debug ( '%s %s %s, %j, %j ' , sh , shFlag , script , options , extraOptions ) ;
4245 const proc = spawn ( sh , [ shFlag , script ] , options ) ;
4346 const stdout = [ ] ;
4447 const stderr = [ ] ;
48+ let isEnd = false ;
49+ let timeoutTimer ;
50+
4551 if ( proc . stdout ) {
4652 proc . stdout . on ( 'data' , buf => {
53+ debug ( 'stdout %d bytes' , buf . length ) ;
4754 stdout . push ( buf ) ;
4855 } ) ;
4956 if ( options . stdout ) {
@@ -52,14 +59,29 @@ module.exports = function runScript(script, options) {
5259 }
5360 if ( proc . stderr ) {
5461 proc . stderr . on ( 'data' , buf => {
62+ debug ( 'stderr %d bytes' , buf . length ) ;
5563 stderr . push ( buf ) ;
5664 } ) ;
5765 if ( options . stderr ) {
5866 proc . stderr . pipe ( options . stderr ) ;
5967 }
6068 }
61- proc . on ( 'error' , reject ) ;
69+
70+ proc . on ( 'error' , err => {
71+ debug ( 'proc emit error: %s' , err ) ;
72+ if ( isEnd ) return ;
73+ isEnd = true ;
74+ clearTimeout ( timeoutTimer ) ;
75+
76+ reject ( err ) ;
77+ } ) ;
78+
6279 proc . on ( 'close' , code => {
80+ debug ( 'proc emit close: %s' , code ) ;
81+ if ( isEnd ) return ;
82+ isEnd = true ;
83+ clearTimeout ( timeoutTimer ) ;
84+
6385 const stdio = {
6486 stdout : null ,
6587 stderr : null ,
@@ -72,10 +94,40 @@ module.exports = function runScript(script, options) {
7294 }
7395 if ( code !== 0 ) {
7496 const err = new Error ( `Run "${ sh } ${ shFlag } ${ script } " error, exit code ${ code } ` ) ;
97+ err . name = 'RunScriptError' ;
7598 err . stdio = stdio ;
7699 return reject ( err ) ;
77100 }
78101 return resolve ( stdio ) ;
79102 } ) ;
103+
104+ proc . on ( 'exit' , code => {
105+ debug ( 'proc emit exit: %s' , code ) ;
106+ } ) ;
107+
108+ if ( typeof extraOptions . timeout === 'number' && extraOptions . timeout > 0 ) {
109+ // start timer
110+ timeoutTimer = setTimeout ( ( ) => {
111+ debug ( 'proc run timeout: %dms' , extraOptions . timeout ) ;
112+ isEnd = true ;
113+ debug ( 'kill child process %s' , proc . pid ) ;
114+ proc . kill ( ) ;
115+
116+ const err = new Error ( `Run "${ sh } ${ shFlag } ${ script } " timeout in ${ extraOptions . timeout } ms` ) ;
117+ err . name = 'RunScriptTimeoutError' ;
118+ const stdio = {
119+ stdout : null ,
120+ stderr : null ,
121+ } ;
122+ if ( stdout . length > 0 ) {
123+ stdio . stdout = Buffer . concat ( stdout ) ;
124+ }
125+ if ( stderr . length > 0 ) {
126+ stdio . stderr = Buffer . concat ( stderr ) ;
127+ }
128+ err . stdio = stdio ;
129+ return reject ( err ) ;
130+ } , extraOptions . timeout ) ;
131+ }
80132 } ) ;
81133} ;
0 commit comments