diff --git a/.gitignore b/.gitignore index d8340224..348042ed 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ coverage/ !test/fixtures/demo-app/node_modules/aliyun-egg/node_modules/ !test/fixtures/test-files-glob/** !test/fixtures/test-files-stack/node_modules/ +!test/fixtures/example/node_modules/ +**/run/*.json .tmp .vscode *.log diff --git a/README.md b/README.md index de998871..2c6bc23a 100644 --- a/README.md +++ b/README.md @@ -87,17 +87,17 @@ Debug egg app with [V8 Inspector Integration](https://nodejs.org/api/debugger.ht automatically detect the protocol used by the targeted runtime, 8.0+ the new 'inspector' protocol is used. +use [inspector-proxy](https://github.com/whxaxes/inspector-proxy) to proxy worker debug, so you don't need to worry about reload. + ```bash -$ egg-bin debug +$ egg-bin debug --debug-port=9229 --proxy=9999 ``` ##### options - all `egg-bin dev` options is accepted. -- `--debug-port=6666` worker debug port, default to 9229(inspect) or 5858(debug), also has `--inspect` alias. -- `--debug-brk` whether stop at the top of worker initial script, also has `--brk` alias. -- `--debug-agent=7777` whether debug agent, could pass Number as debugPort, default to 9227(inspect) or 5856(debug), also has `--agent` alias. -- `--debug-agent-brk` whether stop at the top of agent initial script. +- `--proxy=9999` worker debug proxy port. + ### test diff --git a/lib/cmd/debug.js b/lib/cmd/debug.js index 63ea2a4a..7a4adf00 100644 --- a/lib/cmd/debug.js +++ b/lib/cmd/debug.js @@ -1,31 +1,41 @@ 'use strict'; +const cp = require('child_process'); +const chalk = require('chalk'); +const InspectorProxy = require('inspector-proxy'); +const debug = require('debug')('egg-bin'); +const semver = require('semver'); const Command = require('./dev'); +const newDebugger = semver.gte(process.version, '8.0.0'); class DebugCommand extends Command { constructor(rawArgv) { super(rawArgv); - // egg-bin debug --debug-port=6666 --agent=5555 --brk --agent-brk this.usage = 'Usage: egg-bin debug [dir] [options]'; this.options = { - debug: { - alias: 'inspect', - description: 'auto detect the protocol used by the targeted runtime, use inspect at 8.x+', - default: true, - }, - 'debug-port': { - description: 'worker debug port, default to 9229(inspect) or 5858(debug)', + // set default to empty so `--inspect` will always pass to fork + inspect: { + description: 'V8 Inspector port', + default() { + /* istanbul ignore next */ + return newDebugger ? '' : undefined; + }, }, - 'debug-brk': { - alias: 'brk', - description: 'whether stop at the top of worker initial script', + 'inspect-brk': { + description: 'whether break at start', }, - 'debug-agent': { - alias: 'agent', - description: 'whether debug agent, could pass Number as debugPort, default to 9227(inspect) or 5856(debug)', + + debug: { + description: 'legacy debugger', + default() { + /* istanbul ignore next */ + return newDebugger ? undefined : ''; + }, }, - 'debug-agent-brk': { - description: 'whether stop at the top of agent initial script', + + proxy: { + description: 'worker debug proxy port', + default: 9999, }, }; process.env.EGG_DEBUG = 'true'; @@ -35,32 +45,35 @@ class DebugCommand extends Command { return 'Start server at local debug mode'; } - get context() { - const context = super.context; - const { argv, execArgvObj, debugOptions, debugPort } = context; - - // use debugPort extract from `--inspect=9999 / --debug-port=1111` etc, if not provide just pass true - argv.debug = debugPort || true; + * run(context) { + const proxyPort = context.argv.proxy; + context.argv.proxy = undefined; - if (debugOptions['inspect-brk'] || debugOptions['debug-brk']) { - argv.debugBrk = true; - } - - // remove unused - argv['debug-port'] = undefined; - argv['debug-brk'] = undefined; - argv['debug-agent'] = undefined; - argv['debug-agent-brk'] = undefined; + const eggArgs = yield this.formatArgs(context); + const options = { + execArgv: context.execArgv, + env: Object.assign({ NODE_ENV: 'development', EGG_DEBUG: true }, context.env), + }; + debug('%s %j %j, %j', this.serverBin, eggArgs, options.execArgv, options.env.NODE_ENV); - // remove all debug options from execArgv - for (const key of Object.keys(debugOptions)) { - execArgvObj[key] = undefined; - } + // start egg + const child = cp.fork(this.serverBin, eggArgs, options); - // recreate execArgv array - context.execArgv = this.helper.unparseArgv(execArgvObj); + // start debug proxy + const proxy = new InspectorProxy({ port: proxyPort }); + // proxy to new worker + child.on('message', msg => { + if (msg && msg.action === 'debug' && msg.from === 'app') { + const { debugPort, pid } = msg.data; + debug(`recieve new worker#${pid} debugPort: ${debugPort}`); + proxy.start({ debugPort }).then(() => { + console.log(chalk.yellow(`Debug Proxy online, now you could attach to ${proxyPort} without worry about reload.`)); + if (newDebugger) console.log(chalk.yellow(`DevTools → ${proxy.url}`)); + }); + } + }); - return context; + child.on('exit', () => proxy.end()); } } diff --git a/package.json b/package.json index 91f64f73..0673a782 100644 --- a/package.json +++ b/package.json @@ -9,17 +9,20 @@ }, "dependencies": { "autod": "^2.9.0", + "chalk": "^2.1.0", "co-mocha": "^1.2.0", "common-bin": "^2.7.0", "debug": "^3.0.1", "detect-port": "^1.2.1", "egg-utils": "^2.2.0", "globby": "^6.1.0", + "inspector-proxy": "^1.0.0", "intelli-espower-loader": "^1.0.1", - "mocha": "^3.5.0", + "mocha": "^3.5.3", "mz-modules": "^2.0.0", - "nyc": "^11.2.0", + "nyc": "^11.2.1", "power-assert": "^1.4.4", + "semver": "^5.4.1", "test-exclude": "^4.1.1", "ypkgfiles": "^1.4.0" }, @@ -29,12 +32,14 @@ "babel-register": "^6.4.3", "coffee": "^4.1.0", "cross-env": "^3.1.3", + "egg": "^1.8.0", "egg-ci": "^1.8.0", + "egg-mock": "^3.12.0", "enzyme": "^2.0.0", "eslint": "^4.6.1", "eslint-config-egg": "^5.1.1", "jsdom": "^8.0.1", - "mm": "^2.1.0", + "mm": "^2.2.0", "mz": "^2.6.0", "react": "^0.14.7", "react-addons-test-utils": "^0.14.7", @@ -71,4 +76,4 @@ "ci": { "version": "6, 8" } -} \ No newline at end of file +} diff --git a/test/fixtures/example/app/router.js b/test/fixtures/example/app/router.js new file mode 100644 index 00000000..8bfd2442 --- /dev/null +++ b/test/fixtures/example/app/router.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = app => { + app.get('/', function* () { + this.body = 'hi, egg'; + }); +}; diff --git a/test/fixtures/example/config/config.default.js b/test/fixtures/example/config/config.default.js new file mode 100644 index 00000000..762c2647 --- /dev/null +++ b/test/fixtures/example/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.key = '12345'; diff --git a/test/fixtures/example/node_modules/egg/index.js b/test/fixtures/example/node_modules/egg/index.js new file mode 100644 index 00000000..2060e8f7 --- /dev/null +++ b/test/fixtures/example/node_modules/egg/index.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = require('../../../../../node_modules/egg'); + +setTimeout(() => { + console.log('exit by master test end') + process.exit(0); +}, 5000); diff --git a/test/fixtures/example/node_modules/egg/package.json b/test/fixtures/example/node_modules/egg/package.json new file mode 100644 index 00000000..6697ad3f --- /dev/null +++ b/test/fixtures/example/node_modules/egg/package.json @@ -0,0 +1,3 @@ +{ + "name": "egg" +} diff --git a/test/fixtures/example/package.json b/test/fixtures/example/package.json new file mode 100644 index 00000000..8389bd69 --- /dev/null +++ b/test/fixtures/example/package.json @@ -0,0 +1,3 @@ +{ + "name": "example" +} \ No newline at end of file diff --git a/test/lib/cmd/debug.test.js b/test/lib/cmd/debug.test.js index 9ca0d554..59cb0992 100644 --- a/test/lib/cmd/debug.test.js +++ b/test/lib/cmd/debug.test.js @@ -2,8 +2,9 @@ const path = require('path'); const coffee = require('coffee'); -const mm = require('mm'); +const mm = require('egg-mock'); const net = require('net'); +const semver = require('semver'); describe('test/lib/cmd/debug.test.js', () => { const eggBin = require.resolve('../../../bin/egg-bin.js'); @@ -13,64 +14,11 @@ describe('test/lib/cmd/debug.test.js', () => { it('should startCluster success', () => { return coffee.fork(eggBin, [ 'debug' ], { cwd }) - .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debug":true/) - .notExpect('stdout', /process.execArgv:/) - .expect('code', 0) - .end(); - }); - - it('--debug-port=7777', () => { - return coffee.fork(eggBin, [ 'debug', '--debug-port=7777' ], { cwd }) - // .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debug":7777/) - .notExpect('stdout', /process.execArgv:/) - .expect('code', 0) - .end(); - }); - - it('--inspect=7777', () => { - return coffee.fork(eggBin, [ 'debug', '--inspect=7777' ], { cwd }) - // .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debug":7777/) - .notExpect('stdout', /options: {.*"inspect"/) - .notExpect('stdout', /process.execArgv:/) - .expect('code', 0) - .end(); - }); - - it('--debug-brk --debug-agent --debug-agent-brk --inspect-brk', () => { - return coffee.fork(eggBin, [ 'debug', '--debug-brk', '--debug-agent', '--debug-agent-brk', '--inspect-brk' ], { cwd }) - // .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debugBrk":true,"debugAgent":true,"debugAgentBrk":true,"debug":true/) - .notExpect('stdout', /options: {.*"inspect"/) - .notExpect('stdout', /process.execArgv:/) - .expect('code', 0) - .end(); - }); - - it('--brk --agent', () => { - return coffee.fork(eggBin, [ 'debug', '--brk', '--agent' ], { cwd }) // .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debugBrk":true,"debugAgent":true,"debug":true/) - .notExpect('stdout', /options: {.*"inspect"/) - .notExpect('stdout', /process.execArgv:/) - .expect('code', 0) - .end(); - }); - - it('--agent=6666', () => { - return coffee.fork(eggBin, [ 'debug', '--agent=6666' ], { cwd }) - // .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {"debugAgent":6666,"debug":true/) - .notExpect('stdout', /options: {.*"inspect"/) - .notExpect('stdout', /process.execArgv:/) + .expect('stderr', /Debugger listening/) + // node 8 missing "chrome-devtools" url + // .expect('stderr', /chrome-devtools:/) + .expect('stdout', /"workers":1/) .expect('code', 0) .end(); }); @@ -78,8 +26,7 @@ describe('test/lib/cmd/debug.test.js', () => { it('should startCluster with port', () => { return coffee.fork(eggBin, [ 'debug', '--port', '6001' ], { cwd }) // .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debug":true/) + .expect('stderr', /Debugger listening/) .expect('stdout', /"port":6001/) .expect('stdout', /"workers":1/) .expect('stdout', /"baseDir":".*?demo-app"/) @@ -89,12 +36,10 @@ describe('test/lib/cmd/debug.test.js', () => { }); it('should debug with $NODE_DEBUG_OPTION', () => { - const env = Object.assign({}, process.env, { NODE_DEBUG_OPTION: '--inspect-brk=6666' }); + const env = Object.assign({}, process.env, { NODE_DEBUG_OPTION: '--inspect=5555' }); return coffee.fork(eggBin, [ 'debug' ], { cwd, env }) - .debug() - .notExpect('stderr', /Debugger listening/) - .expect('stdout', /options: {.*"debug":6666/) - .expect('stdout', /options: {.*"debugBrk":true/) + // .debug() + .expect('stderr', /Debugger listening.*5555/) .expect('stdout', /"workers":1/) .expect('code', 0) .end(); @@ -118,4 +63,29 @@ describe('test/lib/cmd/debug.test.js', () => { .end(); }); }); + + describe('real egg', () => { + const cwd = path.join(__dirname, '../../fixtures/example'); + const newDebugger = semver.gte(process.version, '8.0.0'); + + it('should proxy', function* () { + const app = coffee.fork(eggBin, [ 'debug' ], { cwd }); + // app.debug(); + if (newDebugger) app.expect('stdout', /DevTools → chrome-devtools:.*:9999/); + yield app.expect('stderr', /Debugger listening/) + .expect('stdout', /Debug Proxy online, now you could attach to 9999/) + .expect('code', 0) + .end(); + }); + + it('should proxy with port', function* () { + const app = coffee.fork(eggBin, [ 'debug', '--proxy=6666' ], { cwd }); + // app.debug(); + if (newDebugger) app.expect('stdout', /DevTools → chrome-devtools:.*:6666/); + yield app.expect('stderr', /Debugger listening/) + .expect('stdout', /Debug Proxy online, now you could attach to 6666/) + .expect('code', 0) + .end(); + }); + }); }); diff --git a/test/lib/cmd/dev.test.js b/test/lib/cmd/dev.test.js index 36411684..cdaf7c13 100644 --- a/test/lib/cmd/dev.test.js +++ b/test/lib/cmd/dev.test.js @@ -130,14 +130,6 @@ describe('test/lib/cmd/dev.test.js', () => { }); }); - it.skip('should startCluster with execArgv --debug', done => { - coffee.fork(eggBin, [ 'dev', '--debug=7000' ], { cwd }) - .debug() - .expect('stderr', /Debugger listening on .*7000/) - .expect('code', 0) - .end(done); - }); - it('should startCluster with execArgv --inspect', done => { coffee.fork(eggBin, [ 'dev', '--inspect=7000' ], { cwd }) // .debug()