mirror of https://github.com/lukechilds/node.git
Browse Source
Squashed from: - deps: Add node-inspect 1.10.1 This adds a reimplementation of the old CLI debugger (`node debug`) against the new debugger protocol (`node --inspect`). This is necessary because the old protocol won't be supported in future versions of V8. - deps: Update node-inspect to 1.10.2 Starting with 1.10.2 the test suite should pass consistently on windows. - deps: Update to node-inspect 1.10.4 PR-URL: https://github.com/nodejs/node/pull/10187 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>v6
committed by
Anna Henningsen
48 changed files with 3792 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
@ -0,0 +1,147 @@ |
|||
root: true |
|||
|
|||
env: |
|||
node: true |
|||
es6: true |
|||
|
|||
parserOptions: |
|||
ecmaVersion: 2016 |
|||
|
|||
rules: |
|||
# Possible Errors |
|||
# http://eslint.org/docs/rules/#possible-errors |
|||
comma-dangle: [2, only-multiline] |
|||
no-control-regex: 2 |
|||
no-debugger: 2 |
|||
no-dupe-args: 2 |
|||
no-dupe-keys: 2 |
|||
no-duplicate-case: 2 |
|||
no-empty-character-class: 2 |
|||
no-ex-assign: 2 |
|||
no-extra-boolean-cast: 2 |
|||
no-extra-parens: [2, functions] |
|||
no-extra-semi: 2 |
|||
no-func-assign: 2 |
|||
no-invalid-regexp: 2 |
|||
no-irregular-whitespace: 2 |
|||
no-obj-calls: 2 |
|||
no-proto: 2 |
|||
no-template-curly-in-string: 2 |
|||
no-unexpected-multiline: 2 |
|||
no-unreachable: 2 |
|||
no-unsafe-negation: 2 |
|||
use-isnan: 2 |
|||
valid-typeof: 2 |
|||
|
|||
# Best Practices |
|||
# http://eslint.org/docs/rules/#best-practices |
|||
dot-location: [2, property] |
|||
no-fallthrough: 2 |
|||
no-global-assign: 2 |
|||
no-multi-spaces: 2 |
|||
no-octal: 2 |
|||
no-redeclare: 2 |
|||
no-self-assign: 2 |
|||
no-unused-labels: 2 |
|||
no-useless-call: 2 |
|||
no-useless-escape: 2 |
|||
no-void: 2 |
|||
no-with: 2 |
|||
|
|||
# Strict Mode |
|||
# http://eslint.org/docs/rules/#strict-mode |
|||
strict: [2, global] |
|||
|
|||
# Variables |
|||
# http://eslint.org/docs/rules/#variables |
|||
no-delete-var: 2 |
|||
no-undef: 2 |
|||
no-unused-vars: [2, {args: none}] |
|||
|
|||
# Node.js and CommonJS |
|||
# http://eslint.org/docs/rules/#nodejs-and-commonjs |
|||
no-mixed-requires: 2 |
|||
no-new-require: 2 |
|||
no-path-concat: 2 |
|||
no-restricted-modules: [2, sys, _linklist] |
|||
no-restricted-properties: [2, { |
|||
object: assert, |
|||
property: deepEqual, |
|||
message: Please use assert.deepStrictEqual(). |
|||
}, { |
|||
property: __defineGetter__, |
|||
message: __defineGetter__ is deprecated. |
|||
}, { |
|||
property: __defineSetter__, |
|||
message: __defineSetter__ is deprecated. |
|||
}] |
|||
|
|||
# Stylistic Issues |
|||
# http://eslint.org/docs/rules/#stylistic-issues |
|||
brace-style: [2, 1tbs, {allowSingleLine: true}] |
|||
comma-spacing: 2 |
|||
comma-style: 2 |
|||
computed-property-spacing: 2 |
|||
eol-last: 2 |
|||
func-call-spacing: 2 |
|||
func-name-matching: 2 |
|||
indent: [2, 2, {SwitchCase: 1, MemberExpression: 1}] |
|||
key-spacing: [2, {mode: minimum}] |
|||
keyword-spacing: 2 |
|||
linebreak-style: [2, unix] |
|||
max-len: [2, 80, 2] |
|||
new-parens: 2 |
|||
no-mixed-spaces-and-tabs: 2 |
|||
no-multiple-empty-lines: [2, {max: 2, maxEOF: 0, maxBOF: 0}] |
|||
no-tabs: 2 |
|||
no-trailing-spaces: 2 |
|||
quotes: [2, single, avoid-escape] |
|||
semi: 2 |
|||
semi-spacing: 2 |
|||
space-before-blocks: [2, always] |
|||
space-before-function-paren: [2, never] |
|||
space-in-parens: [2, never] |
|||
space-infix-ops: 2 |
|||
space-unary-ops: 2 |
|||
|
|||
# ECMAScript 6 |
|||
# http://eslint.org/docs/rules/#ecmascript-6 |
|||
arrow-parens: [2, always] |
|||
arrow-spacing: [2, {before: true, after: true}] |
|||
constructor-super: 2 |
|||
no-class-assign: 2 |
|||
no-confusing-arrow: 2 |
|||
no-const-assign: 2 |
|||
no-dupe-class-members: 2 |
|||
no-new-symbol: 2 |
|||
no-this-before-super: 2 |
|||
prefer-const: [2, {ignoreReadBeforeAssign: true}] |
|||
rest-spread-spacing: 2 |
|||
template-curly-spacing: 2 |
|||
|
|||
# Custom rules in tools/eslint-rules |
|||
align-function-arguments: 2 |
|||
align-multiline-assignment: 2 |
|||
assert-fail-single-argument: 2 |
|||
new-with-error: [2, Error, RangeError, TypeError, SyntaxError, ReferenceError] |
|||
|
|||
# Global scoped method and vars |
|||
globals: |
|||
COUNTER_HTTP_CLIENT_REQUEST: false |
|||
COUNTER_HTTP_CLIENT_RESPONSE: false |
|||
COUNTER_HTTP_SERVER_REQUEST: false |
|||
COUNTER_HTTP_SERVER_RESPONSE: false |
|||
COUNTER_NET_SERVER_CONNECTION: false |
|||
COUNTER_NET_SERVER_CONNECTION_CLOSE: false |
|||
DTRACE_HTTP_CLIENT_REQUEST: false |
|||
DTRACE_HTTP_CLIENT_RESPONSE: false |
|||
DTRACE_HTTP_SERVER_REQUEST: false |
|||
DTRACE_HTTP_SERVER_RESPONSE: false |
|||
DTRACE_NET_SERVER_CONNECTION: false |
|||
DTRACE_NET_STREAM_END: false |
|||
LTTNG_HTTP_CLIENT_REQUEST: false |
|||
LTTNG_HTTP_CLIENT_RESPONSE: false |
|||
LTTNG_HTTP_SERVER_REQUEST: false |
|||
LTTNG_HTTP_SERVER_RESPONSE: false |
|||
LTTNG_NET_SERVER_CONNECTION: false |
|||
LTTNG_NET_STREAM_END: false |
@ -0,0 +1,4 @@ |
|||
node_modules/ |
|||
npm-debug.log |
|||
/tmp |
|||
/.vs |
@ -0,0 +1 @@ |
|||
registry=https://registry.npmjs.org |
@ -0,0 +1,13 @@ |
|||
language: node_js |
|||
node_js: |
|||
- '6.8' |
|||
before_deploy: |
|||
- git config --global user.email "jan.krems@gmail.com" |
|||
- git config --global user.name "Jan Krems" |
|||
deploy: |
|||
provider: script |
|||
script: ./node_modules/.bin/nlm release |
|||
skip_cleanup: true |
|||
'on': |
|||
branch: master |
|||
node: '6.8' |
@ -0,0 +1,212 @@ |
|||
### 1.10.4 |
|||
|
|||
* [`1c31bf7`](https://github.com/buggerjs/node-inspect/commit/1c31bf7d1b3ea1b424ae0662526596670cb506c9) **chore:** Support embedded mode |
|||
|
|||
|
|||
### 1.10.3 |
|||
|
|||
* [`7b20379`](https://github.com/buggerjs/node-inspect/commit/7b20379069af692a9038a31a4465f72db9eb532f) **chore:** Mark .eslintrc as root |
|||
|
|||
|
|||
### 1.10.2 |
|||
|
|||
* Run tests on windows - **[@jkrems](https://github.com/jkrems)** [#16](https://github.com/buggerjs/node-inspect/pull/16) |
|||
- [`5a57f98`](https://github.com/buggerjs/node-inspect/commit/5a57f9865e02eef0763c2a7f26236c34a632ccdd) **chore:** Run tests on windows |
|||
- [`0a04b50`](https://github.com/buggerjs/node-inspect/commit/0a04b50cc8b4dc6ce868927c635c479d75ce71f4) **chore:** Bump nlm to get rid of postinstall |
|||
- [`4a8b27c`](https://github.com/buggerjs/node-inspect/commit/4a8b27cea814a37895effd2a0c1b85dbfee3a7f4) **test:** Remove unix path assumptions |
|||
|
|||
|
|||
### 1.10.1 |
|||
|
|||
* [`4ba3c72`](https://github.com/buggerjs/node-inspect/commit/4ba3c72270fae9a71343ddca11aa27980678a67c) **refactor:** Undo weird bundling into one file |
|||
|
|||
|
|||
### 1.10.0 |
|||
|
|||
* [`3e1a66a`](https://github.com/buggerjs/node-inspect/commit/3e1a66a489bef19beaa5f859e99e027274ff43cb) **feat:** Support CPU & heap profiles |
|||
|
|||
|
|||
### 1.9.3 |
|||
|
|||
* Move back to single file - **[@jkrems](https://github.com/jkrems)** [#15](https://github.com/buggerjs/node-inspect/pull/15) |
|||
- [`9877660`](https://github.com/buggerjs/node-inspect/commit/9877660a73ff0ec0885ad7f939ba62020a46b4b6) **refactor:** Wrap client in IIFE |
|||
- [`7795c53`](https://github.com/buggerjs/node-inspect/commit/7795c533f0605eb128db610a5874b27e555251ef) **refactor:** Move more code in createRepl scope |
|||
- [`be34a39`](https://github.com/buggerjs/node-inspect/commit/be34a398e823612bdf5ac90bad5222af27035a00) **refactor:** Move back to single file |
|||
- [`ab45b62`](https://github.com/buggerjs/node-inspect/commit/ab45b6273dc0d3a49d3cf46a80cb48ab79d1caf8) **refactor:** Remove single-use functions |
|||
- [`37a711e`](https://github.com/buggerjs/node-inspect/commit/37a711ed5334c06ed4d85f995e567a9f176a68d5) **style:** Stop using `new Buffer` |
|||
- [`d669dc5`](https://github.com/buggerjs/node-inspect/commit/d669dc593f5ad5ca7a48f19f0905ef66ec0e540d) **chore:** Switch to node eslint rules |
|||
- [`15e7917`](https://github.com/buggerjs/node-inspect/commit/15e79177918d96dcffd2384715faf0308e97a26c) **style:** Use var in classical for loops |
|||
|
|||
|
|||
### 1.9.2 |
|||
|
|||
* [`c9dc4be`](https://github.com/buggerjs/node-inspect/commit/c9dc4beb08236e33d64f19417682cf5b3f5aeed6) **doc:** Link directly to GOVERNANCE file |
|||
|
|||
|
|||
### 1.9.1 |
|||
|
|||
* Handle big ws frames correctly - **[@jkrems](https://github.com/jkrems)** [#14](https://github.com/buggerjs/node-inspect/pull/14) |
|||
- [`f80100e`](https://github.com/buggerjs/node-inspect/commit/f80100e932710d232d074b239cbf8fefa564c789) **fix:** Handle big ws frames correctly - see: [#10](https://github.com/buggerjs/node-inspect/issues/10) |
|||
|
|||
|
|||
### 1.9.0 |
|||
|
|||
* Support for low-level agent access - **[@jkrems](https://github.com/jkrems)** [#13](https://github.com/buggerjs/node-inspect/pull/13) |
|||
- [`90ed431`](https://github.com/buggerjs/node-inspect/commit/90ed4310c62d130637c12f8ecdb752075c43ac36) **feat:** Support for low-level agent access |
|||
|
|||
|
|||
### 1.8.4 |
|||
|
|||
* Use proper path for websocket - **[@jkrems](https://github.com/jkrems)** [#12](https://github.com/buggerjs/node-inspect/pull/12) |
|||
- [`3405225`](https://github.com/buggerjs/node-inspect/commit/3405225979dfc2058bcc6d1b90f41c060dbd1f92) **fix:** Use proper path for websocket - see: [#11](https://github.com/buggerjs/node-inspect/issues/11) |
|||
|
|||
|
|||
### 1.8.3 |
|||
|
|||
* [`6f9883d`](https://github.com/buggerjs/node-inspect/commit/6f9883d4b29419831133988981b83e891b19739a) **fix:** Breakpoints & scripts work when not paused |
|||
* [`ecb1362`](https://github.com/buggerjs/node-inspect/commit/ecb1362c842e6ed5bc28c091a32bfd540742db75) **chore:** Pin node to 6.8 |
|||
|
|||
|
|||
### 1.8.2 |
|||
|
|||
* [`4219a98`](https://github.com/buggerjs/node-inspect/commit/4219a98d6514f1068feabce2945c21a0d5ba6561) **refactor:** Decouple source snippet from repl |
|||
|
|||
|
|||
### 1.8.1 |
|||
|
|||
* [`95402ee`](https://github.com/buggerjs/node-inspect/commit/95402ee5dff04057f074677d39db2f61ec74c151) **refactor:** Move `list` into CallFrame |
|||
|
|||
|
|||
### 1.8.0 |
|||
|
|||
* [`d0e6499`](https://github.com/buggerjs/node-inspect/commit/d0e6499084f5d656ef0c5fd470d3ab21f2e9a6b4) **feat:** `exec .scope` |
|||
|
|||
|
|||
### 1.7.0 |
|||
|
|||
* `breakOn{Exception,Uncaught,None}` - **[@jkrems](https://github.com/jkrems)** [#8](https://github.com/buggerjs/node-inspect/pull/8) |
|||
- [`fa8c4c7`](https://github.com/buggerjs/node-inspect/commit/fa8c4c7d7bb6972733c92da4d04fdd62c02b0e3b) **feat:** `breakOn{Exception,Uncaught,None}` - see: [#6](https://github.com/buggerjs/node-inspect/issues/6) |
|||
|
|||
|
|||
### 1.6.0 |
|||
|
|||
* Add `help` command - **[@jkrems](https://github.com/jkrems)** [#7](https://github.com/buggerjs/node-inspect/pull/7) |
|||
- [`09b37a0`](https://github.com/buggerjs/node-inspect/commit/09b37a02e04e16a38ce27f69538d3b098548b47c) **feat:** Add `help` command - see: [#5](https://github.com/buggerjs/node-inspect/issues/5) |
|||
|
|||
|
|||
### 1.5.0 |
|||
|
|||
* [`7e0fd99`](https://github.com/buggerjs/node-inspect/commit/7e0fd99fcfc65d8b647a2259df78f4cabf1d3d63) **feat:** Add `r` shortcut for `run` |
|||
|
|||
|
|||
### 1.4.1 |
|||
|
|||
* [`484d098`](https://github.com/buggerjs/node-inspect/commit/484d0983f06d6ff9639ab5197ba0a58313f532df) **chore:** Remove old implementation |
|||
|
|||
|
|||
### 1.4.0 |
|||
|
|||
* Properly tested implementation - **[@jkrems](https://github.com/jkrems)** [#4](https://github.com/buggerjs/node-inspect/pull/4) |
|||
- [`ba060d3`](https://github.com/buggerjs/node-inspect/commit/ba060d3ef65ae84df2a3a9b9f16d563f3c4b29be) **feat:** Error handling w/o args |
|||
- [`b39b3bc`](https://github.com/buggerjs/node-inspect/commit/b39b3bc07c13adc48fc8bb720889285c51e62548) **feat:** Launch child |
|||
- [`481693f`](https://github.com/buggerjs/node-inspect/commit/481693f676ee099b7787cd2426b980858e973602) **feat:** Connect debug client |
|||
- [`3bba0f2`](https://github.com/buggerjs/node-inspect/commit/3bba0f2416b2e3b4e6010de675003fcc328b16e8) **chore:** Disable lint for inactive code |
|||
- [`cc7bdfc`](https://github.com/buggerjs/node-inspect/commit/cc7bdfcf7f21ef5cd5c32c7800407238b0d4f100) **feat:** Properly fail with invalid host:port |
|||
- [`73f34f9`](https://github.com/buggerjs/node-inspect/commit/73f34f902634e9778597e129f46895aa8b643d72) **refactor:** Remove unused field |
|||
- [`6a23e0c`](https://github.com/buggerjs/node-inspect/commit/6a23e0cf3179f43ca6fc5a0fa2b1dd18ebc044b5) **refactor:** Better debug output & support node 6.6 |
|||
- [`63b0f9b`](https://github.com/buggerjs/node-inspect/commit/63b0f9b6ef8bd9af0f7cb14a5938a45838731fc9) **test:** Add timeout to waitFor(pattern) |
|||
- [`cfa197b`](https://github.com/buggerjs/node-inspect/commit/cfa197bf8325a1a4ca1b296f8d6971d368bfbfbb) **refactor:** Move REPL setup into own file |
|||
- [`3f46c2c`](https://github.com/buggerjs/node-inspect/commit/3f46c2c43f836e1135b66871087aa74969f6b330) **feat:** Working repl eval |
|||
- [`6911eb1`](https://github.com/buggerjs/node-inspect/commit/6911eb1a00b964bc5683506d433fa4f665f5a82c) **feat:** Enter repeats last command |
|||
- [`7d20b7d`](https://github.com/buggerjs/node-inspect/commit/7d20b7deadf1b251ea8cf2cc9167c175624932c4) **chore:** Add missing license header |
|||
- [`23c62f8`](https://github.com/buggerjs/node-inspect/commit/23c62f8375ca7c8b71d032047e728dace02f4efa) **feat:** Print break context |
|||
- [`5dbc83d`](https://github.com/buggerjs/node-inspect/commit/5dbc83df31171f9c38a974c99340bde26f2e24ec) **feat:** Stepping and breakpoints |
|||
- [`8deb8cc`](https://github.com/buggerjs/node-inspect/commit/8deb8cc36b9fca432ab8df63a82e9de7ab5adaf0) **feat:** list for printing source |
|||
- [`1ed2ec9`](https://github.com/buggerjs/node-inspect/commit/1ed2ec9937070652be611dbb6b11dfb42cb840f8) **chore:** Disable verbose output on CI |
|||
- [`625a435`](https://github.com/buggerjs/node-inspect/commit/625a435925dd8fd980bed2dc9e3fd73dd27df4ef) **fix:** Gracefully handle delayed scriptParsed |
|||
- [`8823c60`](https://github.com/buggerjs/node-inspect/commit/8823c60d347600b2313cfdd8cb5e96fe02419a8a) **chore:** Run all the tests |
|||
- [`00506f7`](https://github.com/buggerjs/node-inspect/commit/00506f763928cc440505a81030167a11b9a84e00) **feat:** backtrace/bt |
|||
- [`e1ee02d`](https://github.com/buggerjs/node-inspect/commit/e1ee02d5cc389916489d387d07d5dd161230427a) **refactor:** Leverage util.inspect.custom |
|||
- [`5dcc319`](https://github.com/buggerjs/node-inspect/commit/5dcc31922d40f56c7435319d1538390a442e8e4b) **feat:** scripts and scripts(true) |
|||
- [`085cd5a`](https://github.com/buggerjs/node-inspect/commit/085cd5a76a961edfcaa342fff5eb09bf2f9c8983) **refactor:** Consistent import style |
|||
- [`1c60f91`](https://github.com/buggerjs/node-inspect/commit/1c60f91f233848c05d865617dc7f5aacb36270b6) **feat:** Set breakpoint before file is loaded |
|||
- [`bc82ecc`](https://github.com/buggerjs/node-inspect/commit/bc82eccb2a1a7c0f5332371254f6584e748216aa) **feat:** breakpoints to list breakpoints |
|||
- [`7f48c95`](https://github.com/buggerjs/node-inspect/commit/7f48c9510696ec400d51afaca8d23a9c292640f8) **feat:** watchers & exec |
|||
- [`0f8cd13`](https://github.com/buggerjs/node-inspect/commit/0f8cd13a092e5dbeb395ff04cbe2ed97cb986423) **feat:** clearBreakpoint |
|||
- [`0d31560`](https://github.com/buggerjs/node-inspect/commit/0d315603bdcb9f4da42fab24dc569c325151269e) **feat:** version to print v8 version |
|||
- [`df6b89d`](https://github.com/buggerjs/node-inspect/commit/df6b89df580a9afcb3b8883b0e4224cbcebb384f) **feat:** Paused & global exec |
|||
- [`9e97d73`](https://github.com/buggerjs/node-inspect/commit/9e97d73073ceffd70974d45887c84fadb9159d5c) **feat:** repl to enter exec mode |
|||
- [`9ee9f90`](https://github.com/buggerjs/node-inspect/commit/9ee9f903d6202f54ed2b3b3559da4006b65d39b5) **feat:** run & restart |
|||
* [`3a752aa`](https://github.com/buggerjs/node-inspect/commit/3a752aaa773968bfe16c5f543bd739feed598bea) **feat:** kill |
|||
* [`a67e470`](https://github.com/buggerjs/node-inspect/commit/a67e47018b20d46aeeaa7abd27eb8e7770fd0b8f) **feat:** Restore breakpoints on restart |
|||
|
|||
|
|||
### 1.3.3 |
|||
|
|||
* [`eb7a54c`](https://github.com/buggerjs/node-inspect/commit/eb7a54c6fa731ed3276072c72034046fc5ffbac6) **chore:** Switch to tap for tests |
|||
|
|||
|
|||
### 1.3.2 |
|||
|
|||
* Add notes about governance - **[@jkrems](https://github.com/jkrems)** [#3](https://github.com/buggerjs/node-inspect/pull/3) |
|||
- [`e94089d`](https://github.com/buggerjs/node-inspect/commit/e94089d93689cacf5c953e94563463d1e174452d) **chore:** Add notes about governance |
|||
|
|||
|
|||
### 1.3.1 |
|||
|
|||
* [`8767137`](https://github.com/buggerjs/node-inspect/commit/8767137c53a2f6b1d36970074ea95be9871e50e3) **style:** Remove rogue console.log |
|||
|
|||
|
|||
### 1.3.0 |
|||
|
|||
* [`3ac6232`](https://github.com/buggerjs/node-inspect/commit/3ac623219ba44b0af40ef66826610a26a46c7966) **feat:** Add `version` command |
|||
|
|||
|
|||
### 1.2.0 |
|||
|
|||
* [`86b5812`](https://github.com/buggerjs/node-inspect/commit/86b581218ccab44e6bde259a17ad1e71645a6137) **feat:** scripts & listScripts(true) |
|||
|
|||
|
|||
### 1.1.1 |
|||
|
|||
* [`feaea38`](https://github.com/buggerjs/node-inspect/commit/feaea385a981e6b72a8d99277fbf575c54e15fc6) **style:** Typo in comment |
|||
|
|||
|
|||
### 1.1.0 |
|||
|
|||
* [`c64155f`](https://github.com/buggerjs/node-inspect/commit/c64155faa552f71463842a26330aa5bcbfc31670) **feat:** repl command |
|||
|
|||
|
|||
### 1.0.0 |
|||
|
|||
* [`44c4c79`](https://github.com/buggerjs/node-inspect/commit/44c4c79af5a228ccfd8906f11409b2a33390b878) **chore:** Initial commit |
|||
* [`985873c`](https://github.com/buggerjs/node-inspect/commit/985873cfb97146b38480080f9907219c473f1f6f) **feat:** Launching the example works |
|||
* [`3d92d05`](https://github.com/buggerjs/node-inspect/commit/3d92d05cca152a2c2647aa64eefc80432638bc4d) **chore:** Proper license and passing tests |
|||
* [`b3f99d9`](https://github.com/buggerjs/node-inspect/commit/b3f99d981038b17663fcfd984d2f5d6d9b51ee18) **feat:** Futile attempts to send a valid ws frame |
|||
* [`465cfb7`](https://github.com/buggerjs/node-inspect/commit/465cfb7b295aebb48b285c26f6de9c4657fe590d) **feat:** Working ws connection |
|||
* [`da9f011`](https://github.com/buggerjs/node-inspect/commit/da9f01118e2b144f2da8cd370113a608526774a1) **fix:** Fix remote connect |
|||
* [`5ef33d7`](https://github.com/buggerjs/node-inspect/commit/5ef33d7892cc49becb4c66098fc7927bc74b014a) **feat:** Working step-by-step |
|||
* [`534e1e4`](https://github.com/buggerjs/node-inspect/commit/534e1e46b307d61d51eb4c0aab4a3b17c17aea3d) **chore:** Add bin entry |
|||
* [`8cff9cf`](https://github.com/buggerjs/node-inspect/commit/8cff9cfb0138b5ecff0f5f6a7839dbfddc0684fd) **style:** Use simpler key thingy |
|||
* [`720ec53`](https://github.com/buggerjs/node-inspect/commit/720ec53a5b251ab3caf27f06b60924efb9e03a92) **doc:** Add instructions |
|||
* [`b89ad60`](https://github.com/buggerjs/node-inspect/commit/b89ad601b885a417e6433b1609477d8453f498a1) **doc:** More helpful docs |
|||
* [`de9243c`](https://github.com/buggerjs/node-inspect/commit/de9243c95eabe733d05952229340808c3cebf129) **feat:** Watchers |
|||
* [`e16978f`](https://github.com/buggerjs/node-inspect/commit/e16978ff8e4b2b2bdccf88fd7d3905f525822981) **docs:** Working usage hints |
|||
* [`2dbc204`](https://github.com/buggerjs/node-inspect/commit/2dbc2042145fd97169fc7536186a449715e27810) **refactor:** Use proxies |
|||
* [`b8c9b14`](https://github.com/buggerjs/node-inspect/commit/b8c9b147713f63181396d5a7fe4c2f737b733b4c) **style:** Remove unused var |
|||
* [`f6b4b20`](https://github.com/buggerjs/node-inspect/commit/f6b4b20a1d28d91cfe452b995f7dbe5f7c749e89) **feat:** Nicer inspect of remote values |
|||
* [`36887c6`](https://github.com/buggerjs/node-inspect/commit/36887c66bbf26d540f087f80ddfec38462a33bdf) **fix:** Properly print watchers |
|||
* [`7729442`](https://github.com/buggerjs/node-inspect/commit/77294426157a28cc76e339cb13916a205182641e) **feat:** Add pause command |
|||
* [`e39a713`](https://github.com/buggerjs/node-inspect/commit/e39a7134873f06da37baaa9b6252cede4ad38d7a) **fix:** Properly format boolean properties |
|||
* [`f8f51d7`](https://github.com/buggerjs/node-inspect/commit/f8f51d7a01e8d74023306a08a3d6e2da63d123e1) **fix:** Properly format numeric properties |
|||
* [`89e6e08`](https://github.com/buggerjs/node-inspect/commit/89e6e087220f3c3cb628ac7541c44298485a2e04) **feat:** Add backtrace command |
|||
* [`82362ac`](https://github.com/buggerjs/node-inspect/commit/82362acfc7ce22b4cccc64889ec136dedc8895ec) **feat:** Add setBreakpoint() |
|||
* [`7064cce`](https://github.com/buggerjs/node-inspect/commit/7064ccec3b103683088d532abfe5b4e7c066948b) **feat:** Add `setBreakpoint(line)` |
|||
* [`360580e`](https://github.com/buggerjs/node-inspect/commit/360580eba4353e81311e56df018eec0ca233da11) **feat:** Add run/kill/restart |
|||
* [`b1b576e`](https://github.com/buggerjs/node-inspect/commit/b1b576e2645723a8575df544e0bfb672d60d9d91) **feat:** Add `help` command |
|||
* [`2db4660`](https://github.com/buggerjs/node-inspect/commit/2db46609cd1c8543d31ebd5dc47e4c27ec254841) **feat:** Add remaining sb() variants |
|||
* [`f2ad1ae`](https://github.com/buggerjs/node-inspect/commit/f2ad1aeedafb154043d70bb9195b10986d311d26) **fix:** Display breakpoints set into the future |
|||
* [`73272f9`](https://github.com/buggerjs/node-inspect/commit/73272f9ace1f8546f8cad1d53627dbffba50bb4e) **refactor:** Make breakpoints more inspect friendly |
|||
* [`507a71d`](https://github.com/buggerjs/node-inspect/commit/507a71de345a3de7fe144517e9f5ea264ff993e3) **feat:** Add breakpoints command |
|||
* [`5fb3e5d`](https://github.com/buggerjs/node-inspect/commit/5fb3e5d17bbcfd45b264431547b3cf0b781c7640) **docs:** Link to Command Line API docs |
|||
* [`81af501`](https://github.com/buggerjs/node-inspect/commit/81af501bbf85397e2078310c7f24a9ac5b7f02dc) **chore:** Fix license field |
@ -0,0 +1,183 @@ |
|||
# Contributing |
|||
|
|||
🎉🏅 Thanks for helping us improve this project! 🙏 |
|||
|
|||
This document outlines some of the practices we care about. |
|||
If you have any questions or suggestions about the process, |
|||
feel free to [open an issue](#reporting-issues) |
|||
. |
|||
|
|||
## Code of Conduct |
|||
|
|||
The [Node.js Code of Conduct][] applies to this repo. |
|||
|
|||
[Node.js Code of Conduct]: https://github.com/nodejs/node/blob/master/CODE_OF_CONDUCT.md |
|||
|
|||
## Code Contributions |
|||
|
|||
The nodereport project falls under the governance of the diagnostics |
|||
working group which is documented in: |
|||
https://github.com/nodejs/diagnostics/blob/master/GOVERNANCE.md. |
|||
|
|||
## Developer's Certificate of Origin 1.1 |
|||
|
|||
By making a contribution to this project, I certify that: |
|||
|
|||
* (a) The contribution was created in whole or in part by me and I |
|||
have the right to submit it under the open source license |
|||
indicated in the file; or |
|||
|
|||
* (b) The contribution is based upon previous work that, to the best |
|||
of my knowledge, is covered under an appropriate open source |
|||
license and I have the right under that license to submit that |
|||
work with modifications, whether created in whole or in part |
|||
by me, under the same open source license (unless I am |
|||
permitted to submit under a different license), as indicated |
|||
in the file; or |
|||
|
|||
* (c) The contribution was provided directly to me by some other |
|||
person who certified (a), (b) or (c) and I have not modified |
|||
it. |
|||
|
|||
* (d) I understand and agree that this project and the contribution |
|||
are public and that a record of the contribution (including all |
|||
personal information I submit with it, including my sign-off) is |
|||
maintained indefinitely and may be redistributed consistent with |
|||
this project or the open source license(s) involved. |
|||
|
|||
## How Can I Contribute? |
|||
|
|||
### Reporting Issues |
|||
|
|||
If you find any mistakes in the docs or a bug in the code, |
|||
please [open an issue in Github](https://github.com/nodejs/node-inspect/issues/new) so we can look into it. |
|||
You can also [create a PR](#contributing-code) fixing it yourself, or course. |
|||
|
|||
If you report a bug, please follow these guidelines: |
|||
|
|||
* Make sure the bug exists in the latest version. |
|||
* Include instructions on how to reproduce the issue. |
|||
The instructions should be as minimal as possible |
|||
and answer the three big questions: |
|||
1. What are the exact steps you took? This includes the exact versions of node, npm, and any packages involved. |
|||
1. What result are you expecting? |
|||
1. What is the actual result? |
|||
|
|||
### Improving Documentation |
|||
|
|||
For small documentation changes, you can use [Github's editing feature](https://help.github.com/articles/editing-files-in-another-user-s-repository/). |
|||
The only thing to keep in mind is to prefix the commit message with "docs: ". |
|||
The detault commit message generated by Github will lead to a failing CI build. |
|||
|
|||
For larger updates to the documentation |
|||
it might be better to follow the [instructions for contributing code below](#contributing-code). |
|||
|
|||
### Contributing Code |
|||
|
|||
**Note:** If you're planning on making substantial changes, |
|||
please [open an issue first to discuss your idea](#reporting-issues). |
|||
Otherwise you might end up investing a lot of work |
|||
only to discover that it conflicts with plans the maintainers might have. |
|||
|
|||
The general steps for creating a pull request are: |
|||
|
|||
1. Create a branch for your change. |
|||
Always start your branch from the latest `master`. |
|||
We often prefix the branch name with our initials, e.g. `jk-a-change`. |
|||
1. Run `npm install` to install the dependencies. |
|||
1. If you're fixing a bug, be sure to write a test *first*. |
|||
That way you can validate that the test actually catches the bug and doesn't pass. |
|||
1. Make your changes to the code. |
|||
Remember to update the tests if you add new features or change behavior. |
|||
1. Run the tests via `npm test`. This will also run style checks and other validations. |
|||
You might see errors about uncommitted files. |
|||
This is expected until you commit your changes. |
|||
1. Once you're done, `git add .` and `git commit`. |
|||
Please follow the [commit message conventions](#commits--commit-messages) described below. |
|||
1. Push your branch to Github & create a PR. |
|||
|
|||
#### Code Style |
|||
|
|||
In addition to any linting rules the project might include, |
|||
a few general rules of thumb: |
|||
|
|||
* Try to match the style of the rest of the code. |
|||
* We prefer simple code that is easy to understand over terse, expressive code. |
|||
* We try to structure projects by semantics instead of role. |
|||
E.g. we'd rather have a `tree.js` module that contains tree traversal-related helpers |
|||
than a `helpers.js` module. |
|||
* Actually, if you create helpers you might want to put those into a separate package. |
|||
That way it's easier to reuse them. |
|||
|
|||
#### Commits & Commit Messages |
|||
|
|||
Please follow the [angular commit message conventions](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines). |
|||
We use an automated tool for generating releases |
|||
that depends on the conventions to determine the next version and the content of the changelog. |
|||
Commit messages that don't follow the conventions will cause `npm test` (and thus CI) to fail. |
|||
|
|||
The short summary - a commit message should look like this: |
|||
|
|||
``` |
|||
<type>: <subject> |
|||
|
|||
<body> |
|||
|
|||
<references> |
|||
|
|||
<footer> |
|||
``` |
|||
|
|||
Everything but the first line is optional. |
|||
The empty lines between the different parts are required. |
|||
|
|||
* `<type>`: One of the following: |
|||
- **feat:** Introduces a new feature. This will cause the minor version to go up. |
|||
- **fix:** A bug fix. Causes a patch version bump. |
|||
- **docs:** Changes to the documentation. |
|||
This will also cause an increase of the patch version so that the changes show up in the npm registry. |
|||
- **style:** Cleanup & lint rule fixes. |
|||
Note that often it's better to just amend the previous commit if it introduced lint errors. |
|||
- **refactor:** Changes to the code structure without fixing bugs or adding features. |
|||
- **perf:** Performance optimizations. |
|||
- **test:** Fixing existing tests or adding missing ones. |
|||
Just like with **style**, if you add tests to a feature you just introduced in the previous commit, |
|||
consider keeping the tests and the feature in the same commit instead. |
|||
- **chore:** Changes to the project setup and tools, dependency bumps, house-keeping. |
|||
* `<subject>`: A [good git commit message subject](http://chris.beams.io/posts/git-commit/#limit-50). |
|||
- Keep it brief. If possible the whole first line should have at most 50 characters. |
|||
- Use imperative mood. "Create" instead of "creates" or "created". |
|||
- No period (".") at the end. |
|||
* `<body>`: Motivation for the change and any context required for understanding the choices made. |
|||
Just like the subject, it should use imperative mood. |
|||
* `<references>`: Any URLs relevant to the PR go here. |
|||
Use one line per URL and prefix it with the kind of relationship, e.g. "Closes: " or "See: ". |
|||
If you are referencing an issue in your commit body or PR description, |
|||
never use `#123` but the full URL to the issue or PR you are referencing. |
|||
That way the reference is easy to resolve from the git history without having to "guess" the correct link |
|||
even if the commit got cherry-picked or merged into a different project. |
|||
* `<footer>`: This part only applies if your commit introduces a breaking change. |
|||
It's important this is present, otherwise the major version will not increase. |
|||
See below for an example. |
|||
|
|||
##### Examples |
|||
|
|||
A feature that introduces a breaking change: |
|||
|
|||
``` |
|||
feat: Support --yes CLI option |
|||
|
|||
For existing projects all prompts can be inferred automatically. |
|||
Manual confirmation for each default provides no value in that case. |
|||
|
|||
Closes https://github.com/my/project/issues/123 |
|||
|
|||
BREAKING CHANGE: This removes support for interactive password entry. |
|||
Users will have to login beforehand. |
|||
``` |
|||
|
|||
A simple bug fix: |
|||
|
|||
``` |
|||
fix: Handle multi-byte characters in search logic |
|||
``` |
@ -0,0 +1,5 @@ |
|||
# node-inspect Project Governance |
|||
|
|||
The node-inspect project falls under the governance of the diagnostics |
|||
working group which is documented in: |
|||
https://github.com/nodejs/diagnostics/blob/master/GOVERNANCE.md. |
@ -0,0 +1,19 @@ |
|||
Copyright Node.js contributors. All rights reserved. |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to |
|||
deal in the Software without restriction, including without limitation the |
|||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
sell copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
IN THE SOFTWARE. |
@ -0,0 +1,29 @@ |
|||
# `node-inspect` |
|||
|
|||
```bash |
|||
npm install --global node-inspect |
|||
``` |
|||
|
|||
For the old V8 debugger protocol, |
|||
node has two options: |
|||
|
|||
1. `node --debug <file>`: Start `file` with remote debugging enabled. |
|||
2. `node debug <file>`: Start an interactive CLI debugger for `<file>`. |
|||
|
|||
But for the Chrome inspector protol, |
|||
there's only one: `node --inspect <file>`. |
|||
|
|||
This project tries to provide the missing second option |
|||
by re-implementing `node debug` against the new protocol. |
|||
|
|||
``` |
|||
Usage: node-inspect script.js |
|||
node-inspect <host>:<port> |
|||
``` |
|||
|
|||
#### References |
|||
|
|||
* [Debugger Documentation](https://nodejs.org/api/debugger.html) |
|||
* [EPS: `node inspect` CLI debugger](https://github.com/nodejs/node-eps/pull/42) |
|||
* [Debugger Protocol Viewer](https://chromedevtools.github.io/debugger-protocol-viewer/) |
|||
* [Command Line API](https://developers.google.com/web/tools/chrome-devtools/debug/command-line/command-line-reference?hl=en) |
@ -0,0 +1,13 @@ |
|||
environment: |
|||
nodejs_version: "6" |
|||
|
|||
install: |
|||
- ps: Install-Product node $env:nodejs_version |
|||
- npm install |
|||
|
|||
test_script: |
|||
- node --version |
|||
- npm --version |
|||
- npm test |
|||
|
|||
build: off |
@ -0,0 +1,2 @@ |
|||
#!/usr/bin/env node
|
|||
require('./lib/cli.js'); |
@ -0,0 +1,6 @@ |
|||
'use strict'; |
|||
let x = 0; |
|||
function heartbeat() { |
|||
++x; |
|||
} |
|||
setInterval(heartbeat, 50); |
@ -0,0 +1,31 @@ |
|||
'use strict'; |
|||
const { exports: moduleScoped } = module; |
|||
|
|||
function topFn(a, b = false) { |
|||
const l1 = a; |
|||
let t = typeof l1; |
|||
var v = t.length; |
|||
debugger; |
|||
return b || t || v || moduleScoped; |
|||
} |
|||
|
|||
class Ctor { |
|||
constructor(options) { |
|||
this.options = options; |
|||
} |
|||
|
|||
m() { |
|||
const mLocal = this.options; |
|||
topFn(this); |
|||
return mLocal; |
|||
} |
|||
} |
|||
|
|||
(function () { |
|||
const theOptions = { x: 42 }; |
|||
const arr = [theOptions]; |
|||
arr.forEach(options => { |
|||
const obj = new Ctor(options); |
|||
return obj.m(); |
|||
}); |
|||
}()); |
@ -0,0 +1,16 @@ |
|||
const x = 10; |
|||
let name = 'World'; |
|||
name = 'Robin'; |
|||
function sayHello() { |
|||
if (x > 0) { |
|||
console.log(`Hello ${name}`); |
|||
} |
|||
} |
|||
sayHello(); |
|||
debugger; |
|||
setTimeout(sayHello, 10); |
|||
|
|||
function otherFunction() { |
|||
console.log('x = %d', x); |
|||
} |
|||
setTimeout(otherFunction, 50); |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
const { add } = require('./other'); |
|||
|
|||
const sum = add(40, 2); |
|||
module.exports = sum; |
@ -0,0 +1,4 @@ |
|||
'use strict'; |
|||
exports.add = function add(a, b) { |
|||
return a + b; |
|||
}; |
@ -0,0 +1,11 @@ |
|||
'use strict'; |
|||
let error = null; |
|||
try { |
|||
throw new Error('Caught'); |
|||
} catch (e) { |
|||
error = e; |
|||
} |
|||
|
|||
if (error) { |
|||
throw new Error('Uncaught'); |
|||
} |
@ -0,0 +1,4 @@ |
|||
'use strict'; |
|||
let x = 1; |
|||
x = x + 1; |
|||
module.exports = x; |
@ -0,0 +1,283 @@ |
|||
/* |
|||
* Copyright Node.js contributors. All rights reserved. |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
'use strict'; |
|||
const { spawn } = require('child_process'); |
|||
const { EventEmitter } = require('events'); |
|||
const util = require('util'); |
|||
|
|||
const [ InspectClient, createRepl ] = |
|||
(typeof __dirname !== 'undefined') ? |
|||
// This copy of node-inspect is on-disk, relative paths make sense.
|
|||
[ |
|||
require('./internal/inspect_client'), |
|||
require('./internal/inspect_repl') |
|||
] : |
|||
// This copy of node-inspect is built into the node executable.
|
|||
[ |
|||
require('node-inspect/lib/internal/inspect_client'), |
|||
require('node-inspect/lib/internal/inspect_repl') |
|||
]; |
|||
|
|||
const debuglog = util.debuglog('inspect'); |
|||
|
|||
exports.port = 9229; |
|||
|
|||
function runScript(script, scriptArgs, inspectPort, childPrint) { |
|||
return new Promise((resolve) => { |
|||
const args = [ |
|||
'--inspect', |
|||
`--debug-brk=${inspectPort}`, |
|||
].concat([script], scriptArgs); |
|||
const child = spawn(process.execPath, args); |
|||
child.stdout.setEncoding('utf8'); |
|||
child.stderr.setEncoding('utf8'); |
|||
child.stdout.on('data', childPrint); |
|||
child.stderr.on('data', childPrint); |
|||
|
|||
let output = ''; |
|||
function waitForListenHint(text) { |
|||
output += text; |
|||
if (/chrome-devtools:\/\//.test(output)) { |
|||
child.stderr.removeListener('data', waitForListenHint); |
|||
resolve(child); |
|||
} |
|||
} |
|||
|
|||
child.stderr.on('data', waitForListenHint); |
|||
}); |
|||
} |
|||
|
|||
function createAgentProxy(domain, client) { |
|||
const agent = new EventEmitter(); |
|||
agent.then = (...args) => { |
|||
// TODO: potentially fetch the protocol and pretty-print it here.
|
|||
const descriptor = { |
|||
[util.inspect.custom](depth, { stylize }) { |
|||
return stylize(`[Agent ${domain}]`, 'special'); |
|||
}, |
|||
}; |
|||
return Promise.resolve(descriptor).then(...args); |
|||
}; |
|||
|
|||
return new Proxy(agent, { |
|||
get(target, name) { |
|||
if (name in target) return target[name]; |
|||
return function callVirtualMethod(params) { |
|||
return client.callMethod(`${domain}.${name}`, params); |
|||
}; |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
class NodeInspector { |
|||
constructor(options, stdin, stdout) { |
|||
this.options = options; |
|||
this.stdin = stdin; |
|||
this.stdout = stdout; |
|||
|
|||
this.paused = true; |
|||
this.child = null; |
|||
|
|||
if (options.script) { |
|||
this._runScript = runScript.bind(null, |
|||
options.script, |
|||
options.scriptArgs, |
|||
options.port, |
|||
this.childPrint.bind(this)); |
|||
} else { |
|||
this._runScript = () => Promise.resolve(null); |
|||
} |
|||
|
|||
this.client = new InspectClient(options.port, options.host); |
|||
|
|||
this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime']; |
|||
this.domainNames.forEach((domain) => { |
|||
this[domain] = createAgentProxy(domain, this.client); |
|||
}); |
|||
this.handleDebugEvent = (fullName, params) => { |
|||
const [domain, name] = fullName.split('.'); |
|||
if (domain in this) { |
|||
this[domain].emit(name, params); |
|||
} |
|||
}; |
|||
this.client.on('debugEvent', this.handleDebugEvent); |
|||
const startRepl = createRepl(this); |
|||
|
|||
// 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)); |
|||
|
|||
this.run() |
|||
.then(() => { |
|||
this.repl = startRepl(); |
|||
this.repl.on('exit', () => { |
|||
process.exit(0); |
|||
}); |
|||
this.paused = false; |
|||
}) |
|||
.then(null, (error) => process.nextTick(() => { throw error; })); |
|||
} |
|||
|
|||
suspendReplWhile(fn) { |
|||
this.repl.rli.pause(); |
|||
this.stdin.pause(); |
|||
this.paused = true; |
|||
return new Promise((resolve) => { |
|||
resolve(fn()); |
|||
}).then(() => { |
|||
this.paused = false; |
|||
this.repl.rli.resume(); |
|||
this.repl.displayPrompt(); |
|||
this.stdin.resume(); |
|||
}).then(null, (error) => process.nextTick(() => { throw error; })); |
|||
} |
|||
|
|||
killChild() { |
|||
this.client.reset(); |
|||
if (this.child) { |
|||
this.child.kill(); |
|||
this.child = null; |
|||
} |
|||
} |
|||
|
|||
run() { |
|||
this.killChild(); |
|||
return this._runScript().then((child) => { |
|||
this.child = child; |
|||
|
|||
let connectionAttempts = 0; |
|||
const attemptConnect = () => { |
|||
++connectionAttempts; |
|||
debuglog('connection attempt #%d', connectionAttempts); |
|||
this.stdout.write('.'); |
|||
return this.client.connect() |
|||
.then(() => { |
|||
debuglog('connection established'); |
|||
}, (error) => { |
|||
debuglog('connect failed', error); |
|||
// If it's failed to connect 10 times then print failed message
|
|||
if (connectionAttempts >= 10) { |
|||
this.stdout.write(' failed to connect, please retry\n'); |
|||
process.exit(1); |
|||
} |
|||
|
|||
return new Promise((resolve) => setTimeout(resolve, 500)) |
|||
.then(attemptConnect); |
|||
}); |
|||
}; |
|||
|
|||
const { host, port } = this.options; |
|||
this.print(`connecting to ${host}:${port} ..`, true); |
|||
return attemptConnect(); |
|||
}); |
|||
} |
|||
|
|||
clearLine() { |
|||
if (this.stdout.isTTY) { |
|||
this.stdout.cursorTo(0); |
|||
this.stdout.clearLine(1); |
|||
} else { |
|||
this.stdout.write('\b'); |
|||
} |
|||
} |
|||
|
|||
print(text, oneline = false) { |
|||
this.clearLine(); |
|||
this.stdout.write(oneline ? text : `${text}\n`); |
|||
} |
|||
|
|||
childPrint(text) { |
|||
this.print( |
|||
text.toString() |
|||
.split(/\r\n|\r|\n/g) |
|||
.filter((chunk) => !!chunk) |
|||
.map((chunk) => `< ${chunk}`) |
|||
.join('\n') |
|||
); |
|||
if (!this.paused) { |
|||
this.repl.displayPrompt(true); |
|||
} |
|||
if (/Waiting for the debugger to disconnect\.\.\.\n$/.test(text)) { |
|||
this.killChild(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function parseArgv([target, ...args]) { |
|||
let host = '127.0.0.1'; |
|||
let port = exports.port; |
|||
let isRemote = false; |
|||
let script = target; |
|||
let scriptArgs = args; |
|||
|
|||
const hostMatch = target.match(/^([^:]+):(\d+)$/); |
|||
const portMatch = target.match(/^--port=(\d+)$/); |
|||
if (hostMatch) { |
|||
// Connecting to remote debugger
|
|||
// `node-inspect localhost:9229`
|
|||
host = hostMatch[1]; |
|||
port = parseInt(hostMatch[2], 10); |
|||
isRemote = true; |
|||
script = null; |
|||
} else if (portMatch) { |
|||
// Start debugger on custom port
|
|||
// `node debug --port=8058 app.js`
|
|||
port = parseInt(portMatch[1], 10); |
|||
script = args[0]; |
|||
scriptArgs = args.slice(1); |
|||
} |
|||
|
|||
return { |
|||
host, port, |
|||
isRemote, script, scriptArgs, |
|||
}; |
|||
} |
|||
|
|||
function startInspect(argv = process.argv.slice(2), |
|||
stdin = process.stdin, |
|||
stdout = process.stdout) { |
|||
/* eslint-disable no-console */ |
|||
if (argv.length < 1) { |
|||
console.error('Usage: node-inspect script.js'); |
|||
console.error(' node-inspect <host>:<port>'); |
|||
process.exit(1); |
|||
} |
|||
|
|||
const options = parseArgv(argv); |
|||
const inspector = new NodeInspector(options, stdin, stdout); |
|||
|
|||
stdin.resume(); |
|||
|
|||
function handleUnexpectedError(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); |
|||
} |
|||
|
|||
process.on('uncaughtException', handleUnexpectedError); |
|||
/* eslint-enable no-console */ |
|||
} |
|||
exports.start = startInspect; |
@ -0,0 +1,24 @@ |
|||
/* |
|||
* Copyright Node.js contributors. All rights reserved. |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
'use strict'; |
|||
// ~= NativeModule.require('_debugger').start();
|
|||
require('./_inspect').start(); |
@ -0,0 +1,363 @@ |
|||
/* |
|||
* Copyright Node.js contributors. All rights reserved. |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
'use strict'; |
|||
const Buffer = require('buffer').Buffer; |
|||
const crypto = require('crypto'); |
|||
const { EventEmitter } = require('events'); |
|||
const http = require('http'); |
|||
const URL = require('url'); |
|||
const util = require('util'); |
|||
|
|||
const debuglog = util.debuglog('inspect'); |
|||
|
|||
const kOpCodeText = 0x1; |
|||
const kOpCodeClose = 0x8; |
|||
|
|||
const kFinalBit = 0x80; |
|||
const kReserved1Bit = 0x40; |
|||
const kReserved2Bit = 0x20; |
|||
const kReserved3Bit = 0x10; |
|||
const kOpCodeMask = 0xF; |
|||
const kMaskBit = 0x80; |
|||
const kPayloadLengthMask = 0x7F; |
|||
|
|||
const kMaxSingleBytePayloadLength = 125; |
|||
const kMaxTwoBytePayloadLength = 0xFFFF; |
|||
const kTwoBytePayloadLengthField = 126; |
|||
const kEightBytePayloadLengthField = 127; |
|||
const kMaskingKeyWidthInBytes = 4; |
|||
|
|||
function isEmpty(obj) { |
|||
return Object.keys(obj).length === 0; |
|||
} |
|||
|
|||
function unpackError({ code, message, data }) { |
|||
const err = new Error(`${message} - ${data}`); |
|||
err.code = code; |
|||
Error.captureStackTrace(err, unpackError); |
|||
return err; |
|||
} |
|||
|
|||
function encodeFrameHybi17(payload) { |
|||
var i; |
|||
|
|||
const dataLength = payload.length; |
|||
|
|||
let singleByteLength; |
|||
let additionalLength; |
|||
if (dataLength > kMaxTwoBytePayloadLength) { |
|||
singleByteLength = kEightBytePayloadLengthField; |
|||
additionalLength = Buffer.alloc(8); |
|||
let remaining = dataLength; |
|||
for (i = 0; i < 8; ++i) { |
|||
additionalLength[7 - i] = remaining & 0xFF; |
|||
remaining >>= 8; |
|||
} |
|||
} else if (dataLength > kMaxSingleBytePayloadLength) { |
|||
singleByteLength = kTwoBytePayloadLengthField; |
|||
additionalLength = Buffer.alloc(2); |
|||
additionalLength[0] = (dataLength & 0xFF00) >> 8; |
|||
additionalLength[1] = dataLength & 0xFF; |
|||
} else { |
|||
additionalLength = Buffer.alloc(0); |
|||
singleByteLength = dataLength; |
|||
} |
|||
|
|||
const header = Buffer.from([ |
|||
kFinalBit | kOpCodeText, |
|||
kMaskBit | singleByteLength, |
|||
]); |
|||
|
|||
const mask = Buffer.alloc(4); |
|||
const masked = Buffer.alloc(dataLength); |
|||
for (i = 0; i < dataLength; ++i) { |
|||
masked[i] = payload[i] ^ mask[i % kMaskingKeyWidthInBytes]; |
|||
} |
|||
|
|||
return Buffer.concat([header, additionalLength, mask, masked]); |
|||
} |
|||
|
|||
function decodeFrameHybi17(data) { |
|||
const dataAvailable = data.length; |
|||
const notComplete = { closed: false, payload: null, rest: data }; |
|||
let payloadOffset = 2; |
|||
if ((dataAvailable - payloadOffset) < 0) return notComplete; |
|||
|
|||
const firstByte = data[0]; |
|||
const secondByte = data[1]; |
|||
|
|||
const final = (firstByte & kFinalBit) !== 0; |
|||
const reserved1 = (firstByte & kReserved1Bit) !== 0; |
|||
const reserved2 = (firstByte & kReserved2Bit) !== 0; |
|||
const reserved3 = (firstByte & kReserved3Bit) !== 0; |
|||
const opCode = firstByte & kOpCodeMask; |
|||
const masked = (secondByte & kMaskBit) !== 0; |
|||
const compressed = reserved1; |
|||
if (compressed) { |
|||
throw new Error('Compressed frames not supported'); |
|||
} |
|||
if (!final || reserved2 || reserved3) { |
|||
throw new Error('Only compression extension is supported'); |
|||
} |
|||
|
|||
if (masked) { |
|||
throw new Error('Masked server frame - not supported'); |
|||
} |
|||
|
|||
let closed = false; |
|||
switch (opCode) { |
|||
case kOpCodeClose: |
|||
closed = true; |
|||
break; |
|||
case kOpCodeText: |
|||
break; |
|||
default: |
|||
throw new Error(`Unsupported op code ${opCode}`); |
|||
} |
|||
|
|||
let payloadLength = secondByte & kPayloadLengthMask; |
|||
switch (payloadLength) { |
|||
case kTwoBytePayloadLengthField: |
|||
payloadOffset += 2; |
|||
payloadLength = (data[2] << 8) + data[3]; |
|||
break; |
|||
|
|||
case kEightBytePayloadLengthField: |
|||
payloadOffset += 8; |
|||
payloadLength = 0; |
|||
for (var i = 0; i < 8; ++i) { |
|||
payloadLength <<= 8; |
|||
payloadLength |= data[2 + i]; |
|||
} |
|||
break; |
|||
|
|||
default: |
|||
// Nothing. We already have the right size.
|
|||
} |
|||
if ((dataAvailable - payloadOffset - payloadLength) < 0) return notComplete; |
|||
|
|||
const payloadEnd = payloadOffset + payloadLength; |
|||
return { |
|||
payload: data.slice(payloadOffset, payloadEnd), |
|||
rest: data.slice(payloadEnd), |
|||
closed, |
|||
}; |
|||
} |
|||
|
|||
class Client extends EventEmitter { |
|||
constructor(port, host) { |
|||
super(); |
|||
this.handleChunk = this._handleChunk.bind(this); |
|||
|
|||
this._port = port; |
|||
this._host = host; |
|||
|
|||
this.reset(); |
|||
} |
|||
|
|||
_handleChunk(chunk) { |
|||
this._unprocessed = Buffer.concat([this._unprocessed, chunk]); |
|||
|
|||
while (this._unprocessed.length > 2) { |
|||
const { |
|||
closed, |
|||
payload: payloadBuffer, |
|||
rest |
|||
} = decodeFrameHybi17(this._unprocessed); |
|||
this._unprocessed = rest; |
|||
|
|||
if (closed) { |
|||
this.reset(); |
|||
return; |
|||
} |
|||
if (payloadBuffer === null) break; |
|||
|
|||
const payloadStr = payloadBuffer.toString(); |
|||
debuglog('< %s', payloadStr); |
|||
const lastChar = payloadStr[payloadStr.length - 1]; |
|||
if (payloadStr[0] !== '{' || lastChar !== '}') { |
|||
throw new Error(`Payload does not look like JSON: ${payloadStr}`); |
|||
} |
|||
let payload; |
|||
try { |
|||
payload = JSON.parse(payloadStr); |
|||
} catch (parseError) { |
|||
parseError.string = payloadStr; |
|||
throw parseError; |
|||
} |
|||
|
|||
const { id, method, params, result, error } = payload; |
|||
if (id) { |
|||
const handler = this._pending[id]; |
|||
if (handler) { |
|||
delete this._pending[id]; |
|||
handler(error, result); |
|||
} |
|||
} else if (method) { |
|||
this.emit('debugEvent', method, params); |
|||
this.emit(method, params); |
|||
} else { |
|||
throw new Error(`Unsupported response: ${payloadStr}`); |
|||
} |
|||
} |
|||
} |
|||
|
|||
reset() { |
|||
if (this._http) { |
|||
this._http.destroy(); |
|||
} |
|||
this._http = null; |
|||
this._lastId = 0; |
|||
this._socket = null; |
|||
this._pending = {}; |
|||
this._unprocessed = Buffer.alloc(0); |
|||
} |
|||
|
|||
callMethod(method, params) { |
|||
return new Promise((resolve, reject) => { |
|||
if (!this._socket) { |
|||
reject(new Error('Use `run` to start the app again.')); |
|||
return; |
|||
} |
|||
const data = { id: ++this._lastId, method, params }; |
|||
this._pending[data.id] = (error, result) => { |
|||
if (error) reject(unpackError(error)); |
|||
else resolve(isEmpty(result) ? undefined : result); |
|||
}; |
|||
const json = JSON.stringify(data); |
|||
debuglog('> %s', json); |
|||
this._socket.write(encodeFrameHybi17(Buffer.from(json))); |
|||
}); |
|||
} |
|||
|
|||
_fetchJSON(urlPath) { |
|||
return new Promise((resolve, reject) => { |
|||
const httpReq = http.get({ |
|||
host: this._host, |
|||
port: this._port, |
|||
path: urlPath, |
|||
}); |
|||
|
|||
const chunks = []; |
|||
|
|||
function onResponse(httpRes) { |
|||
function parseChunks() { |
|||
const resBody = Buffer.concat(chunks).toString(); |
|||
if (httpRes.statusCode !== 200) { |
|||
reject(new Error(`Unexpected ${httpRes.statusCode}: ${resBody}`)); |
|||
return; |
|||
} |
|||
try { |
|||
resolve(JSON.parse(resBody)); |
|||
} catch (parseError) { |
|||
reject(new Error(`Response didn't contain JSON: ${resBody}`)); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
httpRes.on('error', reject); |
|||
httpRes.on('data', (chunk) => chunks.push(chunk)); |
|||
httpRes.on('end', parseChunks); |
|||
} |
|||
|
|||
httpReq.on('error', reject); |
|||
httpReq.on('response', onResponse); |
|||
}); |
|||
} |
|||
|
|||
connect() { |
|||
return this._discoverWebsocketPath() |
|||
.then((urlPath) => this._connectWebsocket(urlPath)); |
|||
} |
|||
|
|||
_discoverWebsocketPath() { |
|||
return this._fetchJSON('/json') |
|||
.then(([{ webSocketDebuggerUrl }]) => |
|||
URL.parse(webSocketDebuggerUrl).path); |
|||
} |
|||
|
|||
_connectWebsocket(urlPath) { |
|||
this.reset(); |
|||
|
|||
const key1 = crypto.randomBytes(16).toString('base64'); |
|||
debuglog('request websocket', key1); |
|||
|
|||
const httpReq = this._http = http.request({ |
|||
host: this._host, |
|||
port: this._port, |
|||
path: urlPath, |
|||
headers: { |
|||
Connection: 'Upgrade', |
|||
Upgrade: 'websocket', |
|||
'Sec-WebSocket-Key': key1, |
|||
'Sec-WebSocket-Version': '13', |
|||
}, |
|||
}); |
|||
httpReq.on('error', (e) => { |
|||
this.emit('error', e); |
|||
}); |
|||
httpReq.on('response', (httpRes) => { |
|||
if (httpRes.statusCode >= 400) { |
|||
process.stderr.write(`Unexpected HTTP code: ${httpRes.statusCode}\n`); |
|||
httpRes.pipe(process.stderr); |
|||
} else { |
|||
httpRes.pipe(process.stderr); |
|||
} |
|||
}); |
|||
|
|||
const handshakeListener = (res, socket) => { |
|||
// TODO: we *could* validate res.headers[sec-websocket-accept]
|
|||
debuglog('websocket upgrade'); |
|||
|
|||
this._socket = socket; |
|||
socket.on('data', this.handleChunk); |
|||
socket.on('close', () => { |
|||
this.emit('close'); |
|||
}); |
|||
|
|||
Promise.all([ |
|||
this.callMethod('Runtime.enable'), |
|||
this.callMethod('Debugger.enable'), |
|||
this.callMethod('Debugger.setPauseOnExceptions', { state: 'none' }), |
|||
this.callMethod('Debugger.setAsyncCallStackDepth', { maxDepth: 0 }), |
|||
this.callMethod('Profiler.enable'), |
|||
this.callMethod('Profiler.setSamplingInterval', { interval: 100 }), |
|||
this.callMethod('Debugger.setBlackboxPatterns', { patterns: [] }), |
|||
this.callMethod('Runtime.runIfWaitingForDebugger'), |
|||
]).then(() => { |
|||
this.emit('ready'); |
|||
}, (error) => { |
|||
this.emit('error', error); |
|||
}); |
|||
}; |
|||
|
|||
return new Promise((resolve, reject) => { |
|||
this.once('error', reject); |
|||
this.once('ready', resolve); |
|||
|
|||
httpReq.on('upgrade', handshakeListener); |
|||
httpReq.end(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
module.exports = Client; |
File diff suppressed because it is too large
@ -0,0 +1,45 @@ |
|||
{ |
|||
"name": "node-inspect", |
|||
"version": "1.10.4", |
|||
"description": "Node Inspect", |
|||
"license": "MIT", |
|||
"main": "lib/_inspect.js", |
|||
"bin": "cli.js", |
|||
"homepage": "https://github.com/buggerjs/node-inspect", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+ssh://git@github.com/buggerjs/node-inspect" |
|||
}, |
|||
"bugs": { |
|||
"url": "https://github.com/buggerjs/node-inspect/issues" |
|||
}, |
|||
"scripts": { |
|||
"pretest": "eslint --rulesdir=tools/eslint-rules lib test", |
|||
"test": "tap \"test/**/*.test.js\"", |
|||
"posttest": "nlm verify" |
|||
}, |
|||
"nlm": { |
|||
"license": { |
|||
"files": [ |
|||
"lib" |
|||
] |
|||
} |
|||
}, |
|||
"dependencies": {}, |
|||
"devDependencies": { |
|||
"eslint": "^3.10.2", |
|||
"nlm": "^3.0.0", |
|||
"tap": "^7.1.2" |
|||
}, |
|||
"author": { |
|||
"name": "Jan Krems", |
|||
"email": "jan.krems@gmail.com" |
|||
}, |
|||
"files": [ |
|||
"*.js", |
|||
"lib" |
|||
], |
|||
"publishConfig": { |
|||
"registry": "https://registry.npmjs.org" |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
'use strict'; |
|||
const Path = require('path'); |
|||
|
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('display and navigate backtrace', (t) => { |
|||
const script = Path.join('examples', 'backtrace.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => cli.command('bt')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 topFn ${script}:8:2`); |
|||
}) |
|||
.then(() => cli.command('backtrace')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 topFn ${script}:8:2`); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,195 @@ |
|||
'use strict'; |
|||
const Path = require('path'); |
|||
|
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('stepping through breakpoints', (t) => { |
|||
const script = Path.join('examples', 'break.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:1`, |
|||
'pauses in the first line of the script'); |
|||
t.match( |
|||
cli.output, |
|||
/> 1 \(function \([^)]+\) \{ const x = 10;/, |
|||
'shows the source and marks the current line'); |
|||
}) |
|||
.then(() => cli.stepCommand('n')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:2`, |
|||
'pauses in next line of the script'); |
|||
t.match( |
|||
cli.output, |
|||
'> 2 let name = \'World\';', |
|||
'marks the 2nd line'); |
|||
}) |
|||
.then(() => cli.stepCommand('next')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:3`, |
|||
'pauses in next line of the script'); |
|||
t.match( |
|||
cli.output, |
|||
'> 3 name = \'Robin\';', |
|||
'marks the 3nd line'); |
|||
}) |
|||
.then(() => cli.stepCommand('cont')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:10`, |
|||
'pauses on the next breakpoint'); |
|||
t.match( |
|||
cli.output, |
|||
'>10 debugger;', |
|||
'marks the debugger line'); |
|||
}) |
|||
|
|||
// Prepare additional breakpoints
|
|||
.then(() => cli.command('sb("break.js", 6)')) |
|||
.then(() => t.notMatch(cli.output, 'Could not resolve breakpoint')) |
|||
.then(() => cli.command('sb("otherFunction()")')) |
|||
.then(() => cli.command('sb(16)')) |
|||
.then(() => t.notMatch(cli.output, 'Could not resolve breakpoint')) |
|||
.then(() => cli.command('breakpoints')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 ${script}:6`); |
|||
t.match(cli.output, `#1 ${script}:16`); |
|||
}) |
|||
|
|||
.then(() => cli.command('list()')) |
|||
.then(() => { |
|||
t.match(cli.output, '>10 debugger;', 'prints and marks current line'); |
|||
t.strictDeepEqual( |
|||
cli.parseSourceLines(), |
|||
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], |
|||
'prints 5 lines before and after'); |
|||
}) |
|||
.then(() => cli.command('list(2)')) |
|||
.then(() => { |
|||
t.match(cli.output, '>10 debugger;', 'prints and marks current line'); |
|||
t.strictDeepEqual( |
|||
cli.parseSourceLines(), |
|||
[8, 9, 10, 11, 12], |
|||
'prints 2 lines before and after'); |
|||
}) |
|||
|
|||
.then(() => cli.stepCommand('s')) |
|||
.then(() => cli.stepCommand('')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'break in timers.js', |
|||
'entered timers.js'); |
|||
}) |
|||
.then(() => cli.stepCommand('cont')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:16`, |
|||
'found breakpoint we set above w/ line number only'); |
|||
}) |
|||
.then(() => cli.stepCommand('cont')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:6`, |
|||
'found breakpoint we set above w/ line number & script'); |
|||
}) |
|||
.then(() => cli.stepCommand('')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`debugCommand in ${script}:14`, |
|||
'found function breakpoint we set above'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
|||
|
|||
test('sb before loading file', (t) => { |
|||
const script = Path.join('examples', 'cjs', 'index.js'); |
|||
const otherScript = Path.join('examples', 'cjs', 'other.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('sb("other.js", 3)')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'not loaded yet', |
|||
'warns that the script was not loaded yet'); |
|||
}) |
|||
.then(() => cli.stepCommand('cont')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${otherScript}:3`, |
|||
'found breakpoint in file that was not loaded yet'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
|||
|
|||
test('clearBreakpoint', (t) => { |
|||
const script = Path.join('examples', 'break.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('sb("break.js", 3)')) |
|||
.then(() => cli.command('sb("break.js", 9)')) |
|||
.then(() => cli.command('breakpoints')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 ${script}:3`); |
|||
t.match(cli.output, `#1 ${script}:9`); |
|||
}) |
|||
.then(() => cli.command('clearBreakpoint("break.js", 4)')) |
|||
.then(() => { |
|||
t.match(cli.output, 'Could not find breakpoint'); |
|||
}) |
|||
.then(() => cli.command('clearBreakpoint("not-such-script.js", 3)')) |
|||
.then(() => { |
|||
t.match(cli.output, 'Could not find breakpoint'); |
|||
}) |
|||
.then(() => cli.command('clearBreakpoint("break.js", 3)')) |
|||
.then(() => cli.command('breakpoints')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 ${script}:9`); |
|||
}) |
|||
.then(() => cli.stepCommand('cont')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:9`, |
|||
'hits the 2nd breakpoint because the 1st was cleared'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,63 @@ |
|||
'use strict'; |
|||
const Path = require('path'); |
|||
|
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('break on (uncaught) exceptions', (t) => { |
|||
const script = Path.join('examples', 'exceptions.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:1`); |
|||
}) |
|||
// making sure it will die by default:
|
|||
.then(() => cli.command('c')) |
|||
.then(() => cli.waitFor(/disconnect/)) |
|||
|
|||
// Next run: With `breakOnException` it pauses in both places
|
|||
.then(() => cli.stepCommand('r')) |
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:1`); |
|||
}) |
|||
.then(() => cli.command('breakOnException')) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => { |
|||
t.match(cli.output, `exception in ${script}:4`); |
|||
}) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => { |
|||
t.match(cli.output, `exception in ${script}:10`); |
|||
}) |
|||
|
|||
// Next run: With `breakOnUncaught` it only pauses on the 2nd exception
|
|||
.then(() => cli.command('breakOnUncaught')) |
|||
.then(() => cli.stepCommand('r')) // also, the setting survives the restart
|
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:1`); |
|||
}) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => { |
|||
t.match(cli.output, `exception in ${script}:10`); |
|||
}) |
|||
|
|||
// Next run: Back to the initial state! It should die again.
|
|||
.then(() => cli.command('breakOnNone')) |
|||
.then(() => cli.stepCommand('r')) |
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:1`); |
|||
}) |
|||
.then(() => cli.command('c')) |
|||
.then(() => cli.waitFor(/disconnect/)) |
|||
|
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,77 @@ |
|||
'use strict'; |
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('examples/alive.js', (t) => { |
|||
const cli = startCLI(['examples/alive.js']); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('exec [typeof heartbeat, typeof process.exit]')) |
|||
.then(() => { |
|||
t.match(cli.output, '[ \'function\', \'function\' ]', 'works w/o paren'); |
|||
}) |
|||
.then(() => cli.command('repl')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'Press Ctrl + C to leave debug repl\n> ', |
|||
'shows hint for how to leave repl'); |
|||
t.notMatch(cli.output, 'debug>', 'changes the repl style'); |
|||
}) |
|||
.then(() => cli.command('[typeof heartbeat, typeof process.exit]')) |
|||
.then(() => cli.waitFor(/function/)) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'[ \'function\', \'function\' ]', 'can evaluate in the repl'); |
|||
t.match(cli.output, /> $/); |
|||
}) |
|||
.then(() => cli.ctrlC()) |
|||
.then(() => cli.waitFor(/debug> $/)) |
|||
.then(() => cli.command('exec("[typeof heartbeat, typeof process.exit]")')) |
|||
.then(() => { |
|||
t.match(cli.output, '[ \'function\', \'function\' ]', 'works w/ paren'); |
|||
}) |
|||
.then(() => cli.command('cont')) |
|||
.then(() => cli.command('exec [typeof heartbeat, typeof process.exit]')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'[ \'undefined\', \'function\' ]', |
|||
'non-paused exec can see global but not module-scope values'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
|||
|
|||
test('exec .scope', (t) => { |
|||
const cli = startCLI(['examples/backtrace.js']); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => cli.command('exec .scope')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'\'moduleScoped\'', 'displays closure from module body'); |
|||
t.match(cli.output, '\'a\'', 'displays local / function arg'); |
|||
t.match(cli.output, '\'l1\'', 'displays local scope'); |
|||
t.notMatch(cli.output, '\'encodeURIComponent\'', 'omits global scope'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,22 @@ |
|||
'use strict'; |
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('examples/empty.js', (t) => { |
|||
const cli = startCLI(['examples/empty.js']); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('help')) |
|||
.then(() => { |
|||
t.match(cli.output, /run, restart, r\s+/m); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,25 @@ |
|||
'use strict'; |
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('launch CLI w/o args', (t) => { |
|||
const cli = startCLI([]); |
|||
return cli.quit() |
|||
.then((code) => { |
|||
t.equal(code, 1, 'exits with non-zero exit code'); |
|||
t.match(cli.output, /^Usage:/, 'Prints usage info'); |
|||
}); |
|||
}); |
|||
|
|||
test('launch w/ invalid host:port', (t) => { |
|||
const cli = startCLI(['localhost:914']); |
|||
return cli.quit() |
|||
.then((code) => { |
|||
t.match( |
|||
cli.output, |
|||
'failed to connect', |
|||
'Tells the user that the connection failed'); |
|||
t.equal(code, 1, 'exits with non-zero exit code'); |
|||
}); |
|||
}); |
@ -0,0 +1,110 @@ |
|||
'use strict'; |
|||
const Path = require('path'); |
|||
|
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('examples/empty.js', (t) => { |
|||
const script = Path.join('examples', 'empty.js'); |
|||
const cli = startCLI([script]); |
|||
return cli.waitForPrompt() |
|||
.then(() => { |
|||
t.match(cli.output, 'debug>', 'prints a prompt'); |
|||
t.match( |
|||
cli.output, |
|||
'< Debugger listening on port 9229', |
|||
'forwards child output'); |
|||
}) |
|||
.then(() => cli.command('["hello", "world"].join(" ")')) |
|||
.then(() => { |
|||
t.match(cli.output, 'hello world', 'prints the result'); |
|||
}) |
|||
.then(() => cli.command('')) |
|||
.then(() => { |
|||
t.match(cli.output, 'hello world', 'repeats the last command on <enter>'); |
|||
}) |
|||
.then(() => cli.command('version')) |
|||
.then(() => { |
|||
t.match(cli.output, process.versions.v8, 'version prints the v8 version'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then((code) => { |
|||
t.equal(code, 0, 'exits with success'); |
|||
}); |
|||
}); |
|||
|
|||
test('run after quit / restart', (t) => { |
|||
const script = Path.join('examples', 'three-lines.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.stepCommand('n')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:2`, |
|||
'steps to the 2nd line'); |
|||
}) |
|||
.then(() => cli.command('cont')) |
|||
.then(() => cli.waitFor(/disconnect/)) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
'Waiting for the debugger to disconnect', |
|||
'the child was done'); |
|||
}) |
|||
.then(() => { |
|||
// On windows the socket won't close by itself
|
|||
return cli.command('kill'); |
|||
}) |
|||
.then(() => cli.command('cont')) |
|||
.then(() => cli.waitFor(/start the app/)) |
|||
.then(() => { |
|||
t.match(cli.output, 'Use `run` to start the app again'); |
|||
}) |
|||
.then(() => cli.stepCommand('run')) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:1`, |
|||
'is back at the beginning'); |
|||
}) |
|||
.then(() => cli.stepCommand('n')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:2`, |
|||
'steps to the 2nd line'); |
|||
}) |
|||
.then(() => cli.stepCommand('restart')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:1`, |
|||
'is back at the beginning'); |
|||
}) |
|||
.then(() => cli.command('kill')) |
|||
.then(() => cli.command('cont')) |
|||
.then(() => cli.waitFor(/start the app/)) |
|||
.then(() => { |
|||
t.match(cli.output, 'Use `run` to start the app again'); |
|||
}) |
|||
.then(() => cli.stepCommand('run')) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
`break in ${script}:1`, |
|||
'is back at the beginning'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,31 @@ |
|||
'use strict'; |
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('Debugger agent direct access', (t) => { |
|||
const cli = startCLI(['examples/empty.js']); |
|||
const scriptPattern = /^\* (\d+): examples(?:\/|\\)empty.js/; |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('scripts')) |
|||
.then(() => { |
|||
const [, scriptId] = cli.output.match(scriptPattern); |
|||
return cli.command( |
|||
`Debugger.getScriptSource({ scriptId: '${scriptId}' })` |
|||
); |
|||
}) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
/scriptSource: '\(function \([^)]+\) \{ \\n}\);'/); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,56 @@ |
|||
'use strict'; |
|||
const Path = require('path'); |
|||
|
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('run after quit / restart', (t) => { |
|||
const script = Path.join('examples', 'three-lines.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('breakpoints')) |
|||
.then(() => { |
|||
t.match(cli.output, 'No breakpoints yet'); |
|||
}) |
|||
.then(() => cli.command('sb(2)')) |
|||
.then(() => cli.command('sb(3)')) |
|||
.then(() => cli.command('breakpoints')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 ${script}:2`); |
|||
t.match(cli.output, `#1 ${script}:3`); |
|||
}) |
|||
.then(() => cli.stepCommand('c')) // hit line 2
|
|||
.then(() => cli.stepCommand('c')) // hit line 3
|
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:3`); |
|||
}) |
|||
.then(() => cli.command('restart')) |
|||
.then(() => cli.waitFor([/break in examples/, /breakpoints restored/])) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:1`); |
|||
}) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:2`); |
|||
}) |
|||
.then(() => cli.stepCommand('c')) |
|||
.then(() => { |
|||
t.match(cli.output, `break in ${script}:3`); |
|||
}) |
|||
.then(() => cli.command('breakpoints')) |
|||
.then(() => { |
|||
t.match(cli.output, `#0 ${script}:2`); |
|||
t.match(cli.output, `#1 ${script}:3`); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,32 @@ |
|||
'use strict'; |
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
function delay(ms) { |
|||
return new Promise((resolve) => setTimeout(resolve, ms)); |
|||
} |
|||
|
|||
test('profiles', (t) => { |
|||
const cli = startCLI(['examples/empty.js']); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('exec console.profile()')) |
|||
.then(() => { |
|||
t.match(cli.output, 'undefined'); |
|||
}) |
|||
.then(() => cli.command('exec console.profileEnd()')) |
|||
.then(() => delay(250)) |
|||
.then(() => { |
|||
t.match(cli.output, 'undefined'); |
|||
t.match(cli.output, 'Captured new CPU profile.'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,43 @@ |
|||
'use strict'; |
|||
const Path = require('path'); |
|||
|
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('list scripts', (t) => { |
|||
const script = Path.join('examples', 'empty.js'); |
|||
const cli = startCLI([script]); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('scripts')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
/^\* \d+: examples(?:\/|\\)empty\.js/, |
|||
'lists the user script'); |
|||
t.notMatch( |
|||
cli.output, |
|||
/\d+: module\.js <native>/, |
|||
'omits node-internal scripts'); |
|||
}) |
|||
.then(() => cli.command('scripts(true)')) |
|||
.then(() => { |
|||
t.match( |
|||
cli.output, |
|||
/\* \d+: examples(?:\/|\\)empty\.js/, |
|||
'lists the user script'); |
|||
t.match( |
|||
cli.output, |
|||
/\d+: module\.js <native>/, |
|||
'includes node-internal scripts'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,136 @@ |
|||
'use strict'; |
|||
const spawn = require('child_process').spawn; |
|||
|
|||
const CLI = |
|||
process.env.USE_EMBEDDED_NODE_INSPECT === '1' ? |
|||
'inspect' : |
|||
require.resolve('../../cli.js'); |
|||
|
|||
function startCLI(args) { |
|||
const child = spawn(process.execPath, [CLI, ...args]); |
|||
let isFirstStdoutChunk = true; |
|||
|
|||
const outputBuffer = []; |
|||
function bufferOutput(chunk) { |
|||
if (isFirstStdoutChunk) { |
|||
isFirstStdoutChunk = false; |
|||
outputBuffer.push(chunk.replace(/^debug>\s*/, '')); |
|||
} else { |
|||
outputBuffer.push(chunk); |
|||
} |
|||
} |
|||
|
|||
function getOutput() { |
|||
return outputBuffer.join('').toString() |
|||
.replace(/^[^\n]*?[\b]/mg, ''); |
|||
} |
|||
|
|||
child.stdout.setEncoding('utf8'); |
|||
child.stdout.on('data', bufferOutput); |
|||
child.stderr.setEncoding('utf8'); |
|||
child.stderr.on('data', bufferOutput); |
|||
|
|||
if (process.env.VERBOSE === '1') { |
|||
child.stdout.pipe(process.stderr); |
|||
child.stderr.pipe(process.stderr); |
|||
} |
|||
|
|||
return { |
|||
flushOutput() { |
|||
const output = this.output; |
|||
outputBuffer.length = 0; |
|||
return output; |
|||
}, |
|||
|
|||
waitFor(pattern, timeout = 2000) { |
|||
function checkPattern(str) { |
|||
if (Array.isArray(pattern)) { |
|||
return pattern.every((p) => p.test(str)); |
|||
} |
|||
return pattern.test(str); |
|||
} |
|||
|
|||
return new Promise((resolve, reject) => { |
|||
function checkOutput() { |
|||
if (checkPattern(getOutput())) { |
|||
tearDown(); // eslint-disable-line no-use-before-define
|
|||
resolve(); |
|||
} |
|||
} |
|||
|
|||
function onChildExit() { |
|||
tearDown(); // eslint-disable-line no-use-before-define
|
|||
reject(new Error( |
|||
`Child quit while waiting for ${pattern}; found: ${this.output}`)); |
|||
} |
|||
|
|||
const timer = setTimeout(() => { |
|||
tearDown(); // eslint-disable-line no-use-before-define
|
|||
reject(new Error([ |
|||
`Timeout (${timeout}) while waiting for ${pattern}`, |
|||
`found: ${this.output}`, |
|||
].join('; '))); |
|||
}, timeout); |
|||
|
|||
function tearDown() { |
|||
clearTimeout(timer); |
|||
child.stdout.removeListener('data', checkOutput); |
|||
child.removeListener('exit', onChildExit); |
|||
} |
|||
|
|||
child.on('exit', onChildExit); |
|||
child.stdout.on('data', checkOutput); |
|||
checkOutput(); |
|||
}); |
|||
}, |
|||
|
|||
waitForPrompt(timeout = 2000) { |
|||
return this.waitFor(/>\s+$/, timeout); |
|||
}, |
|||
|
|||
ctrlC() { |
|||
return this.command('.interrupt'); |
|||
}, |
|||
|
|||
get output() { |
|||
return getOutput(); |
|||
}, |
|||
|
|||
get rawOutput() { |
|||
return outputBuffer.join('').toString(); |
|||
}, |
|||
|
|||
parseSourceLines() { |
|||
return getOutput().split('\n') |
|||
.map((line) => line.match(/(?:\*|>)?\s*(\d+)/)) |
|||
.filter((match) => match !== null) |
|||
.map((match) => +match[1]); |
|||
}, |
|||
|
|||
command(input) { |
|||
this.flushOutput(); |
|||
child.stdin.write(input); |
|||
child.stdin.write('\n'); |
|||
return this.waitForPrompt(); |
|||
}, |
|||
|
|||
stepCommand(input) { |
|||
this.flushOutput(); |
|||
child.stdin.write(input); |
|||
child.stdin.write('\n'); |
|||
return this |
|||
.waitFor( |
|||
/(?:assert|break|debugCommand|exception|other|promiseRejection) in/ |
|||
) |
|||
.then(() => this.waitForPrompt()); |
|||
}, |
|||
|
|||
quit() { |
|||
return new Promise((resolve) => { |
|||
child.stdin.end(); |
|||
child.on('exit', resolve); |
|||
}); |
|||
}, |
|||
}; |
|||
} |
|||
module.exports = startCLI; |
@ -0,0 +1,42 @@ |
|||
'use strict'; |
|||
const { test } = require('tap'); |
|||
|
|||
const startCLI = require('./start-cli'); |
|||
|
|||
test('stepping through breakpoints', (t) => { |
|||
const cli = startCLI(['examples/break.js']); |
|||
|
|||
function onFatal(error) { |
|||
cli.quit(); |
|||
throw error; |
|||
} |
|||
|
|||
return cli.waitFor(/break/) |
|||
.then(() => cli.waitForPrompt()) |
|||
.then(() => cli.command('watch("x")')) |
|||
.then(() => cli.command('watch("\\"Hello\\"")')) |
|||
.then(() => cli.command('watch("42")')) |
|||
.then(() => cli.command('watch("NaN")')) |
|||
.then(() => cli.command('watch("true")')) |
|||
.then(() => cli.command('watch("[1, 2]")')) |
|||
.then(() => cli.command('watch("process.env")')) |
|||
.then(() => cli.command('watchers')) |
|||
.then(() => { |
|||
t.match(cli.output, 'x is not defined'); |
|||
}) |
|||
.then(() => cli.command('unwatch("42")')) |
|||
.then(() => cli.stepCommand('n')) |
|||
.then(() => { |
|||
t.match(cli.output, '0: x = 10'); |
|||
t.match(cli.output, '1: "Hello" = \'Hello\''); |
|||
t.match(cli.output, '2: NaN = NaN'); |
|||
t.match(cli.output, '3: true = true'); |
|||
t.match(cli.output, '4: [1, 2] = [ 1, 2 ]'); |
|||
t.match( |
|||
cli.output, |
|||
/5: process\.env =\n\s+\{[\s\S]+,\n\s+\.\.\. \}/, |
|||
'shows "..." for process.env'); |
|||
}) |
|||
.then(() => cli.quit()) |
|||
.then(null, onFatal); |
|||
}); |
@ -0,0 +1,9 @@ |
|||
'use strict'; |
|||
const tap = require('tap'); |
|||
|
|||
const nodeInspect = require('../'); |
|||
|
|||
tap.equal( |
|||
9229, |
|||
nodeInspect.port, |
|||
'Uses the --inspect default port'); |
@ -0,0 +1,76 @@ |
|||
/** |
|||
* @fileoverview Align arguments in multiline function calls |
|||
* @author Rich Trott |
|||
*/ |
|||
'use strict'; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
function checkArgumentAlignment(context, node) { |
|||
|
|||
function isNodeFirstInLine(node, byEndLocation) { |
|||
const firstToken = byEndLocation === true ? context.getLastToken(node, 1) : |
|||
context.getTokenBefore(node); |
|||
const startLine = byEndLocation === true ? node.loc.end.line : |
|||
node.loc.start.line; |
|||
const endLine = firstToken ? firstToken.loc.end.line : -1; |
|||
|
|||
return startLine !== endLine; |
|||
} |
|||
|
|||
if (node.arguments.length === 0) |
|||
return; |
|||
|
|||
var msg = ''; |
|||
const first = node.arguments[0]; |
|||
var currentLine = first.loc.start.line; |
|||
const firstColumn = first.loc.start.column; |
|||
|
|||
const ignoreTypes = [ |
|||
'ArrowFunctionExpression', |
|||
'FunctionExpression', |
|||
'ObjectExpression', |
|||
]; |
|||
|
|||
const args = node.arguments; |
|||
|
|||
// For now, don't bother trying to validate potentially complicating things
|
|||
// like closures. Different people will have very different ideas and it's
|
|||
// probably best to implement configuration options.
|
|||
if (args.some((node) => { return ignoreTypes.indexOf(node.type) !== -1; })) { |
|||
return; |
|||
} |
|||
|
|||
if (!isNodeFirstInLine(node)) { |
|||
return; |
|||
} |
|||
|
|||
var misaligned; |
|||
|
|||
args.slice(1).forEach((argument) => { |
|||
if (!misaligned) { |
|||
if (argument.loc.start.line === currentLine + 1) { |
|||
if (argument.loc.start.column !== firstColumn) { |
|||
if (isNodeFirstInLine(argument)) { |
|||
msg = 'Function argument in column ' + |
|||
`${argument.loc.start.column + 1}, ` + |
|||
`expected in ${firstColumn + 1}`; |
|||
misaligned = argument; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
currentLine = argument.loc.start.line; |
|||
}); |
|||
|
|||
if (msg) |
|||
context.report(misaligned, msg); |
|||
} |
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
'CallExpression': (node) => checkArgumentAlignment(context, node) |
|||
}; |
|||
}; |
@ -0,0 +1,68 @@ |
|||
/** |
|||
* @fileoverview Align multiline variable assignments |
|||
* @author Rich Trott |
|||
*/ |
|||
'use strict'; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
function getBinaryExpressionStarts(binaryExpression, starts) { |
|||
function getStartsFromOneSide(side, starts) { |
|||
starts.push(side.loc.start); |
|||
if (side.type === 'BinaryExpression') { |
|||
starts = getBinaryExpressionStarts(side, starts); |
|||
} |
|||
return starts; |
|||
} |
|||
|
|||
starts = getStartsFromOneSide(binaryExpression.left, starts); |
|||
starts = getStartsFromOneSide(binaryExpression.right, starts); |
|||
return starts; |
|||
} |
|||
|
|||
function checkExpressionAlignment(expression) { |
|||
if (!expression) |
|||
return; |
|||
|
|||
var msg = ''; |
|||
|
|||
switch (expression.type) { |
|||
case 'BinaryExpression': |
|||
var starts = getBinaryExpressionStarts(expression, []); |
|||
var startLine = starts[0].line; |
|||
const startColumn = starts[0].column; |
|||
starts.forEach((loc) => { |
|||
if (loc.line > startLine) { |
|||
startLine = loc.line; |
|||
if (loc.column !== startColumn) { |
|||
msg = 'Misaligned multiline assignment'; |
|||
} |
|||
} |
|||
}); |
|||
break; |
|||
} |
|||
return msg; |
|||
} |
|||
|
|||
function testAssignment(context, node) { |
|||
const msg = checkExpressionAlignment(node.right); |
|||
if (msg) |
|||
context.report(node, msg); |
|||
} |
|||
|
|||
function testDeclaration(context, node) { |
|||
node.declarations.forEach((declaration) => { |
|||
const msg = checkExpressionAlignment(declaration.init); |
|||
// const start = declaration.init.loc.start;
|
|||
if (msg) |
|||
context.report(node, msg); |
|||
}); |
|||
} |
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
'AssignmentExpression': (node) => testAssignment(context, node), |
|||
'VariableDeclaration': (node) => testDeclaration(context, node) |
|||
}; |
|||
}; |
@ -0,0 +1,30 @@ |
|||
/** |
|||
* @fileoverview Prohibit use of a single argument only in `assert.fail()`. It |
|||
* is almost always an error. |
|||
* @author Rich Trott |
|||
*/ |
|||
'use strict'; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
const msg = 'assert.fail() message should be third argument'; |
|||
|
|||
function isAssert(node) { |
|||
return node.callee.object && node.callee.object.name === 'assert'; |
|||
} |
|||
|
|||
function isFail(node) { |
|||
return node.callee.property && node.callee.property.name === 'fail'; |
|||
} |
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
'CallExpression': function(node) { |
|||
if (isAssert(node) && isFail(node) && node.arguments.length === 1) { |
|||
context.report(node, msg); |
|||
} |
|||
} |
|||
}; |
|||
}; |
@ -0,0 +1,25 @@ |
|||
/** |
|||
* @fileoverview Require use of new Buffer constructor methods in lib |
|||
* @author James M Snell |
|||
*/ |
|||
'use strict'; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
const msg = 'Use of the Buffer() constructor has been deprecated. ' + |
|||
'Please use either Buffer.alloc(), Buffer.allocUnsafe(), ' + |
|||
'or Buffer.from()'; |
|||
|
|||
function test(context, node) { |
|||
if (node.callee.name === 'Buffer') { |
|||
context.report(node, msg); |
|||
} |
|||
} |
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
'NewExpression': (node) => test(context, node), |
|||
'CallExpression': (node) => test(context, node) |
|||
}; |
|||
}; |
@ -0,0 +1,31 @@ |
|||
/** |
|||
* @fileoverview Require `throw new Error()` rather than `throw Error()` |
|||
* @author Rich Trott |
|||
*/ |
|||
'use strict'; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
var errorList = context.options.length !== 0 ? context.options : ['Error']; |
|||
|
|||
return { |
|||
'ThrowStatement': function(node) { |
|||
if (node.argument.type === 'CallExpression' && |
|||
errorList.indexOf(node.argument.callee.name) !== -1) { |
|||
context.report(node, 'Use new keyword when throwing.'); |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = { |
|||
'type': 'array', |
|||
'additionalItems': { |
|||
'type': 'string' |
|||
}, |
|||
'uniqueItems': true |
|||
}; |
@ -0,0 +1,46 @@ |
|||
/** |
|||
* @fileoverview Prohibit the use of `let` as the loop variable |
|||
* in the initialization of for, and the left-hand |
|||
* iterator in forIn and forOf loops. |
|||
* |
|||
* @author Jessica Quynh Tran |
|||
*/ |
|||
|
|||
'use strict'; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
create(context) { |
|||
|
|||
const msg = 'Use of `let` as the loop variable in a for-loop is ' + |
|||
'not recommended. Please use `var` instead.'; |
|||
|
|||
/** |
|||
* Report function to test if the for-loop is declared using `let`. |
|||
*/ |
|||
function testForLoop(node) { |
|||
if (node.init && node.init.kind === 'let') { |
|||
context.report(node.init, msg); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Report function to test if the for-in or for-of loop |
|||
* is declared using `let`. |
|||
*/ |
|||
function testForInOfLoop(node) { |
|||
if (node.left && node.left.kind === 'let') { |
|||
context.report(node.left, msg); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
'ForStatement': testForLoop, |
|||
'ForInStatement': testForInOfLoop, |
|||
'ForOfStatement': testForInOfLoop |
|||
}; |
|||
} |
|||
}; |
@ -0,0 +1,39 @@ |
|||
'use strict'; |
|||
|
|||
function isAssert(node) { |
|||
return node.expression && |
|||
node.expression.type === 'CallExpression' && |
|||
node.expression.callee && |
|||
node.expression.callee.name === 'assert'; |
|||
} |
|||
|
|||
function getFirstArg(expression) { |
|||
return expression.arguments && expression.arguments[0]; |
|||
} |
|||
|
|||
function parseError(method, op) { |
|||
return `'assert.${method}' should be used instead of '${op}'`; |
|||
} |
|||
|
|||
const preferedAssertMethod = { |
|||
'===': 'strictEqual', |
|||
'!==': 'notStrictEqual', |
|||
'==': 'equal', |
|||
'!=': 'notEqual' |
|||
}; |
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
ExpressionStatement(node) { |
|||
if (isAssert(node)) { |
|||
const arg = getFirstArg(node.expression); |
|||
if (arg && arg.type === 'BinaryExpression') { |
|||
const assertMethod = preferedAssertMethod[arg.operator]; |
|||
if (assertMethod) { |
|||
context.report(node, parseError(assertMethod, arg.operator)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
}; |
@ -0,0 +1,19 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = function(context) { |
|||
function flagIt(reference) { |
|||
const msg = 'Use const Buffer = require(\'buffer\').Buffer; ' + |
|||
'at the beginning of this file'; |
|||
context.report(reference.identifier, msg); |
|||
} |
|||
|
|||
return { |
|||
'Program:exit': function() { |
|||
const globalScope = context.getScope(); |
|||
const variable = globalScope.set.get('Buffer'); |
|||
if (variable) { |
|||
variable.references.forEach(flagIt); |
|||
} |
|||
} |
|||
}; |
|||
}; |
@ -0,0 +1,99 @@ |
|||
/** |
|||
* @fileoverview Require usage of specified node modules. |
|||
* @author Rich Trott |
|||
*/ |
|||
'use strict'; |
|||
|
|||
var path = require('path'); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
// trim required module names
|
|||
var requiredModules = context.options; |
|||
|
|||
var foundModules = []; |
|||
|
|||
// if no modules are required we don't need to check the CallExpressions
|
|||
if (requiredModules.length === 0) { |
|||
return {}; |
|||
} |
|||
|
|||
/** |
|||
* Function to check if a node is a string literal. |
|||
* @param {ASTNode} node The node to check. |
|||
* @returns {boolean} If the node is a string literal. |
|||
*/ |
|||
function isString(node) { |
|||
return node && node.type === 'Literal' && typeof node.value === 'string'; |
|||
} |
|||
|
|||
/** |
|||
* Function to check if a node is a require call. |
|||
* @param {ASTNode} node The node to check. |
|||
* @returns {boolean} If the node is a require call. |
|||
*/ |
|||
function isRequireCall(node) { |
|||
return node.callee.type === 'Identifier' && node.callee.name === 'require'; |
|||
} |
|||
|
|||
/** |
|||
* Function to check if a node has an argument that is a required module and |
|||
* return its name. |
|||
* @param {ASTNode} node The node to check |
|||
* @returns {undefined|String} required module name or undefined |
|||
*/ |
|||
function getRequiredModuleName(node) { |
|||
var moduleName; |
|||
|
|||
// node has arguments and first argument is string
|
|||
if (node.arguments.length && isString(node.arguments[0])) { |
|||
var argValue = path.basename(node.arguments[0].value.trim()); |
|||
|
|||
// check if value is in required modules array
|
|||
if (requiredModules.indexOf(argValue) !== -1) { |
|||
moduleName = argValue; |
|||
} |
|||
} |
|||
|
|||
return moduleName; |
|||
} |
|||
|
|||
return { |
|||
'CallExpression': function(node) { |
|||
if (isRequireCall(node)) { |
|||
var requiredModuleName = getRequiredModuleName(node); |
|||
|
|||
if (requiredModuleName) { |
|||
foundModules.push(requiredModuleName); |
|||
} |
|||
} |
|||
}, |
|||
'Program:exit': function(node) { |
|||
if (foundModules.length < requiredModules.length) { |
|||
var missingModules = requiredModules.filter( |
|||
function(module) { |
|||
return foundModules.indexOf(module === -1); |
|||
} |
|||
); |
|||
missingModules.forEach(function(moduleName) { |
|||
context.report( |
|||
node, |
|||
'Mandatory module "{{moduleName}}" must be loaded.', |
|||
{ moduleName: moduleName } |
|||
); |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = { |
|||
'type': 'array', |
|||
'additionalItems': { |
|||
'type': 'string' |
|||
}, |
|||
'uniqueItems': true |
|||
}; |
Loading…
Reference in new issue