123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- #!/usr/bin/env node
- var fork = require('child_process').fork;
- var fs = require('fs');
- var path = require('path');
- var util = require('util');
- var open = require('opener');
- var yargs = require('yargs');
- var whichSync = require('which').sync;
- var inspector = require('..');
- var WIN_CMD_LINK_MATCHER = /node "%~dp0\\(.*?)"/;
- var argvOptions = {
- 'debug-brk': {
- alias: 'b',
- default: true,
- description: 'Break on the first line (`node --debug-brk`)'
- },
- 'web-port': {
- alias: ['p', 'port'],
- type: 'number',
- description: 'Node Inspector port (`node-inspector --web-port={port}`)'
- },
- 'debug-port': {
- alias: 'd',
- type: 'number',
- description: 'Node/V8 debugger port (`node --debug={port}`)'
- },
- nodejs: {
- type: 'string',
- description: 'Pass NodeJS options to debugged process (`node --option={value}`)\n' +
- 'Usage example: node-debug --nodejs --harmony --nodejs --random_seed=2 app'
- },
- cli: {
- alias: 'c',
- type: 'boolean',
- description: 'CLI mode, do not open browser.'
- },
- version: {
- alias: 'v',
- type: 'boolean',
- description: 'Print Node Inspector\'s version.'
- },
- help: {
- alias: 'h',
- type: 'boolean',
- description: 'Show this help.'
- }
- };
- var argvParser = createYargs();
- module.exports = main;
- module.exports.parseArgs = parseArgs;
- if (require.main == module)
- main();
- //-- Implementation --
- var config;
- /**
- * By default:
- *
- * 1. Runs node-inspector.
- * 2. Runs the supplied script in debug mode
- * 3. Opens the user's browser, pointing it at the inspector.
- *
- * NOTE: Finishes with a call to process.exit() when running without arguments
- * or when an error occured.
- */
- function main() {
- config = parseArgs(process.argv);
- if (config.options.help) {
- argvParser.showHelp(console.log);
- console.log('The [script] argument is resolved relative to the current working\n' +
- 'directory. If no such file exists, then env.PATH is searched.\n');
- console.log('The default mode is to break on the first line of the script, to run\n' +
- 'immediately on start use `--no-debug-brk` or press the Resume button.\n');
- console.log('When there is no script specified, the module in the current working\n' +
- 'directory is loaded in the REPL session as `m`. This allows you to call\n' +
- 'and debug arbitrary functions exported by the current module.\n');
- process.exit();
- }
- if (config.options.version) {
- console.log('v' + require('../package.json').version);
- process.exit();
- }
- startInspector(function(err) {
- if (err) {
- console.error(formatNodeInspectorError(err));
- process.exit(1);
- }
- startDebuggedProcess(function(err) {
- if (err) {
- console.error(
- 'Cannot start %s: %s',
- config.subproc.script,
- err.message || err
- );
- process.exit(2);
- }
- openBrowserAndPrintInfo();
- });
- });
- }
- function parseArgs(argv) {
- argv = argv.slice(2);
- //Preparse --nodejs options
- var nodejsArgs = [];
- var nodejsIndex = argv.indexOf('--nodejs');
- while (nodejsIndex !== -1) {
- var nodejsArg = argv.splice(nodejsIndex, 2)[1];
- if (nodejsArg !== undefined) {
- nodejsArgs.push(nodejsArg);
- }
- nodejsIndex = argv.indexOf('--nodejs');
- }
- var options = argvParser.parse(argv);
- var script = options._[0];
- var printScript = true;
- var subprocArgs;
- if (script) {
- // We want to pass along subarguments, but re-parse our arguments.
- subprocArgs = argv.splice(argv.indexOf(script) + 1);
- } else {
- script = require.resolve('./run-repl');
- subprocArgs = [];
- printScript = false;
- process.env.CMD = process.env.CMD || process.argv[1];
- }
- options = argvParser.parse(argv);
- var subprocPort = options['debug-port'] || 5858;
- var subprocExecArgs = ['--debug=' + subprocPort].concat(nodejsArgs);
- if (options['debug-brk']) {
- subprocExecArgs.push('--debug-brk');
- }
- var inspectorPort = options['web-port'] || 8080;
- var inspectorArgs = extractPassThroughArgs(options, argvOptions)
- .concat(['--web-port=' + inspectorPort]);
- return {
- printScript: printScript,
- options: options,
- subproc: {
- script: script,
- args: subprocArgs,
- execArgs: subprocExecArgs,
- debugPort: subprocPort
- },
- inspector: {
- port: inspectorPort,
- args: inspectorArgs
- }
- };
- }
- function createYargs() {
- var y = yargs
- .options(argvOptions)
- .usage('Usage:\n' +
- ' $0 [node-inspector-options] [options] script [script-arguments]');
- y.$0 = getCmd();
- return y;
- }
- function getCmd() {
- return process.env.CMD || path.basename(process.argv[1]);
- }
- function extractPassThroughArgs(options, argvOptions) {
- var result = [];
- var optionsToSkip = { _: true, $0: true };
- // Skip options handled by node-debug
- Object.keys(argvOptions).forEach(function(key) {
- optionsToSkip[key] = true;
- var alias = argvOptions[key].alias;
- if (Array.isArray(alias)) {
- alias.forEach(function(opt) { optionsToSkip[opt] = true; });
- } else if (alias) {
- optionsToSkip[alias] = true;
- }
- });
- // Filter options not handled by node-debug
- Object.keys(options).forEach(function(key) {
- //Filter options handled by node-debug
- if (optionsToSkip[key]) return;
- //Filter camelKey options created by yargs
- if (/[A-Z]/.test(key)) return;
-
- var value = options[key];
- if (value === undefined) return;
- if (value === true) {
- result.push('--' + key);
- } else if (value === false) {
- result.push('--no-' + key);
- } else {
- result.push('--' + key);
- result.push(value);
- }
- });
- return result;
- }
- function startInspector(callback) {
- var inspectorProcess = fork(
- require.resolve('./inspector'),
- config.inspector.args,
- { silent: true }
- );
- inspectorProcess.once('message', function(msg) {
- switch (msg.event) {
- case 'SERVER.LISTENING':
- return callback(null, msg.address);
- case 'SERVER.ERROR':
- return callback(msg.error);
- default:
- console.warn('Unknown Node Inspector event: %s', msg.event);
- return callback(
- null,
- {
- address: 'localhost',
- port: config.inspector.port
- }
- );
- }
- });
- process.on('exit', function() {
- inspectorProcess.kill();
- });
- }
- function formatNodeInspectorError(err) {
- var reason = err.message || err.code || err;
- if (err.code === 'EADDRINUSE') {
- reason += '\nThere is another process already listening at 0.0.0.0:' +
- config.inspector.port + '.\n' +
- 'Run `' + getCmd() + ' -p {port}` to use a different port.';
- }
- return util.format('Cannot start Node Inspector:', reason);
- }
- function startDebuggedProcess(callback) {
- var script = path.resolve(process.cwd(), config.subproc.script);
- if (!fs.existsSync(script)) {
- try {
- script = whichSync(config.subproc.script);
- script = checkWinCmdFiles(script);
- } catch (err) {
- return callback(err);
- }
- }
- var debuggedProcess = fork(
- script,
- config.subproc.args,
- {
- execArgv: config.subproc.execArgs
- }
- );
- debuggedProcess.on('exit', function() { process.exit(); });
- callback();
- }
- function checkWinCmdFiles(script) {
- if (process.platform == 'win32' && path.extname(script).toLowerCase() == '.cmd') {
- var cmdContent = '' + fs.readFileSync(script);
- var link = (WIN_CMD_LINK_MATCHER.exec(cmdContent) || [])[1];
-
- if (link) script = path.resolve(path.dirname(script), link);
- }
- return script;
- }
- function openBrowserAndPrintInfo() {
- var url = inspector.buildInspectorUrl(
- 'localhost',
- config.inspector.port,
- config.subproc.debugPort
- );
- if (!config.options.cli) {
- open(url);
- }
- console.log('Node Inspector is now available from %s', url);
- if (config.printScript)
- console.log('Debugging `%s`\n', config.subproc.script);
- }
|