1'use strict'; 2const common = require('../common'); 3const spawn = require('child_process').spawn; 4 5const BREAK_MESSAGE = new RegExp('(?:' + [ 6 'assert', 'break', 'break on start', 'debugCommand', 7 'exception', 'other', 'promiseRejection', 8].join('|') + ') in', 'i'); 9 10const TIMEOUT = common.platformTimeout(5000); 11 12function isPreBreak(output) { 13 return /Break on start/.test(output) && /1 \(function \(exports/.test(output); 14} 15 16function startCLI(args, flags = [], spawnOpts = {}) { 17 let stderrOutput = ''; 18 const child = 19 spawn(process.execPath, [...flags, 'inspect', ...args], spawnOpts); 20 21 const outputBuffer = []; 22 function bufferOutput(chunk) { 23 if (this === child.stderr) { 24 stderrOutput += chunk; 25 } 26 outputBuffer.push(chunk); 27 } 28 29 function getOutput() { 30 return outputBuffer.join('\n').replace(/\b/g, ''); 31 } 32 33 child.stdout.setEncoding('utf8'); 34 child.stdout.on('data', bufferOutput); 35 child.stderr.setEncoding('utf8'); 36 child.stderr.on('data', bufferOutput); 37 38 if (process.env.VERBOSE === '1') { 39 child.stdout.pipe(process.stdout); 40 child.stderr.pipe(process.stderr); 41 } 42 43 return { 44 flushOutput() { 45 const output = this.output; 46 outputBuffer.length = 0; 47 return output; 48 }, 49 50 waitFor(pattern) { 51 function checkPattern(str) { 52 if (Array.isArray(pattern)) { 53 return pattern.every((p) => p.test(str)); 54 } 55 return pattern.test(str); 56 } 57 58 return new Promise((resolve, reject) => { 59 function checkOutput() { 60 if (checkPattern(getOutput())) { 61 tearDown(); 62 resolve(); 63 } 64 } 65 66 function onChildClose(code, signal) { 67 tearDown(); 68 let message = 'Child exited'; 69 if (code) { 70 message += `, code ${code}`; 71 } 72 if (signal) { 73 message += `, signal ${signal}`; 74 } 75 message += ` while waiting for ${pattern}; found: ${this.output}`; 76 if (stderrOutput) { 77 message += `\n STDERR: ${stderrOutput}`; 78 } 79 reject(new Error(message)); 80 } 81 82 const timer = setTimeout(() => { 83 tearDown(); 84 reject(new Error([ 85 `Timeout (${TIMEOUT}) while waiting for ${pattern}`, 86 `found: ${this.output}`, 87 ].join('; '))); 88 }, TIMEOUT); 89 90 function tearDown() { 91 clearTimeout(timer); 92 child.stdout.removeListener('data', checkOutput); 93 child.removeListener('close', onChildClose); 94 } 95 96 child.on('close', onChildClose); 97 child.stdout.on('data', checkOutput); 98 checkOutput(); 99 }); 100 }, 101 102 waitForPrompt() { 103 return this.waitFor(/>\s+$/); 104 }, 105 106 waitForInitialBreak() { 107 return this.waitFor(/break (?:on start )?in/i) 108 .then(() => { 109 if (isPreBreak(this.output)) { 110 return this.command('next', false) 111 .then(() => this.waitFor(/break in/)); 112 } 113 }); 114 }, 115 116 get breakInfo() { 117 const output = this.output; 118 const breakMatch = 119 output.match(/break (?:on start )?in ([^\n]+):(\d+)\n/i); 120 121 if (breakMatch === null) { 122 throw new Error( 123 `Could not find breakpoint info in ${JSON.stringify(output)}`); 124 } 125 return { filename: breakMatch[1], line: +breakMatch[2] }; 126 }, 127 128 ctrlC() { 129 return this.command('.interrupt'); 130 }, 131 132 get output() { 133 return getOutput(); 134 }, 135 136 get rawOutput() { 137 return outputBuffer.join('').toString(); 138 }, 139 140 parseSourceLines() { 141 return getOutput().split('\n') 142 .map((line) => line.match(/(?:\*|>)?\s*(\d+)/)) 143 .filter((match) => match !== null) 144 .map((match) => +match[1]); 145 }, 146 147 writeLine(input, flush = true) { 148 if (flush) { 149 this.flushOutput(); 150 } 151 if (process.env.VERBOSE === '1') { 152 process.stderr.write(`< ${input}\n`); 153 } 154 child.stdin.write(input); 155 child.stdin.write('\n'); 156 }, 157 158 command(input, flush = true) { 159 this.writeLine(input, flush); 160 return this.waitForPrompt(); 161 }, 162 163 stepCommand(input) { 164 this.writeLine(input, true); 165 return this 166 .waitFor(BREAK_MESSAGE) 167 .then(() => this.waitForPrompt()); 168 }, 169 170 quit() { 171 return new Promise((resolve) => { 172 child.stdin.end(); 173 child.on('close', resolve); 174 }); 175 }, 176 }; 177} 178module.exports = startCLI; 179