javascript AST trickery with burrito [ 2011-07-07 05:58:29 UTC ]

Uglify is a pretty nifty javascript minifier but it also has the lesser-known ability to generate and walk the AST.

Unfortunately, uglify's traverser is a bit cumbersome to use so I wrote a library using my own traverser to make it easy!

Introducing burrito, a crazy new library to make AST traversals and manipulations crazy easy.

Given this snippet of code:

f() && g(h()); foo();

It's super easy to wrap all the function calls in another function call, qqq():

var burrito = require('burrito'); var src = burrito('f() && g(h())\nfoo()', function (node) { if (node.name === 'call') node.wrap('qqq(%s)'); }); console.log(src);

$ node wrap.js qqq(f()) && qqq(g(qqq(h()))); qqq(foo());

Or maybe dive in and surgically replace that && with a ||, how about!

var burrito = require('burrito'); var src = burrito('f() && g(h())\nfoo()', function (node) { if (node.name === 'binary') node.wrap('%a || %b'); }); console.log(src);

$ node wrap.js f() || g(h()); foo();

Plus you can .microwave() your burrito too, which evals the code using node's vm.runInNewContext() (which even runs in the browser thanks to browserify).

var burrito = require('burrito'); var res = burrito.microwave('Math.sin(2)', function (node) { if (node.name === 'num') node.wrap('Math.PI / %s'); }); console.log(res);

This snippet takes the expression Math.sin(2) and replaces the 2 with Math.PI / 2 thanks to some %s interpolation. And since Math.sin(Math.PI / 2) evaluates at 1, the code should print as much...

$ node microwave.js 1

And it does! Hooray!

This craziness makes an amazing number of things possible, like stack traces that work in all the browsers without relying on a stack trace API or code coverage tools without any binary extensions!

Give burrito a spin!

npm install burrito

or get the code on github!


singsong at music hack day [ 2011-05-11 18:02:13 UTC ]

Just off the plane from the first ever nodeconf where I gave a talk about dnode culminating in a live dnode-powered markov rap battle, I headed down to Music Hack Day, San Francisco with Marak to represent node.js.

Music Hack Day is a 24 hour hacking contest for musical hacks. I got in late but stayed the whole night and managed to hack up singsong, interface on top of festival's little-known singing voice synthesizer plugin. You can click on the staff to plop notes down and then use the text boxes below to associate bits of text with them.

Sample output: [ ogg ] | [ wav ]

It's not quite ideal since you've got to break up the words by syllables yourself. I wrote a lib that can do this automatically but didn't hack it in. Good enough for 24 hours at least. Check it out on github.


browserify: browser-side require() for your node.js [ 2011-03-29 00:03:52 UTC ]

Announcing browserify, a node.js module that bundles up all your javascript into a single file so you can use node.js-style require() from the browser.

Check this:

server.js

var connect = require('connect'); var server = connect.createServer(); server.use(connect.static(__dirname)); server.use(require('browserify')(__dirname + '/js')); server.listen(9797);

js/foo.js

var bar = require('./bar'); module.exports = function (x) { return x * bar.coeff(x); };

js/bar.js

exports.coeff = function (x) { return Math.log(x) / Math.log(2) + 1; };

index.html

<html> <head> <script type="text/javascript" src="/browserify.js"></script> <script type="text/javascript"> var foo = require('./foo'); window.onload = function () { document.getElementById('result').innerHTML = foo(100); }; </script> </head> <body> foo = <span id="result"></span> </body> </html>

Then fire up the server (node server.js) and load up http://localhost:9797/

Amazing! Browserify just hosted all of those files at /browserify.js and made require() work just like in node.js!

But wait! There's more! You can specify npm modules to use and then you can require() those too! Just pass the "require" parameter to browserify:

server.js

var connect = require('connect'); var server = connect.createServer(); server.use(connect.static(__dirname)); server.use(require('browserify')({ require : [ 'traverse' ] })); server.listen(9797);

Then spew out some HTML:

index.html

<html> <head> <script type="text/javascript" src="/browserify.js"></script> <script type="text/javascript"> var Traverse = require('traverse'); window.onload = function () { var obj = { a : 1, b : [ 2, 3, 4 ], c : [ { d : 5, e : 6 } ] }; var leaves = Traverse(obj).reduce(function (acc, x) { if (this.isLeaf) acc.push(x); return acc; }, []) ; document.getElementById('result').innerHTML = leaves.join(', '); }; </script> </head> <body> <span id="result"></span> </body> </html>

Then fire up the server and check http://localhost:9797/

Success! It pulled out the leaves from that pesky nested data structure using traverse! If the npm modules have dependencies, those dependencies will get bundled along recursively!

Plus, browserify looks for the "browserify" field in package.json files up on npm, so you can serve up custom browser versions of packages. Here's the "browserify" part of dnode's package.json:

"browserify" : { "name" : "dnode", "main" : "./browser/index.js", "base" : "./browser", "require" : [ "dnode-protocol" ] },

With this field in the package.json and "dnode" in the "require" list, require('dnode') serves up the browser-side dnode code!

Here are some packages that have been tested with browserify:

And that's not all!

What an amazing amount of features! Check it out on github!

Want to make sure your browserified code runs on all the browsers? Check out my startup, browserling! Cross-browser testing from your browser!


slides from my dnode talk at parisoma [ 2011-03-23 05:27:02 UTC ]

I gave a talk at parisoma in San Francisco about dnode, my asynchronous RPC system that we use at my startup, browserling.

Check out the slides or the LaTeX source used to generate the slides below:

Fuck yeah, callbacks!


Graph of node.js package dependencies [ 2011-01-29 18:21:50 UTC ]

The node.js community has been growing like crazy with 995 packages on npm at the time of this writing.

$ npm --loglevel=silent ls latest | wc -l 995

For fun I graphed the dependencies between npm modules with graphviz. Here are the results with the fdp and twopi graphing algorithms:

Check out the code on github Or npm install npmdep!