Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.

Commit 985873c

Browse files
author
Jan Krems
committed
feat: Launching the example works
1 parent 44c4c79 commit 985873c

File tree

6 files changed

+391
-3
lines changed

6 files changed

+391
-3
lines changed

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "groupon-es5"
2+
"extends": "groupon-node6"
33
}

cli.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env node
2+
require('./lib/cli.js');

examples/myscript.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* eslint no-restricted-syntax: 0, no-debugger: 0 */
2+
'use strict';
3+
const x = process.argv[2] || 'world';
4+
setTimeout(() => {
5+
debugger;
6+
console.log(x);
7+
}, 1000);
8+
console.log('hello');

lib/cli.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
// ~= NativeModule.require('_debugger').start();
3+
require('./node-inspect').start();

lib/node-inspect.js

Lines changed: 375 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,375 @@
1-
'use strict';
1+
'use strict'; /* eslint no-underscore-dangle: 0 */
2+
// Our equivalent of '_debugger' in node itthis
3+
const assert = require('assert');
4+
const repl = require('repl');
5+
const spawn = require('child_process').spawn;
6+
const util = require('util');
7+
8+
exports.port = process.debugPort;
9+
10+
class Client {
11+
once() {}
12+
destroy() {}
13+
on() {}
14+
connect() {}
15+
}
16+
17+
// This class is the repl-enabled debugger interface which is invoked on
18+
// "node-inspect"
19+
class Inspector {
20+
constructor(stdin, stdout, args) {
21+
this.stdin = stdin;
22+
this.stdout = stdout;
23+
this.args = args;
24+
25+
// Two eval modes are available: controlEval and debugEval
26+
// But controlEval is used by default
27+
const opts = {
28+
prompt: 'debug> ',
29+
input: this.stdin,
30+
output: this.stdout,
31+
eval: (code, ctx, file, cb) => this.controlEval(code, ctx, file, cb),
32+
useGlobal: false,
33+
ignoreUndefined: true,
34+
};
35+
if (parseInt(process.env.NODE_NO_READLINE, 10)) {
36+
opts.terminal = false;
37+
} else if (parseInt(process.env.NODE_FORCE_READLINE, 10)) {
38+
opts.terminal = true;
39+
40+
// Emulate Ctrl+C if we're emulating terminal
41+
if (!this.stdout.isTTY) {
42+
process.on('SIGINT', () => {
43+
this.repl.rli.emit('SIGINT');
44+
});
45+
}
46+
}
47+
if (parseInt(process.env.NODE_DISABLE_COLORS, 10)) {
48+
opts.useColors = false;
49+
}
50+
51+
this.repl = repl.start(opts);
52+
53+
// Do not print useless warning
54+
repl._builtinLibs.splice(repl._builtinLibs.indexOf('repl'), 1);
55+
56+
// Kill child process when main process dies
57+
this.repl.on('exit', () => {
58+
process.exit(0);
59+
});
60+
61+
// Handle all possible exits
62+
process.on('exit', () => this.killChild());
63+
process.once('SIGTERM', process.exit.bind(process, 0));
64+
process.once('SIGHUP', process.exit.bind(process, 0));
65+
66+
// const proto = Inspector.prototype;
67+
// const ignored = ['pause', 'resume', 'exitRepl', 'handleBreak',
68+
// 'requireConnection', 'killChild', 'trySpawn',
69+
// 'controlEval', 'debugEval', 'print', 'childPrint',
70+
// 'clearline'];
71+
// const shortcut = {
72+
// run: 'r',
73+
// cont: 'c',
74+
// next: 'n',
75+
// step: 's',
76+
// out: 'o',
77+
// backtrace: 'bt',
78+
// setBreakpoint: 'sb',
79+
// clearBreakpoint: 'cb',
80+
// pause_: 'pause',
81+
// };
82+
83+
// function defineProperty(key, protoKey) {
84+
// // Check arity
85+
// const fn = proto[protoKey].bind(this);
86+
87+
// if (proto[protoKey].length === 0) {
88+
// Object.defineProperty(this.repl.context, key, {
89+
// get: fn,
90+
// enumerable: true,
91+
// configurable: false,
92+
// });
93+
// } else {
94+
// this.repl.context[key] = fn;
95+
// }
96+
// }
97+
98+
// // Copy all prototype methods in repl context
99+
// // Setup them as getters if possible
100+
// for (var i in proto) {
101+
// if (Object.prototype.hasOwnProperty.call(proto, i) &&
102+
// ignored.indexOf(i) === -1) {
103+
// defineProperty(i, i);
104+
// if (shortcut[i]) defineProperty(shortcut[i], i);
105+
// }
106+
// }
107+
108+
this.killed = false;
109+
this.waiting = null;
110+
this.paused = 0;
111+
this.context = this.repl.context;
112+
this.history = { debug: [], control: [] };
113+
this.breakpoints = [];
114+
this._watchers = [];
115+
116+
// Run script automatically
117+
this.pause();
118+
119+
setImmediate(() => { this.run(); });
120+
}
121+
122+
// Clear current line
123+
clearline() {
124+
if (this.stdout.isTTY) {
125+
this.stdout.cursorTo(0);
126+
this.stdout.clearLine(1);
127+
} else {
128+
this.stdout.write('\b');
129+
}
130+
}
131+
132+
// Print text to output stream
133+
print(text, oneline) {
134+
if (this.killed) return;
135+
this.clearline();
136+
137+
this.stdout.write(typeof text === 'string' ? text : util.inspect(text));
138+
139+
if (oneline !== true) {
140+
this.stdout.write('\n');
141+
}
142+
}
143+
144+
// Format and print text from child process
145+
childPrint(text) {
146+
this.print(
147+
text.toString()
148+
.split(/\r\n|\r|\n/g)
149+
.filter(chunk => !!chunk)
150+
.map(chunk => `< ${chunk}`)
151+
.join('\n')
152+
);
153+
this.repl.displayPrompt(true);
154+
}
155+
156+
// Errors formatting
157+
error(text) {
158+
this.print(text);
159+
this.resume();
160+
}
161+
162+
163+
// Stream control
164+
165+
166+
pause() {
167+
if (this.killed || this.paused++ > 0) return this;
168+
this.repl.rli.pause();
169+
this.stdin.pause();
170+
return this;
171+
}
172+
173+
resume(silent) {
174+
if (this.killed || this.paused === 0 || --this.paused !== 0) return this;
175+
this.repl.rli.resume();
176+
if (silent !== true) {
177+
this.repl.displayPrompt();
178+
}
179+
this.stdin.resume();
180+
181+
if (this.waiting) {
182+
this.waiting();
183+
this.waiting = null;
184+
}
185+
return this;
186+
}
187+
188+
189+
// Commands
190+
191+
192+
// // Print help message
193+
// help() {
194+
// this.print(helpMessage);
195+
// }
196+
197+
198+
// Run script
199+
run(callback) {
200+
if (this.child) {
201+
this.error('App is already running... Try `restart` instead');
202+
if (callback) callback(true);
203+
} else {
204+
this.trySpawn(callback);
205+
}
206+
}
207+
208+
209+
// Quit
210+
quit() {
211+
this.killChild();
212+
process.exit(0);
213+
}
214+
215+
// Kills child process
216+
killChild() {
217+
if (this.child) {
218+
this.child.kill();
219+
this.child = null;
220+
}
221+
222+
if (this.client) {
223+
// Save breakpoints
224+
this.breakpoints = this.client.breakpoints;
225+
226+
this.client.destroy();
227+
this.client = null;
228+
}
229+
}
230+
231+
// Spawns child process (and restores breakpoints)
232+
trySpawn(cb) {
233+
const breakpoints = this.breakpoints || [];
234+
let port = exports.port;
235+
let host = '127.0.0.1';
236+
let childArgs = this.args;
237+
238+
this.killChild();
239+
assert(!this.child);
240+
241+
let isRemote = false;
242+
if (this.args.length === 2) {
243+
const match = this.args[1].match(/^([^:]+):(\d+)$/);
244+
245+
if (match) {
246+
// Connecting to remote debugger
247+
// `node debug localhost:5858`
248+
host = match[1];
249+
port = parseInt(match[2], 10);
250+
isRemote = true;
251+
}
252+
} else if (this.args.length === 3) {
253+
// `node debug -p pid`
254+
if (this.args[1] === '-p' && /^\d+$/.test(this.args[2])) {
255+
const pid = parseInt(this.args[2], 10);
256+
try {
257+
process._debugProcess(pid);
258+
} catch (e) {
259+
if (e.code === 'ESRCH') {
260+
console.error(`Target process: ${pid} doesn't exist.`);
261+
process.exit(1);
262+
}
263+
throw e;
264+
}
265+
isRemote = true;
266+
} else {
267+
const match = this.args[1].match(/^--port=(\d+)$/);
268+
if (match) {
269+
// Start debugger on custom port
270+
// `node debug --port=5858 app.js`
271+
port = parseInt(match[1], 10);
272+
childArgs = [`--inspect=${port}`, '--debug-brk'].concat(this.args.slice(2));
273+
}
274+
}
275+
}
276+
277+
if (!isRemote) {
278+
// pipe stream into debugger
279+
this.child = spawn(process.execPath, childArgs);
280+
281+
this.child.stdout.on('data', text => this.childPrint(text));
282+
this.child.stderr.on('data', text => this.childPrint(text));
283+
}
284+
285+
this.pause();
286+
287+
const client = this.client = new Client();
288+
let connectionAttempts = 0;
289+
290+
client.once('ready', () => {
291+
this.stdout.write(' ok\n');
292+
293+
// Restore breakpoints
294+
breakpoints.forEach(bp => {
295+
this.print(`Restoring breakpoint ${bp.scriptReq}: ${bp.line}`);
296+
this.setBreakpoint(bp.scriptReq, bp.line, bp.condition, true);
297+
});
298+
299+
client.on('close', () => {
300+
this.pause();
301+
this.print('program terminated');
302+
this.resume();
303+
this.client = null;
304+
this.killChild();
305+
});
306+
307+
if (cb) cb();
308+
this.resume();
309+
});
310+
311+
client.on('unhandledResponse', res => {
312+
this.pause();
313+
this.print(`\nunhandled res:${JSON.stringify(res)}`);
314+
this.resume();
315+
});
316+
317+
client.on('break', res => {
318+
this.handleBreak(res.body);
319+
});
320+
321+
client.on('exception', res => {
322+
this.handleBreak(res.body);
323+
});
324+
325+
const attemptConnect = () => {
326+
++connectionAttempts;
327+
this.stdout.write('.');
328+
client.connect(port, host);
329+
};
330+
331+
client.on('error', () => {
332+
// If it's failed to connect 10 times then print failed message
333+
if (connectionAttempts >= 10) {
334+
console.error(' failed to connect, please retry');
335+
process.exit(1);
336+
}
337+
setTimeout(attemptConnect, 500);
338+
});
339+
340+
if (isRemote) {
341+
this.print(`connecting to ${host}:${port} ..`, true);
342+
attemptConnect();
343+
} else {
344+
this.child.stderr.once('data', () => {
345+
this.print(`connecting to ${host}:${port} ..`, true);
346+
setImmediate(attemptConnect);
347+
});
348+
}
349+
}
350+
}
351+
352+
exports.start = function start(argv = process.argv.slice(2),
353+
stdin = process.stdin,
354+
stdout = process.stdout) {
355+
if (argv.length < 1) {
356+
console.error('Usage: node debug script.js');
357+
console.error(' node debug <host>:<port>');
358+
console.error(' node debug -p <pid>');
359+
process.exit(1);
360+
}
361+
362+
const args = [`--inspect=${exports.port}`, '--debug-brk'].concat(argv);
363+
const inspector = new Inspector(stdin, stdout, args);
364+
365+
stdin.resume();
366+
367+
process.on('uncaughtException', e => {
368+
console.error('There was an internal error in node-inspect. ' +
369+
'Please report this bug.');
370+
console.error(e.message);
371+
console.error(e.stack);
372+
if (inspector.child) inspector.child.kill();
373+
process.exit(1);
374+
});
375+
};

0 commit comments

Comments
 (0)