CrabDude / babel-node-debug

node-inspector's node-debug using babel-node
MIT License
91 stars 11 forks source link

babel-node-inspector? node-inspector + babel #6

Closed CrabDude closed 9 years ago

CrabDude commented 9 years ago

Below copied from email discussion with 3rd party.

There is a debugging scheme I often use with stock node (i.e. without babel):

  1. Start the actual process with --debug flag:

    # node --debug-brk=8010 test.js
    Debugger listening on port 8010
  2. Start the standalone instance of node-inspector:

    # node-inspector --web-host 0.0.0.0
    Node Inspector v0.12.3
    Visit http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5858 to start debugging.
  3. Navigate to the following URL to access the web interface:

    http://<machine-url>:8080/?ws=<machine-url>:8080&port=8010

TLDR; We start a standalone instance of node inspector and tell it to connect to some other V8 debugger.

Is it possible to achieve something similar when using babel?

If I substitute commands with babel-related ones (bode-debug instead of node-inspector and babel-node instead of node) I get the debugger that looks at babel’s internal files, not my script. I reckon it’s because there is no susbstitute for node-inspector command in your package (only for node-debug).

Any help will be greatly appreciated. Have a great day!

CrabDude commented 9 years ago

You can accomplish this with the regular babel-node and node-inspector executables if you add debugger; to the top of your main file and substitute node with babel-node in Step 1 above:

$ babel-node --debug-brk=8010 test.js
Debugger listening on port 8010

As an aside, breaking at the first line of application code was the primary motivation for building babel-node-debug, which turned into a huge hack that only partially works since babel-node-debug can only break on the first line of compiled code, which is accomplished through manually sending very hacky V8 debugger protocol messages to continue execution until the first line of the file with the same name as the main file. It's all very ugly / ad-hoc. Here's an issue, https://github.com/node-inspector/node-inspector/issues/748, I raised on node-inspector to address this limitation.

Building something like babel-node-inspector that does the same thing is possible, I just don't have the time. I'll follow-up with some more details for anyone that wants to try and tackle this.

CrabDude commented 9 years ago

I attempted to implement a babel-node-inspector by augmenting bin/bode-debug.js to invoke node-inspector/bin/inspector.js instead of node-inspector/bin/node-debug.js, but it turns out it's probably impossible.

Below is my attempt. It seems it would 100% work, except there's no way to get a value for config.script. In short, the current hack programmatically adds a breakpoint by pattern matching on the main file's name, which we won't ever have when invoking node-inspector / babel-node-inspector.

The best way / only way to solve this would be for babel-node to programatically add the breakpoint when --debug or --debug-brk is set, which would also conveniently obviate the need for babel-node-debug.

#!/usr/bin/env node

var util = require('util')
var Debugger = require('yadc').Debugger

var OriginalDebugServer = require('node-inspector/lib/debug-server').DebugServer
function DebugServer() {
  var debugServer = this
  OriginalDebugServer.apply(this, arguments)
  var oldStart = debugServer.start
  debugServer.start = function start(config) {
    var returnValue = oldStart.apply(this, arguments)
    continueToMainFile(debugServer, config)
    return returnValue
  }
  return debugServer
}
util.inherits(DebugServer, OriginalDebugServer)
require('node-inspector/lib/debug-server').DebugServer = DebugServer

function continueToMainFile(debugServer, config) {
  debugServer.on('listening', function() {
    setTimeout(function() {
      console.log(config)
      var debug = new Debugger({
        port: config.debugPort,
        host: '127.0.0.1'
      })
      debug.send({command: 'continue'}, function() {
        debug.send({
            command: 'setBreakpoint',
            arguments: {
              type: 'scriptRegExp',
              target: config.script,
              line: 1,
              column: 1
            }
          }, function(err, result) {
            if (err) return
            debug.on('event', function listener(event) {
              if (event.event === 'break') {
                debug.send({
                    command: 'clearbreakpoint',
                    arguments: {
                      type: 'scriptRegExp',
                      breakpoint: result.res.body.breakpoint
                    }
                  })
                debug.client.end()
                debug.removeListener('event', listener)
                debug.removeListener('error', onError)
              }
            })
        })
      })
      function onError(errObj) {}
      debug.on('error', onError)
    }, 1000)
  })
}

require('node-inspector/bin/inspector')
gear54rus commented 9 years ago

Oh wow, so much info. Thanks a ton! I originally didn't create an issue because I thought this was kind of abandoned (the last issue about --save-live-edit is created a month ago), but you really came through here!

Indeed, I can confirm that your proposed solution works (although startup times are obscene on this one):

You can accomplish this with the regular babel-node and node-inspector executables if you add debugger; to the top of your main file and substitute node with babel-node in Step 1 above.

Here's a GIF of it in action (real time, about 20 sec of startup): https://i.imgur.com/Cr1XOlC.gifv

GIF shows debugging of a V8 instance started via child_process.fork (process.execArgv.unshift('--debug-brk=8010'); was used to make it start its debugger) targeted at module that had debugger as its first statement.

As you can see, it needs quite some time to get to the actual code of the application, but it works nevertheless.

I'll be monitoring those issues you created. Thanks!

CrabDude commented 9 years ago

There's not much activity on the project because it's new and very niche (babel's babel-node + node-inspector's node-debug). Also, it seemed to be pretty much "done" in that there weren't any real bugs that needed to be solved in this project.

Thanks for reaching out to me. I think something very good will come of this.