├── .gitattributes
├── .gitignore
├── .npmignore
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── README.md
├── bin
└── electron-inspector.js
├── bugs.gif
├── lib
├── cli.js
├── index.js
└── rebuild.js
├── npm-shrinkwrap.json
├── package.json
└── src
├── cli.ts
├── electron-rebuild.d.ts
├── electron.d.ts
├── index.ts
├── node-pre-gyp.d.ts
├── rebuild.ts
└── tsconfig.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | /bin/*.js eol=lf
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/.BROWSE.VC.DB*
2 | /node_modules/**
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /.vscode/
2 | /src/
3 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch",
6 | "type": "node",
7 | "request": "launch",
8 | "program": "${workspaceRoot}/lib/index.js",
9 | "stopOnEntry": true,
10 | "args": [],
11 | "cwd": "${workspaceRoot}",
12 | "preLaunchTask": null,
13 | "runtimeExecutable": null,
14 | "runtimeArgs": [
15 | "--nolazy"
16 | ],
17 | "env": {
18 | "NODE_ENV": "development"
19 | },
20 | "console": "internalConsole",
21 | "sourceMaps": false,
22 | "outDir": null
23 | },
24 | {
25 | "name": "Attach",
26 | "type": "node",
27 | "request": "attach",
28 | "port": 5858,
29 | "address": "localhost",
30 | "restart": false,
31 | "sourceMaps": false,
32 | "outDir": null,
33 | "localRoot": "${workspaceRoot}",
34 | "remoteRoot": null
35 | },
36 | {
37 | "name": "Attach to Process",
38 | "type": "node",
39 | "request": "attach",
40 | "processId": "${command.PickProcess}",
41 | "port": 5858,
42 | "sourceMaps": false,
43 | "outDir": null
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "typescript.tsdk": "node_modules/typescript/lib"
4 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Vadim Macagon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # electron-inspector
2 | Debugger UI for the main Electron process
3 |
4 | ## Overview
5 |
6 | This package wraps [`node-inspector`][node-inspector-npm], which can be used to debug JavaScript
7 | code running in the main Electron process. Getting `node-inspector` running can require somewhere
8 | between a little and [a lot of effort][node-inspector-electron-v13x] depending on the Electron
9 | version you wish to debug. The goal of `electron-inspector` is to get `node-inspector` running
10 | with minimum effort on your part.
11 |
12 | [node-inspector-npm]: https://www.npmjs.com/package/node-inspector
13 | [node-inspector-electron-v13x]: http://vadim.macagon.com/blog/2016/09/11/rebuilding-node-inspector-for-electron-v13x/
14 |
15 | ## Prerequisites
16 |
17 | - NPM v3
18 | - NodeJS v4.5 (or later)
19 | - [`electron`][electron-npm], or [`electron-prebuilt`][electron-prebuilt-npm], or [`electron-prebuilt-compile`][electron-prebuilt-compile-npm]
20 | - [`electron-rebuild`][electron-rebuild-npm] (optional)
21 |
22 | [electron-npm]: https://www.npmjs.com/package/electron
23 | [electron-prebuilt-npm]: https://www.npmjs.com/package/electron-prebuilt
24 | [electron-prebuilt-compile-npm]: https://www.npmjs.com/package/electron-prebuilt-compile
25 | [electron-rebuild-npm]: https://www.npmjs.com/package/electron-rebuild
26 |
27 | ## Quick Start
28 |
29 | `electron-inspector` should be installed as a local dev dependency of your Electron app:
30 |
31 | ```shell
32 | npm install electron-inspector --save-dev
33 | ```
34 |
35 | The easiest way to run the inspector in a cross-platform manner is to add an NPM script to your
36 | `package.json`, for example:
37 |
38 | ```json
39 | "scripts": {
40 | "inspect-main": "electron-inspector"
41 | }
42 | ```
43 |
44 | Then run the `inspect-main` script on the command line with:
45 |
46 | ```shell
47 | npm run inspect-main
48 | ```
49 |
50 | Alternatively, if you don't want to mess with your `package.json` you can directly execute
51 | `electron-inspector` (macOS / Linux), or `.\\node_modules\\.bin\\electron-inspector` (Windows).
52 |
53 | On startup `electron-inspector` will check for compatibility of the native modules in
54 | `node-inspector` with the Electron version you wish to debug, if the compatibility check
55 | fails and `electron-rebuild` is installed then the native modules will be automatically
56 | rebuilt. You can disable auto-rebuild using the `--no-auto-rebuild` command line option.
57 |
58 | When `electron-inspector` finally gets `node-inspector` running you will see a URL printed to the
59 | console window. For example:
60 |
61 | ```shell
62 | Visit http://127.0.0.1:8080/?port=5858 to start debugging.
63 | ```
64 |
65 | You can then [start Electron in debug mode][electron-debug] and open the given URL in your browser.
66 |
67 | [electron-debug]: http://electron.atom.io/docs/tutorial/debugging-main-process/#enable-debug-mode-for-electron
68 |
69 | ## Known Bugs
70 |
71 | - Somewhere around Chrome 54 the DevTools front-end in `node-inspector` started crashing on startup
72 | because it hasn't been updated in quite a while. `electron-inspector` contains a workaround for
73 | the crash but upon opening the DevTools front-end you'll see
74 | `Error: No NativeModule in target scope` printed out in the console, ignore it.
75 | - On some systems when you launch Electron with the `--debug-brk` flag in order to pause execution
76 | on the first line of code it may look like the DevTools front-end never actually loads your app
77 | code. In this case your app is actually paused on the first line of code, it's just that the
78 | front-end wasn't refreshed to reflect that fact. You can force the DevTools front-end to refresh
79 | by pressing the `Pause script execution (F8)` button.
80 |
81 | 
82 |
83 | ## Configuration
84 |
85 | `node-inspector` can be configured in [multiple ways][node-inspector-config], `electron-inspector`
86 | will pass through most of the supported command line options.
87 |
88 | [node-inspector-config]: https://www.npmjs.com/package/node-inspector#configuration
89 |
90 | ### Command Line Options
91 |
92 | `electron-inspector` accepts most of the commandline options `node-inspector` does:
93 |
94 |
95 |
96 |
97 | Option |
98 | Alias |
99 | Default |
100 | Description |
101 |
102 |
103 |
104 |
105 | --help |
106 | |
107 | |
108 |
109 | Display information about the available options.
110 | |
111 |
112 |
113 | --auto-rebuild |
114 | |
115 | true |
116 |
117 | Toggle automatic rebuild of native node-inspector modules, this only works if
118 | electron-rebuild is installed.
119 | |
120 |
121 |
122 | --electron |
123 | |
124 | |
125 |
126 | Path to the Electron executable that should be used to run node-inspector .
127 | |
128 |
129 |
130 | node-inspector |
131 | |
132 | |
133 | |
134 |
135 |
136 | --config |
137 | |
138 | |
139 |
140 | Path to file with node-inspector config information.
141 | |
142 |
143 |
144 | --debug-port |
145 |
146 | -d
147 | |
148 | 5858 |
149 |
150 | Debug port of the Electron process you wish to debug.
151 | |
152 |
153 |
154 | --web-host |
155 | |
156 | 0.0.0.0 |
157 |
158 | Host to listen on for node-inspector web interface, 127.0.0.1 by
159 | default.
160 | |
161 |
162 |
163 | --web-port |
164 |
165 | -p
166 | |
167 | 8080 |
168 |
169 | Port to listen on for node-inspector web interface.
170 | |
171 |
172 |
173 | --save-live-edit |
174 | |
175 | false |
176 |
177 | Save live edit changes to disk (update the edited files).
178 | |
179 |
180 |
181 | --preload |
182 | |
183 | true |
184 |
185 | Preload *.js files. You can disable this option to speed up the startup.
186 | |
187 |
188 |
189 | --inject |
190 | |
191 | true |
192 |
193 | Enable injection of debugger extensions into the debugged process. It's possible disable only
194 | part of injections using subkeys, e.g. --no-inject.network .
195 | Allowed keys: network , profiles , console .
196 | |
197 |
198 | --hidden |
199 | |
200 | |
201 |
202 | Array of files to hide from the UI, breakpoints in these files will be ignored. All paths are
203 | interpreted as regular expressions.
204 | |
205 |
206 |
207 | --stack-trace-limit |
208 | |
209 |
210 | 50
211 | |
212 |
213 | Number of stack frames to show on a breakpoint.
214 | |
215 |
216 |
217 | --ssl-key |
218 | |
219 | |
220 |
221 | Path to file containing a valid SSL key.
222 | |
223 |
224 |
225 | --ssl-cert |
226 | |
227 | |
228 |
229 | Path to file containing a valid SSL certificate.
230 | |
231 |
232 |
233 |
234 |
235 | # License
236 |
237 | MIT
238 |
--------------------------------------------------------------------------------
/bin/electron-inspector.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | require('../lib/cli');
4 |
--------------------------------------------------------------------------------
/bugs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enlight/electron-inspector/cb2cc7bd555b99919e8be54b3be114049b0d34ad/bugs.gif
--------------------------------------------------------------------------------
/lib/cli.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 | "use strict";
4 | const yargs = require("yargs");
5 | const index_1 = require("./index");
6 | const argv = yargs
7 | .usage('Usage: $0 [options]')
8 | .option('debug-port', {
9 | alias: 'd',
10 | type: 'number',
11 | default: 5858,
12 | describe: 'Debug port of the Electron process you wish to debug.'
13 | })
14 | .option('web-host', {
15 | type: 'string',
16 | default: '0.0.0.0',
17 | describe: "Host to listen on for node-inspector's web interface."
18 | })
19 | .option('web-port', {
20 | alias: ['p', 'port'],
21 | type: 'number',
22 | default: 8080,
23 | describe: "Port to listen on for node-inspector's web interface."
24 | })
25 | .option('save-live-edit', {
26 | type: 'boolean',
27 | default: false,
28 | describe: 'Save live edit changes to disk (update the edited files).'
29 | })
30 | .option('preload', {
31 | type: 'boolean',
32 | default: true,
33 | describe: 'Preload *.js files. You can disable this option to speed up the startup.'
34 | })
35 | .option('inject', {
36 | type: 'boolean',
37 | default: true,
38 | description: 'Enable/disable injection of debugger extensions into the debugged process.\n' +
39 | "It's posible to disable only some of the injections using subkeys.\n" +
40 | 'Available subkeys: network, profiles, console'
41 | })
42 | .option('hidden', {
43 | type: 'string',
44 | describe: 'Array of files to hide from the UI.' +
45 | 'Breakpoints in these files will be ignored.\n' +
46 | 'All paths are interpreted as regular expressions.'
47 | })
48 | .option('stack-trace-limit', {
49 | type: 'number',
50 | default: 50,
51 | describe: 'Number of stack frames to show on a breakpoint.'
52 | })
53 | .option('ssl-key', {
54 | type: 'string',
55 | describe: 'A file containing a valid SSL key.'
56 | })
57 | .option('ssl-cert', {
58 | type: 'string',
59 | describe: 'A file containing a valid SSL certificate.'
60 | })
61 | .option('electron', {
62 | type: 'string',
63 | describe: 'Path to the Electron executable that should be used to run node-inspector.'
64 | })
65 | .option('auto-rebuild', {
66 | type: 'boolean',
67 | default: true,
68 | describe: 'Toggle automatic rebuild of native node-inspector modules,\n' +
69 | 'this only works if electron-rebuild is installed.'
70 | })
71 | .help()
72 | .argv;
73 | index_1.inspect(argv);
74 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 | "use strict";
4 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5 | return new (P || (P = Promise))(function (resolve, reject) {
6 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7 | function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
8 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
9 | step((generator = generator.apply(thisArg, _arguments)).next());
10 | });
11 | };
12 | const path = require("path");
13 | const fs = require("fs");
14 | const child_process = require("child_process");
15 | const node_pre_gyp_1 = require("node-pre-gyp");
16 | function getElectronPackageVersion(moduleName) {
17 | // try to grab the version from the electron-prebuilt package if it's installed
18 | const packageText = fs.readFileSync(require.resolve(`${moduleName}/package.json`), 'utf8');
19 | const packageJson = JSON.parse(packageText);
20 | return packageJson.version;
21 | }
22 | const electronVersionRegex = /^v(\d{1,2}\.\d{1,2}\.\d{1,2})$/;
23 | /**
24 | * Obtain the path of the Electron executable and its version.
25 | *
26 | * @param exePath Path of an Electron executable, if omitted an attempt will be made to find it
27 | * by looking for `electron`, `electron-prebuilt`, or `electron-prebuilt-compile`.
28 | * @return `null` if an Electron executable wasn't found, or its version couldn't be determined.
29 | */
30 | function getElectronInfo(exePath) {
31 | // if a path to the electron executable was provided run it to figure out its version
32 | if (exePath) {
33 | if (fs.existsSync(exePath)) {
34 | try {
35 | const stdout = child_process.execFileSync(exePath, ['--version'], { encoding: 'utf8' });
36 | const version = stdout.replace(/[\r\n]/g, '');
37 | const match = electronVersionRegex.exec(version);
38 | if (match) {
39 | return {
40 | executablePath: exePath,
41 | version: match[1]
42 | };
43 | }
44 | }
45 | catch (error) {
46 | }
47 | }
48 | return null;
49 | }
50 | const candidates = ['electron-prebuilt', 'electron', 'electron-prebuilt-compile'];
51 | for (let candidate of candidates) {
52 | try {
53 | return {
54 | // the path to the electron executable is exported by the module
55 | executablePath: require(candidate),
56 | version: getElectronPackageVersion(candidate)
57 | };
58 | }
59 | catch (error) {
60 | }
61 | }
62 | return null;
63 | }
64 | /**
65 | * Check if the `v8-profiler` and `v8-debug` native binaries are compatibile with the given
66 | * Electron version.
67 | *
68 | * NOTE: If the `v8-profiler` or the `v8-debug` modules aren't installed at all this function will
69 | * throw an error.
70 | *
71 | * @param Electron version, e.g. 1.3.0.
72 | * @return `true` if the native binaries are compatible.
73 | */
74 | function isInspectorCompatible(electronVersion) {
75 | // require.resolve() will throw if it fails to find the module
76 | let packageDir = path.dirname(require.resolve(path.join(__dirname, '../node_modules/v8-profiler')));
77 | let binaryFile = node_pre_gyp_1.find(path.join(packageDir, 'package.json'), { runtime: 'electron', target: electronVersion });
78 | if (!fs.existsSync(binaryFile)) {
79 | return false;
80 | }
81 | packageDir = path.dirname(require.resolve(path.join(__dirname, '../node_modules/v8-debug')));
82 | binaryFile = node_pre_gyp_1.find(path.join(packageDir, 'package.json'), { runtime: 'electron', target: electronVersion });
83 | return fs.existsSync(binaryFile);
84 | }
85 | function getNodeInspectorCmdLineArgs(options) {
86 | const args = [];
87 | if (options.debugPort != null) {
88 | args.push('-d', options.debugPort.toString());
89 | }
90 | if (options.webHost) {
91 | args.push('--web-host', options.webHost);
92 | }
93 | if (options.webPort != null) {
94 | args.push('--web-port', options.webPort.toString());
95 | }
96 | if (options.saveLiveEdit) {
97 | args.push('--save-live-edit', options.saveLiveEdit.toString());
98 | }
99 | if (options.preload === false) {
100 | args.push('--no-preload');
101 | }
102 | if (options.inject === false) {
103 | args.push('--no-inject');
104 | }
105 | if (options.hidden) {
106 | if (options.hidden instanceof Array) {
107 | options.hidden.forEach(pattern => args.push('--hidden', pattern));
108 | }
109 | else {
110 | args.push('--hidden', options.hidden);
111 | }
112 | }
113 | if (options.stackTraceLimit != null) {
114 | args.push('--stack-trace-limit', options.stackTraceLimit.toString());
115 | }
116 | if (options.sslKey) {
117 | args.push('--ssl-key', options.sslKey);
118 | }
119 | if (options.sslCert) {
120 | args.push('--ssl-cert', options.sslCert);
121 | }
122 | return args;
123 | }
124 | /**
125 | * Launch an Electron process that runs `node-inspector`.
126 | *
127 | * @param electronPath Full path to the Electron executable.
128 | */
129 | function launchInspector(electronPath, options) {
130 | const scriptPath = require.resolve('node-inspector/bin/inspector.js');
131 | const inspector = child_process.fork(scriptPath, getNodeInspectorCmdLineArgs(options), { execPath: electronPath, silent: true });
132 | inspector.on('error', (error) => console.error(error));
133 | inspector.on('message', (msg) => {
134 | if (msg.event === 'SERVER.LISTENING') {
135 | // node-inspector will print the address to the console,
136 | // so there's no need to do anything here
137 | console.info(`Visit ${msg.address.url} to start debugging.`);
138 | }
139 | else if (msg.event === 'SERVER.ERROR') {
140 | console.error(`Cannot start the server: ${msg.error.code}.`);
141 | }
142 | });
143 | inspector.on('close', (exitCode, signal) => {
144 | process.exit(exitCode);
145 | });
146 | }
147 | /**
148 | * Rebuild the native modules `node-inspector` uses to target the given Electron version.
149 | */
150 | function rebuildInspector(electronPath, electronVersion, arch) {
151 | return __awaiter(this, void 0, void 0, function* () {
152 | console.log('node-inspector binaries are incompatible or missing.');
153 | console.log('Attempting to rebuild...');
154 | let rebuild;
155 | try {
156 | rebuild = require('./rebuild').rebuild;
157 | }
158 | catch (error) {
159 | console.log('Failed to load electron-prebuilt, abandoning rebuild.');
160 | throw error;
161 | }
162 | yield rebuild(electronPath, electronVersion, arch);
163 | });
164 | }
165 | function inspect(options) {
166 | return __awaiter(this, void 0, void 0, function* () {
167 | const electron = getElectronInfo(options.electron);
168 | if (!electron) {
169 | console.log('Electron not found.');
170 | return;
171 | }
172 | if (!isInspectorCompatible(electron.version)) {
173 | if (options.autoRebuild) {
174 | yield rebuildInspector(electron.executablePath, electron.version);
175 | }
176 | else {
177 | console.warn(`Native node-inspector modules are incompatible with Electron ${electron.version}, ` +
178 | 'and auto-rebuild is disabled, node-inspector may fail to run.');
179 | }
180 | }
181 | launchInspector(electron.executablePath, options);
182 | });
183 | }
184 | exports.inspect = inspect;
185 |
--------------------------------------------------------------------------------
/lib/rebuild.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 | "use strict";
4 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5 | return new (P || (P = Promise))(function (resolve, reject) {
6 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7 | function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } }
8 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
9 | step((generator = generator.apply(thisArg, _arguments)).next());
10 | });
11 | };
12 | const path = require("path");
13 | const electron_rebuild_1 = require("electron-rebuild");
14 | /**
15 | * Rebuild any native NodeJS modules in the given `node_modules` directory to work with the
16 | * specified Electron runtime. If the Electron path and version are not specified they'll be
17 | * obtained from the `electron-prebuilt` package.
18 | */
19 | function rebuild(executablePath, version, arch) {
20 | return __awaiter(this, void 0, void 0, function* () {
21 | let modulesDir = path.join(__dirname, '../node_modules');
22 | console.log(`Rebuilding native node-inspector modules for Electron ${version}`);
23 | yield electron_rebuild_1.installNodeHeaders(version, null, null, arch);
24 | console.log(`Rebuilding in ${modulesDir}`);
25 | yield electron_rebuild_1.rebuildNativeModules(version, modulesDir, 'v8-profiler', null, arch);
26 | yield electron_rebuild_1.rebuildNativeModules(version, modulesDir, 'v8-debug', null, arch);
27 | console.log(`Rebuild complete`);
28 | });
29 | }
30 | exports.rebuild = rebuild;
31 |
--------------------------------------------------------------------------------
/npm-shrinkwrap.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-inspector",
3 | "version": "0.1.4",
4 | "dependencies": {
5 | "node-inspector": {
6 | "version": "0.12.8",
7 | "from": "enlight/node-inspector",
8 | "resolved": "git://github.com/enlight/node-inspector.git#accd46758501d8187e55b32b54e3eff65c3fdaa3"
9 | },
10 | "v8-debug": {
11 | "version": "0.7.7",
12 | "from": "v8-debug@>=0.7.1 <0.8.0",
13 | "resolved": "https://registry.npmjs.org/v8-debug/-/v8-debug-0.7.7.tgz"
14 | },
15 | "v8-profiler": {
16 | "version": "5.6.5",
17 | "from": "enlight/v8-profiler#v5.6.5-electron-v1.3",
18 | "resolved": "git://github.com/enlight/v8-profiler.git#dc3a4e15f06dd6eaca705e69e235d21d5962eaf5"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-inspector",
3 | "version": "0.1.4",
4 | "engines": {
5 | "node": ">=4.5.0"
6 | },
7 | "description": "Debugger UI for the main Electron process",
8 | "main": "lib/index.js",
9 | "bin": {
10 | "electron-inspector": "bin/electron-inspector.js"
11 | },
12 | "scripts": {
13 | "prepublish": "npm run build",
14 | "build": "tsc -p src/tsconfig.json"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/enlight/electron-inspector.git"
19 | },
20 | "keywords": [
21 | "electron",
22 | "node-inspector",
23 | "inspector",
24 | "debug",
25 | "debugger"
26 | ],
27 | "author": "Vadim Macagon ",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/enlight/electron-inspector/issues"
31 | },
32 | "homepage": "https://github.com/enlight/electron-inspector#readme",
33 | "dependencies": {
34 | "node-inspector": "git+https://github.com/enlight/node-inspector",
35 | "v8-debug": "^0.7.7",
36 | "v8-profiler": "git+https://github.com/enlight/v8-profiler.git#v5.6.5-electron-v1.3",
37 | "yargs": "^5.0.0"
38 | },
39 | "devDependencies": {
40 | "@types/node": "^6.0.38",
41 | "@types/yargs": "0.0.30",
42 | "electron": "^1.3.5",
43 | "electron-rebuild": "^1.2.1",
44 | "tslint": "^3.15.0-dev.0",
45 | "typescript": "^2.1.0-dev.20160912"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 |
4 | import * as yargs from 'yargs';
5 | import { inspect, IOptions, NODE_INSPECTOR_DEFAULTS } from './index';
6 |
7 | const argv: yargs.Argv & IOptions = yargs
8 | .usage('Usage: electron-inspector [options]')
9 | // node-inspector options
10 | .option('config', {
11 | type: 'string',
12 | describe: 'Path to file with node-inspector config information.'
13 | })
14 | .option('debug-port', {
15 | alias: 'd',
16 | type: 'number',
17 | default: NODE_INSPECTOR_DEFAULTS.debugPort,
18 | describe: 'Debug port of the Electron process you wish to debug.'
19 | })
20 | .option('web-host', {
21 | type: 'string',
22 | default: NODE_INSPECTOR_DEFAULTS.webHost,
23 | describe: "Host to listen on for node-inspector's web interface."
24 | })
25 | .option('web-port', {
26 | alias: ['p', 'port'],
27 | type: 'number',
28 | default: NODE_INSPECTOR_DEFAULTS.webPort,
29 | describe: "Port to listen on for node-inspector's web interface."
30 | })
31 | .option('save-live-edit', {
32 | type: 'boolean',
33 | default: NODE_INSPECTOR_DEFAULTS.saveLiveEdit,
34 | describe: 'Save live edit changes to disk (update the edited files).'
35 | })
36 | .option('preload', {
37 | type: 'boolean',
38 | default: NODE_INSPECTOR_DEFAULTS.preload,
39 | describe: 'Preload *.js files. You can disable this option to speed up the startup.'
40 | })
41 | .option('inject', {
42 | type: 'boolean',
43 | default: NODE_INSPECTOR_DEFAULTS.inject,
44 | description: 'Enable/disable injection of debugger extensions into the debugged process.\n' +
45 | "It's posible to disable only some of the injections using subkeys.\n" +
46 | 'Available subkeys: network, profiles, console'
47 | })
48 | .option('hidden', {
49 | type: 'string',
50 | describe: 'Array of files to hide from the UI.' +
51 | 'Breakpoints in these files will be ignored.\n' +
52 | 'All paths are interpreted as regular expressions.'
53 | })
54 | .option('stack-trace-limit', {
55 | type: 'number',
56 | default: NODE_INSPECTOR_DEFAULTS.stackTraceLimit,
57 | describe: 'Number of stack frames to show on a breakpoint.'
58 | })
59 | .option('ssl-key', {
60 | type: 'string',
61 | describe: 'A file containing a valid SSL key.'
62 | })
63 | .option('ssl-cert', {
64 | type: 'string',
65 | describe: 'A file containing a valid SSL certificate.'
66 | })
67 | // electron-inspector options
68 | .option('electron', {
69 | type: 'string',
70 | describe: 'Path to the Electron executable that should be used to run node-inspector.'
71 | })
72 | .option('auto-rebuild', {
73 | type: 'boolean',
74 | default: true,
75 | describe: 'Toggle automatic rebuild of native node-inspector modules,\n' +
76 | 'this only works if electron-rebuild is installed.'
77 | })
78 | .help()
79 | .argv;
80 |
81 | inspect(argv);
82 |
--------------------------------------------------------------------------------
/src/electron-rebuild.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for electron-rebuild 1.0.2
2 | // Project: https://github.com/electronjs/electron-rebuild/
3 | // Definitions by: Vadim Macagon
4 |
5 | declare module "electron-rebuild" {
6 | /**
7 | * Determine whether native modules need to be rebuilt (not terribly accurate right now).
8 | *
9 | * @param pathToElectronExecutable Path to the Electron executable native modules should be
10 | * rebuilt for.
11 | * @param electronVersion If omitted it will be determined from Electron executable.
12 | * @return A promise that will be resolved with `true` iff native modules need to be rebuilt.
13 | */
14 | export function shouldRebuildNativeModules(
15 | pathToElectronExecutable: string, electronVersion?: string
16 | ): Promise;
17 |
18 | /**
19 | * Download and install the NodeJS SDK for the given Electron runtime.
20 | *
21 | * @param electronVersion Electron version to download the NodeJS SDK for.
22 | * @param nodeDistUrl URL to download the NodeJS SDK from.
23 | * @param headersDir The directory to download the NodeJS SDK to.
24 | * @param arch `x64` or `ia32`
25 | * @return A promise that will be resolved when the operation completes.
26 | */
27 | export function installNodeHeaders(
28 | electronVersion: string, nodeDistUrl?: string, headersDir?: string, arch?: string
29 | ): Promise;
30 |
31 | /**
32 | * Rebuild the native modules in the given `node_modules` directory for the given Electron version.
33 | *
34 | * @param electronVersion Electron version to rebuild module for.
35 | * @param nodeModulesPath The path to the `node_modules` directory containing native modules.
36 | * @param whichModule Name of a specific module that should be rebuilt, if specified no other
37 | * modules will be rebuilt.
38 | * @param headersDir Path to the NodeJS SDK to use when rebuilding the native modules.
39 | * @return A promise that will be resolved when the operation completes.
40 | */
41 | export function rebuildNativeModules(
42 | electronVersion: string,
43 | nodeModulesPath: string,
44 | whichModule?: string,
45 | headersDir?: string,
46 | arch?: string
47 | ): Promise;
48 | }
49 |
50 | declare module "electron-rebuild/lib/node-pre-gyp-fix" {
51 | export function preGypFixRun(
52 | cwd: string, shouldRun: boolean, electronPath: string, explicitNodeVersion?: string
53 | ): Promise;
54 | }
55 |
--------------------------------------------------------------------------------
/src/electron.d.ts:
--------------------------------------------------------------------------------
1 | declare module "electron";
2 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 |
4 | import * as path from 'path';
5 | import * as fs from 'fs';
6 | import * as child_process from 'child_process';
7 | import { find as findNativeModule } from 'node-pre-gyp';
8 | import { RebuildFunction } from './rebuild';
9 |
10 | // default values of various `node-inspector` options
11 | export const NODE_INSPECTOR_DEFAULTS = {
12 | debugPort: 5858,
13 | webHost: '0.0.0.0',
14 | webPort: 8080,
15 | saveLiveEdit: false,
16 | preload: true,
17 | inject: true,
18 | stackTraceLimit: 50
19 | }
20 |
21 | function getElectronPackageVersion(moduleName: string): string {
22 | // try to grab the version from the electron-prebuilt package if it's installed
23 | const packageText = fs.readFileSync(require.resolve(`${moduleName}/package.json`), 'utf8');
24 | const packageJson = JSON.parse(packageText);
25 | return packageJson.version;
26 | }
27 |
28 | const electronVersionRegex = /^v(\d{1,2}\.\d{1,2}\.\d{1,2})$/;
29 |
30 | /**
31 | * Obtain the path of the Electron executable and its version.
32 | *
33 | * @param exePath Path of an Electron executable, if omitted an attempt will be made to find it
34 | * by looking for `electron`, `electron-prebuilt`, or `electron-prebuilt-compile`.
35 | * @return `null` if an Electron executable wasn't found, or its version couldn't be determined.
36 | */
37 | function getElectronInfo(exePath?: string): { executablePath: string, version: string } | null {
38 | // if a path to the electron executable was provided run it to figure out its version
39 | if (exePath) {
40 | if (fs.existsSync(exePath)) {
41 | try {
42 | const stdout = child_process.execFileSync(exePath, ['--version'], { encoding: 'utf8' });
43 | const version = stdout.replace(/[\r\n]/g, '');
44 | const match = electronVersionRegex.exec(version);
45 | if (match) {
46 | return {
47 | executablePath: exePath,
48 | version: match[1]
49 | };
50 | }
51 | } catch (error) {
52 | // noop
53 | }
54 | }
55 | return null;
56 | }
57 |
58 | const candidates = ['electron-prebuilt', 'electron', 'electron-prebuilt-compile'];
59 | for (let candidate of candidates) {
60 | try {
61 | return {
62 | // the path to the electron executable is exported by the module
63 | executablePath: require(candidate),
64 | version: getElectronPackageVersion(candidate)
65 | };
66 | } catch (error) {
67 | // noop
68 | }
69 | }
70 | return null;
71 | }
72 |
73 | /**
74 | * Check if the `v8-profiler` and `v8-debug` native binaries are compatibile with the given
75 | * Electron version.
76 | *
77 | * NOTE: If the `v8-profiler` or the `v8-debug` modules aren't installed at all this function will
78 | * throw an error.
79 | *
80 | * @param Electron version, e.g. 1.3.0.
81 | * @return `true` if the native binaries are compatible.
82 | */
83 | function isInspectorCompatible(electronVersion: string): boolean {
84 | // require.resolve() will throw if it fails to find the module
85 | let packageDir = path.dirname(
86 | require.resolve(path.join(__dirname, '../node_modules/v8-profiler'))
87 | );
88 | let binaryFile = findNativeModule(
89 | path.join(packageDir, 'package.json'),
90 | { runtime: 'electron', target: electronVersion }
91 | );
92 | if (!fs.existsSync(binaryFile)) {
93 | return false;
94 | }
95 |
96 | packageDir = path.dirname(require.resolve(path.join(__dirname, '../node_modules/v8-debug')));
97 | binaryFile = findNativeModule(
98 | path.join(packageDir, 'package.json'),
99 | { runtime: 'electron', target: electronVersion }
100 | );
101 | return fs.existsSync(binaryFile);
102 | }
103 |
104 | export interface INodeInspectorOptions {
105 | config?: string;
106 | debugPort?: number;
107 | webHost?: string;
108 | webPort?: number;
109 | saveLiveEdit?: boolean;
110 | preload?: boolean;
111 | inject?: boolean; // FIXME: can also be an object
112 | hidden?: string | string[];
113 | stackTraceLimit?: number;
114 | sslKey?: string;
115 | sslCert?: string;
116 | }
117 |
118 | /**
119 | * Build an array of command line args that will be passed to `node-inspector`.
120 | *
121 | * Only options whose value differs from the default will be added to the
122 | * argument list, this makes it possible to override `node-inspector` options
123 | * via a config file.
124 | */
125 | function getNodeInspectorCmdLineArgs(options: INodeInspectorOptions): string[] {
126 | const args: string[] = [];
127 | if (options.config) {
128 | args.push('--config', options.config);
129 | }
130 | if ((options.debugPort != null) && (options.debugPort !== NODE_INSPECTOR_DEFAULTS.debugPort)) {
131 | args.push('-d', options.debugPort.toString());
132 | }
133 | if (options.webHost && (options.webHost !== NODE_INSPECTOR_DEFAULTS.webHost)) {
134 | args.push('--web-host', options.webHost);
135 | }
136 | if ((options.webPort != null) && (options.webPort !== NODE_INSPECTOR_DEFAULTS.webPort)) {
137 | args.push('--web-port', options.webPort.toString());
138 | }
139 | if (options.saveLiveEdit) {
140 | args.push('--save-live-edit', options.saveLiveEdit.toString())
141 | }
142 | if (options.preload === false) {
143 | args.push('--no-preload');
144 | }
145 | if (options.inject === false) {
146 | args.push('--no-inject');
147 | }
148 | if (options.hidden) {
149 | if (options.hidden instanceof Array) {
150 | options.hidden.forEach(pattern => args.push('--hidden', pattern));
151 | } else {
152 | args.push('--hidden', options.hidden);
153 | }
154 | }
155 | if ((options.stackTraceLimit != null) &&
156 | (options.stackTraceLimit !== NODE_INSPECTOR_DEFAULTS.stackTraceLimit)) {
157 | args.push('--stack-trace-limit', options.stackTraceLimit.toString());
158 | }
159 | if (options.sslKey) {
160 | args.push('--ssl-key', options.sslKey);
161 | }
162 | if (options.sslCert) {
163 | args.push('--ssl-cert', options.sslCert);
164 | }
165 | return args;
166 | }
167 |
168 | /**
169 | * Launch an Electron process that runs `node-inspector`.
170 | *
171 | * @param electronPath Full path to the Electron executable.
172 | */
173 | function launchInspector(electronPath: string, options: INodeInspectorOptions): void {
174 | const scriptPath = require.resolve('node-inspector/bin/inspector.js');
175 | const inspector = child_process.fork(
176 | scriptPath, getNodeInspectorCmdLineArgs(options),
177 | { execPath: electronPath, silent: true }
178 | );
179 |
180 | inspector.on('error', (error: Error) => console.error(error));
181 |
182 | inspector.on('message', (msg: any) => {
183 | if (msg.event === 'SERVER.LISTENING') {
184 | // node-inspector will print the address to the console,
185 | // so there's no need to do anything here
186 | console.info(`Visit ${msg.address.url} to start debugging.`);
187 | } else if (msg.event === 'SERVER.ERROR') {
188 | console.error(`Cannot start the server: ${msg.error.code}.`);
189 | }
190 | });
191 |
192 | inspector.on('close', (exitCode: number, signal: string) => {
193 | process.exit(exitCode);
194 | });
195 | }
196 |
197 | /**
198 | * Rebuild the native modules `node-inspector` uses to target the given Electron version.
199 | */
200 | async function rebuildInspector(
201 | electronPath: string, electronVersion: string, arch?: string
202 | ): Promise {
203 | console.log('node-inspector binaries are incompatible or missing.');
204 | console.log('Attempting to rebuild...');
205 | let rebuild: RebuildFunction;
206 | try {
207 | rebuild = require('./rebuild').rebuild;
208 | } catch (error) {
209 | console.log('Failed to load electron-prebuilt, abandoning rebuild.');
210 | throw error;
211 | }
212 | await rebuild(electronPath, electronVersion, arch);
213 | }
214 |
215 | export interface IOptions extends INodeInspectorOptions {
216 | /** Path to Electron executable. */
217 | electron?: string;
218 | autoRebuild: boolean;
219 | }
220 |
221 | export async function inspect(options: IOptions): Promise {
222 | const electron = getElectronInfo(options.electron);
223 | if (!electron) {
224 | console.log('Electron not found.');
225 | return;
226 | }
227 |
228 | if (!isInspectorCompatible(electron.version)) {
229 | if (options.autoRebuild) {
230 | await rebuildInspector(electron.executablePath, electron.version);
231 | } else {
232 | console.warn(
233 | `Native node-inspector modules are incompatible with Electron ${electron.version}, `+
234 | 'and auto-rebuild is disabled, node-inspector may fail to run.'
235 | );
236 | }
237 | }
238 |
239 | launchInspector(electron.executablePath, options);
240 | }
241 |
--------------------------------------------------------------------------------
/src/node-pre-gyp.d.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 |
4 | declare module "node-pre-gyp" {
5 | export interface FindOptions {
6 | /** Defaults to the directory containing the package file. */
7 | module_root?: string;
8 | /** If not specified the runtime will be determined from the current process. */
9 | runtime?: string;
10 | /** Version of the runtime, if not specified will be determined from the current process. */
11 | target?: string;
12 | /** Defaults to `process.platform`. */
13 | target_platform?: string;
14 | /** Defaults to `process.arch`. */
15 | target_arch?: string;
16 | /**
17 | * If `true` the search algo will look for the debug binary, otherwise it will look for the
18 | * release binary. Defaults to `false`.
19 | */
20 | debug?: boolean;
21 | }
22 |
23 | /**
24 | * Finds a native module binary in the given package.
25 | *
26 | * @param packageFile Path to the `package.json` of the package to search.
27 | * @return A path that can be used to require the `.node` binary.
28 | */
29 | export function find(packageFile: string, options: FindOptions): string;
30 | }
31 |
--------------------------------------------------------------------------------
/src/rebuild.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Vadim Macagon
2 | // MIT License, see LICENSE file for full terms.
3 |
4 | import * as path from 'path';
5 | import { installNodeHeaders, rebuildNativeModules } from 'electron-rebuild';
6 | import { preGypFixRun } from 'electron-rebuild/lib/node-pre-gyp-fix';
7 |
8 | /**
9 | * Rebuild any native NodeJS modules in the given `node_modules` directory to work with the
10 | * specified Electron runtime. If the Electron path and version are not specified they'll be
11 | * obtained from the `electron-prebuilt` package.
12 | */
13 | export async function rebuild(
14 | executablePath: string, version: string, arch: string
15 | ): Promise {
16 | let modulesDir = path.join(__dirname, '../node_modules');
17 | console.log(`Rebuilding native node-inspector modules for Electron ${version}`);
18 | await installNodeHeaders(version, null, null, arch);
19 | console.log(`Rebuilding ${modulesDir}/v8-profiler`);
20 | await rebuildNativeModules(version, modulesDir, 'v8-profiler', null, arch);
21 | // `node-inspector` will be launched in a "run as node" Electron process,
22 | // so `node-pre-gyp` will be looking for a `node-vXX-platform-arch` directory
23 | await preGypFixRun(path.join(modulesDir, 'v8-profiler'), true, executablePath);
24 | console.log(`Rebuilding ${modulesDir}/v8-debug`);
25 | await rebuildNativeModules(version, modulesDir, 'v8-debug', null, arch);
26 | await preGypFixRun(path.join(modulesDir, 'v8-debug'), true, executablePath);
27 | console.log(`Done.`);
28 | }
29 |
30 | // Export the type of the function so that it can be referenced without actually requiring this
31 | // module (which is useful for conditional imports in TypeScript).
32 | export type RebuildFunction = typeof rebuild;
33 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es5",
5 | "noImplicitAny": true,
6 | "sourceMap": false,
7 | "lib": [
8 | "es5",
9 | "dom",
10 | "es2015.promise"
11 | ],
12 | "outDir": "../lib"
13 | },
14 | "files": [
15 | "electron-rebuild.d.ts",
16 | "electron.d.ts",
17 | "node-pre-gyp.d.ts",
18 | "index.ts",
19 | "rebuild.ts",
20 | "cli.ts"
21 | ]
22 | }
--------------------------------------------------------------------------------