Skip to content

Commit

Permalink
bam!
Browse files Browse the repository at this point in the history
  • Loading branch information
mattdesl committed Jan 19, 2016
0 parents commit 8789bc9
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bower_components
node_modules
*.log
.DS_Store
bundle.js
10 changes: 10 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
bower_components
node_modules
*.log
.DS_Store
bundle.js
test
test.js
demo/
.npmignore
LICENSE.md
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Jam3

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.

52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# devtool

[![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges)

### WORK IN PROGRESS

Documentation is currently being written for this module. Check back soon!

---

Runs Node.js source code through Chromium DevTools (using Electron).

This allows you to profile, debug and develop typical Node.js programs with some of the features of Chrome DevTools.

## Example

For example, we can use this to profile and debug [browserify](https://github.com/substack/node-browserify), a node program that would not typically work within the browser's DevTools.

```js
var browserify = require('browserify');

// Start DevTools profiling...
console.profile('build');

browserify('client.js').bundle(function (err, src) {
if (err) throw err;

// Finish DevTools profiling...
console.profileEnd('build');
});
```

Here are some screenshots after the profile has run, and also during debugging of a hot code path.

## Features

This builds on Electron, providing some additional features:

- Improved error handling (more detailed syntax errors in console)
- Improved source map support for required files
- Makes various Node features behave as expected, like `require.main` and `process.argv`
- Console redirection back to terminal (optional)
- File watching for development and quit-on-error flags for unit testing (e.g. continuous integration)
- Exit error codes

## Usage

[![NPM](https://nodei.co/npm/devtool.png)](https://www.npmjs.com/package/devtool)

## License

MIT, see [LICENSE.md](http://github.com/Jam3/devtool/blob/master/LICENSE.md) for details.
14 changes: 14 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env node
const spawn = require('child_process').spawn;
const electron = require('electron-prebuilt');
const path = require('path');
const serverPath = path.join(__dirname, '../server.js');

var args = [ serverPath ].concat([].concat(process.argv).splice(2));

var proc = spawn(electron, args);
proc.stdout.pipe(process.stdout);
proc.stderr.pipe(process.stderr);
proc.on('close', function (code) {
process.exit(code);
});
17 changes: 17 additions & 0 deletions example/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// An example profiling browserify source
var browserify = require('browserify');
var path = require('path');

function run () {
console.profile('build');
browserify(path.resolve(__dirname, 'client.js'))
.bundle(function (err, src) {
if (err) throw err;
console.profileEnd('build');
console.log('bundle size in bytes:', src.length);
});
}

if (require.main === module) {
run();
}
2 changes: 2 additions & 0 deletions example/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var url = require('url');
console.log(url.parse(window.location.href));
43 changes: 43 additions & 0 deletions lib/file-watch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// a thin wrapper around chokidar file watching HTML / CSS
var chokidar = require('chokidar');
var assign = require('object-assign');
var EventEmitter = require('events').EventEmitter;

var ignores = [
'node_modules/**', 'bower_components/**',
'.git', '.hg', '.svn', '.DS_Store',
'*.swp', 'thumbs.db', 'desktop.ini'
];

module.exports = function fileWatch (glob, opt) {
opt = assign({
ignored: ignores,
ignoreInitial: true
}, opt);

if (opt.poll) {
opt.usePolling = true;
}

var emitter = new EventEmitter();
var closed = false;
var ready = false;

var watcher = chokidar.watch(glob, opt);
watcher.on('change', function (file) {
emitter.emit('change', file);
});

// [email protected] only allows close after ready event
watcher.once('ready', function () {
ready = true;
if (closed) watcher.close();
});

emitter.close = function () {
if (closed) return;
if (ready) watcher.close();
closed = true;
};
return emitter;
};
9 changes: 9 additions & 0 deletions lib/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>devtool</title>
</head>
<body>
</body>
</html>
61 changes: 61 additions & 0 deletions lib/preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
(function () {
var electron = require('electron');
var path = require('path');
var serialize = require('serializerr');
var remote = electron.remote;
var requireHook = require('./require-hook');

var ipc = electron.ipcRenderer;
var _process = remote.process;
var cwd = _process.cwd();

// setup renderer process to look a bit more like node
process.chdir(cwd);
process.argv = _process.argv;
process.exit = _process.exit.bind(_process);

// if we should pipe DevTools console back to terminal
if (remote.getGlobal('__electronConsoleHook')) {
require('console-redirect/process');
}

// in DevTools console (i.e. REPL), these will be
// undefined to mimic Node REPL
delete global.__dirname;
delete global.__filename;

// When there is an uncaught exception in the entry
// script, we may want to quit the devtool (i.e. for CI)
// or just print an error in DevTools console (i.e. for dev)
var shouldQuit = remote.getGlobal('__shouldElectronQuitOnError');
if (shouldQuit) {
window.onerror = function (a, b, c, d, err) {
fatalError(err);
return true;
};
}

// get an absolute path to our entry point
var entry = remote.getGlobal('__electronEntryFile');
entry = path.isAbsolute(entry) ? entry : path.resolve(cwd, entry);

// hook into the internal require for a few features:
// - better error reporting on syntax errors and missing modules
// - require.main acts like node.js CLI
// - add source maps so the files show up in DevTools Sources
requireHook({
entry: entry,
basedir: cwd
}, function (err) {
if (err && shouldQuit) {
fatalError(err);
}
});

// boot up entry application
require(entry);

function fatalError (err) {
ipc.send('error', JSON.stringify(serialize(err)));
}
})();
62 changes: 62 additions & 0 deletions lib/require-hook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
var path = require('path');
var noop = function () {};

module.exports = function requireHook (opts, cb) {
if (typeof opts === 'function') {
cb = opts;
opts = {};
}
opts = opts || {};
cb = cb || noop;

var remote = require('electron').remote;
var Module = require('module');
var syntaxError = require('syntax-error');
var fs = remote.require('fs');
var stripBOM = require('strip-bom');
var combineSourceMap = require('combine-source-map');
var entry = opts.entry;
// var basedir = opts.basedir || remote.process.cwd();

var hasSetMain = false;
var currentWrapFile = null;

require.extensions['.js'] = function devtoolCompileModule (module, file) {
// set the main module so that Node.js scripts run correctly
if (!hasSetMain && entry && file === entry) {
hasSetMain = true;
process.mainModule = module;
}

var code = fs.readFileSync(file, 'utf8');
try {
currentWrapFile = file;
module._compile(stripBOM(code), file);
cb(null);
} catch (err) {
// improve Electron's error handling (i.e. SyntaxError)
var realErr = syntaxError(code, file) || err;
var msg = 'Error compiling module: ' + file + '\n' + (realErr.annotated || realErr.message);
console.error(msg);
cb(new Error(msg));
}
};

// Include source maps for required modules
var wrap = Module.wrap;
Module.wrap = function (script) {
var wrapScript = wrap.call(wrap, script);
if (!currentWrapFile) return wrapScript;
var baseFileDir = path.dirname(entry);
// TODO: Use path.dirname(entry) or opts.basedir ?
var sourceFile = path.relative(baseFileDir, currentWrapFile)
.replace(/\\/g, '/');
var sourceMap = combineSourceMap.create().addFile(
{ sourceFile: sourceFile, source: script },
{ line: 0 });
return [
combineSourceMap.removeComments(wrapScript),
sourceMap.comment()
].join('\n');
};
};
42 changes: 42 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "devtool",
"version": "1.0.0",
"description": "",
"main": "index.js",
"license": "MIT",
"author": {
"name": "Matt DesLauriers",
"email": "[email protected]",
"url": "https://github.com/mattdesl"
},
"dependencies": {
"chokidar": "^1.4.2",
"combine-source-map": "^0.7.1",
"console-redirect": "^1.0.0",
"electron-prebuilt": "^0.35.6",
"events": "^1.1.0",
"minimist": "^1.2.0",
"object-assign": "^4.0.1",
"serializerr": "^1.0.2",
"strip-bom": "^2.0.0",
"syntax-error": "^1.1.4"
},
"devDependencies": {
"browserify": "^13.0.0"
},
"scripts": {
"test": "electron server.js"
},
"keywords": [],
"repository": {
"type": "git",
"url": "git://github.com/Jam3/devtool.git"
},
"homepage": "https://github.com/Jam3/devtool",
"bugs": {
"url": "https://github.com/Jam3/devtool/issues"
},
"bin": {
"devtool": "./bin/index.js"
}
}
Loading

0 comments on commit 8789bc9

Please sign in to comment.