This repository has been archived by the owner on Feb 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jan Krems
committed
Sep 3, 2016
1 parent
44c4c79
commit 985873c
Showing
6 changed files
with
391 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
{ | ||
"extends": "groupon-es5" | ||
"extends": "groupon-node6" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/usr/bin/env node | ||
require('./lib/cli.js'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/* eslint no-restricted-syntax: 0, no-debugger: 0 */ | ||
'use strict'; | ||
const x = process.argv[2] || 'world'; | ||
setTimeout(() => { | ||
debugger; | ||
console.log(x); | ||
}, 1000); | ||
console.log('hello'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
'use strict'; | ||
// ~= NativeModule.require('_debugger').start(); | ||
require('./node-inspect').start(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,375 @@ | ||
'use strict'; | ||
'use strict'; /* eslint no-underscore-dangle: 0 */ | ||
// Our equivalent of '_debugger' in node itthis | ||
const assert = require('assert'); | ||
const repl = require('repl'); | ||
const spawn = require('child_process').spawn; | ||
const util = require('util'); | ||
|
||
exports.port = process.debugPort; | ||
|
||
class Client { | ||
once() {} | ||
destroy() {} | ||
on() {} | ||
connect() {} | ||
} | ||
|
||
// This class is the repl-enabled debugger interface which is invoked on | ||
// "node-inspect" | ||
class Inspector { | ||
constructor(stdin, stdout, args) { | ||
this.stdin = stdin; | ||
this.stdout = stdout; | ||
this.args = args; | ||
|
||
// Two eval modes are available: controlEval and debugEval | ||
// But controlEval is used by default | ||
const opts = { | ||
prompt: 'debug> ', | ||
input: this.stdin, | ||
output: this.stdout, | ||
eval: (code, ctx, file, cb) => this.controlEval(code, ctx, file, cb), | ||
useGlobal: false, | ||
ignoreUndefined: true, | ||
}; | ||
if (parseInt(process.env.NODE_NO_READLINE, 10)) { | ||
opts.terminal = false; | ||
} else if (parseInt(process.env.NODE_FORCE_READLINE, 10)) { | ||
opts.terminal = true; | ||
|
||
// Emulate Ctrl+C if we're emulating terminal | ||
if (!this.stdout.isTTY) { | ||
process.on('SIGINT', () => { | ||
this.repl.rli.emit('SIGINT'); | ||
}); | ||
} | ||
} | ||
if (parseInt(process.env.NODE_DISABLE_COLORS, 10)) { | ||
opts.useColors = false; | ||
} | ||
|
||
this.repl = repl.start(opts); | ||
|
||
// Do not print useless warning | ||
repl._builtinLibs.splice(repl._builtinLibs.indexOf('repl'), 1); | ||
|
||
// Kill child process when main process dies | ||
this.repl.on('exit', () => { | ||
process.exit(0); | ||
}); | ||
|
||
// Handle all possible exits | ||
process.on('exit', () => this.killChild()); | ||
process.once('SIGTERM', process.exit.bind(process, 0)); | ||
process.once('SIGHUP', process.exit.bind(process, 0)); | ||
|
||
// const proto = Inspector.prototype; | ||
// const ignored = ['pause', 'resume', 'exitRepl', 'handleBreak', | ||
// 'requireConnection', 'killChild', 'trySpawn', | ||
// 'controlEval', 'debugEval', 'print', 'childPrint', | ||
// 'clearline']; | ||
// const shortcut = { | ||
// run: 'r', | ||
// cont: 'c', | ||
// next: 'n', | ||
// step: 's', | ||
// out: 'o', | ||
// backtrace: 'bt', | ||
// setBreakpoint: 'sb', | ||
// clearBreakpoint: 'cb', | ||
// pause_: 'pause', | ||
// }; | ||
|
||
// function defineProperty(key, protoKey) { | ||
// // Check arity | ||
// const fn = proto[protoKey].bind(this); | ||
|
||
// if (proto[protoKey].length === 0) { | ||
// Object.defineProperty(this.repl.context, key, { | ||
// get: fn, | ||
// enumerable: true, | ||
// configurable: false, | ||
// }); | ||
// } else { | ||
// this.repl.context[key] = fn; | ||
// } | ||
// } | ||
|
||
// // Copy all prototype methods in repl context | ||
// // Setup them as getters if possible | ||
// for (var i in proto) { | ||
// if (Object.prototype.hasOwnProperty.call(proto, i) && | ||
// ignored.indexOf(i) === -1) { | ||
// defineProperty(i, i); | ||
// if (shortcut[i]) defineProperty(shortcut[i], i); | ||
// } | ||
// } | ||
|
||
this.killed = false; | ||
this.waiting = null; | ||
this.paused = 0; | ||
this.context = this.repl.context; | ||
this.history = { debug: [], control: [] }; | ||
this.breakpoints = []; | ||
this._watchers = []; | ||
|
||
// Run script automatically | ||
this.pause(); | ||
|
||
setImmediate(() => { this.run(); }); | ||
} | ||
|
||
// Clear current line | ||
clearline() { | ||
if (this.stdout.isTTY) { | ||
this.stdout.cursorTo(0); | ||
this.stdout.clearLine(1); | ||
} else { | ||
this.stdout.write('\b'); | ||
} | ||
} | ||
|
||
// Print text to output stream | ||
print(text, oneline) { | ||
if (this.killed) return; | ||
this.clearline(); | ||
|
||
this.stdout.write(typeof text === 'string' ? text : util.inspect(text)); | ||
|
||
if (oneline !== true) { | ||
this.stdout.write('\n'); | ||
} | ||
} | ||
|
||
// Format and print text from child process | ||
childPrint(text) { | ||
this.print( | ||
text.toString() | ||
.split(/\r\n|\r|\n/g) | ||
.filter(chunk => !!chunk) | ||
.map(chunk => `< ${chunk}`) | ||
.join('\n') | ||
); | ||
this.repl.displayPrompt(true); | ||
} | ||
|
||
// Errors formatting | ||
error(text) { | ||
this.print(text); | ||
this.resume(); | ||
} | ||
|
||
|
||
// Stream control | ||
|
||
|
||
pause() { | ||
if (this.killed || this.paused++ > 0) return this; | ||
this.repl.rli.pause(); | ||
this.stdin.pause(); | ||
return this; | ||
} | ||
|
||
resume(silent) { | ||
if (this.killed || this.paused === 0 || --this.paused !== 0) return this; | ||
this.repl.rli.resume(); | ||
if (silent !== true) { | ||
this.repl.displayPrompt(); | ||
} | ||
this.stdin.resume(); | ||
|
||
if (this.waiting) { | ||
this.waiting(); | ||
this.waiting = null; | ||
} | ||
return this; | ||
} | ||
|
||
|
||
// Commands | ||
|
||
|
||
// // Print help message | ||
// help() { | ||
// this.print(helpMessage); | ||
// } | ||
|
||
|
||
// Run script | ||
run(callback) { | ||
if (this.child) { | ||
this.error('App is already running... Try `restart` instead'); | ||
if (callback) callback(true); | ||
} else { | ||
this.trySpawn(callback); | ||
} | ||
} | ||
|
||
|
||
// Quit | ||
quit() { | ||
this.killChild(); | ||
process.exit(0); | ||
} | ||
|
||
// Kills child process | ||
killChild() { | ||
if (this.child) { | ||
this.child.kill(); | ||
this.child = null; | ||
} | ||
|
||
if (this.client) { | ||
// Save breakpoints | ||
this.breakpoints = this.client.breakpoints; | ||
|
||
this.client.destroy(); | ||
this.client = null; | ||
} | ||
} | ||
|
||
// Spawns child process (and restores breakpoints) | ||
trySpawn(cb) { | ||
const breakpoints = this.breakpoints || []; | ||
let port = exports.port; | ||
let host = '127.0.0.1'; | ||
let childArgs = this.args; | ||
|
||
this.killChild(); | ||
assert(!this.child); | ||
|
||
let isRemote = false; | ||
if (this.args.length === 2) { | ||
const match = this.args[1].match(/^([^:]+):(\d+)$/); | ||
|
||
if (match) { | ||
// Connecting to remote debugger | ||
// `node debug localhost:5858` | ||
host = match[1]; | ||
port = parseInt(match[2], 10); | ||
isRemote = true; | ||
} | ||
} else if (this.args.length === 3) { | ||
// `node debug -p pid` | ||
if (this.args[1] === '-p' && /^\d+$/.test(this.args[2])) { | ||
const pid = parseInt(this.args[2], 10); | ||
try { | ||
process._debugProcess(pid); | ||
} catch (e) { | ||
if (e.code === 'ESRCH') { | ||
console.error(`Target process: ${pid} doesn't exist.`); | ||
process.exit(1); | ||
} | ||
throw e; | ||
} | ||
isRemote = true; | ||
} else { | ||
const match = this.args[1].match(/^--port=(\d+)$/); | ||
if (match) { | ||
// Start debugger on custom port | ||
// `node debug --port=5858 app.js` | ||
port = parseInt(match[1], 10); | ||
childArgs = [`--inspect=${port}`, '--debug-brk'].concat(this.args.slice(2)); | ||
} | ||
} | ||
} | ||
|
||
if (!isRemote) { | ||
// pipe stream into debugger | ||
this.child = spawn(process.execPath, childArgs); | ||
|
||
this.child.stdout.on('data', text => this.childPrint(text)); | ||
this.child.stderr.on('data', text => this.childPrint(text)); | ||
} | ||
|
||
this.pause(); | ||
|
||
const client = this.client = new Client(); | ||
let connectionAttempts = 0; | ||
|
||
client.once('ready', () => { | ||
this.stdout.write(' ok\n'); | ||
|
||
// Restore breakpoints | ||
breakpoints.forEach(bp => { | ||
this.print(`Restoring breakpoint ${bp.scriptReq}: ${bp.line}`); | ||
this.setBreakpoint(bp.scriptReq, bp.line, bp.condition, true); | ||
}); | ||
|
||
client.on('close', () => { | ||
this.pause(); | ||
this.print('program terminated'); | ||
this.resume(); | ||
this.client = null; | ||
this.killChild(); | ||
}); | ||
|
||
if (cb) cb(); | ||
this.resume(); | ||
}); | ||
|
||
client.on('unhandledResponse', res => { | ||
this.pause(); | ||
this.print(`\nunhandled res:${JSON.stringify(res)}`); | ||
this.resume(); | ||
}); | ||
|
||
client.on('break', res => { | ||
this.handleBreak(res.body); | ||
}); | ||
|
||
client.on('exception', res => { | ||
this.handleBreak(res.body); | ||
}); | ||
|
||
const attemptConnect = () => { | ||
++connectionAttempts; | ||
this.stdout.write('.'); | ||
client.connect(port, host); | ||
}; | ||
|
||
client.on('error', () => { | ||
// If it's failed to connect 10 times then print failed message | ||
if (connectionAttempts >= 10) { | ||
console.error(' failed to connect, please retry'); | ||
process.exit(1); | ||
} | ||
setTimeout(attemptConnect, 500); | ||
}); | ||
|
||
if (isRemote) { | ||
this.print(`connecting to ${host}:${port} ..`, true); | ||
attemptConnect(); | ||
} else { | ||
this.child.stderr.once('data', () => { | ||
this.print(`connecting to ${host}:${port} ..`, true); | ||
setImmediate(attemptConnect); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
exports.start = function start(argv = process.argv.slice(2), | ||
stdin = process.stdin, | ||
stdout = process.stdout) { | ||
if (argv.length < 1) { | ||
console.error('Usage: node debug script.js'); | ||
console.error(' node debug <host>:<port>'); | ||
console.error(' node debug -p <pid>'); | ||
process.exit(1); | ||
} | ||
|
||
const args = [`--inspect=${exports.port}`, '--debug-brk'].concat(argv); | ||
const inspector = new Inspector(stdin, stdout, args); | ||
|
||
stdin.resume(); | ||
|
||
process.on('uncaughtException', e => { | ||
console.error('There was an internal error in node-inspect. ' + | ||
'Please report this bug.'); | ||
console.error(e.message); | ||
console.error(e.stack); | ||
if (inspector.child) inspector.child.kill(); | ||
process.exit(1); | ||
}); | ||
}; |
Oops, something went wrong.