In one of my apps, I was including a few libraries, and the resulting code was getting rather large and hard to manage. I also couldn't get it to work when the files were separate, so I was working with them all in the same file. Having some experience with webapps, I knew exactly what to do: uglify my code.

UglifyJS, that is. Specifically, UglifyJS2. I modified my wscript to combine all my javascript files into a single one before creating the pebble bundle using UglifyJS2. Actually, once you have UglifyJS2 installed, the wscript change is pretty simple.

This simple function will compile your javascript into a tiny file:

def compile_js ( ctx , in_js_files ): # Concatenate all JS files into pebble-js-app.js in_js_nodes = ctx . path . ant_glob ( in_js_files , excl = 'src/js/pebble-js-app.js' ) out_js_node = ctx . path . make_node ( 'src/js/pebble-js-app.js' ) # Cleanup js and comments uglifyjs = 'uglifyjs' comp_opts = ',' . join ([ 'drop_console' , 'pure_getters' , 'warnings' , 'cascade' , 'join_vars' , 'dead_code' , 'sequences' , 'properties' , 'loops' , 'unused' , "pure_funcs=['DMath.sin','DMath.cos']" , ]) defines = "" options = '--screw-ie8 -c %s --lint -m toplevel %s ' % ( comp_opts , defines ) inputs = ' ' . join ( node . relpath () for node in in_js_nodes ) ret = ctx . exec_command ( ' %s %s %s > %s ' % ( uglifyjs , options , inputs , out_js_node . relpath ())) if ret != 0 : ctx . fatal ( 'uglifyjs failed' ) return out_js_node

To use it, replace the last two lines in your normal wscript file:

ctx . pbl_bundle ( elf = 'pebble-app.elf' , js = ctx . path . ant_glob ( 'src/js/**/*.js' ))

With these:

js_node = compile_js ( ctx , 'src/js/**/*.js' ) ctx . pbl_bundle ( elf = 'pebble-app.elf' , js = js_node )

This will provide your users a slightly better experience; uglifyjs removes any unnecessary code (like console logging via the drop_console option) speeding up your code and since most javascript engines store a copy of a function's source in memory, your javascript will be that much smaller and faster.

For my particular case, I had a library that had lots of comments, so my final js file went from 19K to less than 8K, a saving of almost 60%! While this isn't a major performance change, I admit, it is one of many steps to giving your users a great experience.

An Exaltation of the User Experience

Remember, while the javascript doesn't run on the Pebble, it does run on devices that actually have worse battery lives. I have to charge my phone daily, but my Pebble can go at least 5 days without a charge. If a Pebble app decides to start pinging the Internet constantly, my phone will die way before my Pebble.

Extra Options with UglifyJS2

In my code block above, you may have noticed a few different things, like the uglifyjs variable and the empty defines variable. I don't actually use that block exactly, I have some customizations.

uglifyjs

I actually don't have uglifyjs2 installed to my system, I have it checked out along with its dependencies into a nodejs-src folder. That's amdefine, async, minimist, node-optimist, node-wordwrap, source-map, and UglifyJS2, each with a symlink from a nodejs folder to the various source files/folders:

amdefine.js -> ../nodejs-src/amdefine/amdefine.js async.js -> ../nodejs-src/async/lib/async.js minimist.js -> ../nodejs-src/minimist/index.js optimist.js -> ../nodejs-src/node-optimist/index.js source-map -> ../nodejs-src/source-map/lib/source-map source-map.js -> ../nodejs-src/source-map/lib/source-map.js wordwrap.js -> ../nodejs-src/node-wordwrap/index.js

Then I have uglifyjs set to:

NODE_PATH=/path/to/pebble/app/nodejs/ ./nodejs-src/UglifyJS2/bin/uglifyjs

I copied this structure from a friend in college (initially with Python and using PYTHONPATH), and I think it works pretty well.

UglifyJS2 Options

There are quite a few options possible, I only set a few. For creating a pbw to upload to the app store, I stick with the ones I wrote above, but for development sometimes I add -b to beautify the output, which just adds whitespace by default. I can also normally don't remove console logging, but that's a compressor options.

Compressor Options

comp_opts is my current preference for compressor options, but they're not set in stone. You can add/remove anything from that list according to the readme of UglifyJS2.

Defines

Defines are great, but the one thing I like the most is the ability to pull another file into javascript. I can, since wscript is python, grab something from appinfo.json, or I can read authentication tokens into variables. I do the latter with my app's SetPebble token, which I have in .gitignore since I don't want to accidentally ship that with any open source apps. You can define a DEBUG variable as true or false to conditionally compile, much like the UglifyJS2 docs mention.

Here's my actual defines variable:

defines = "--define SET_PEBBLE_TOKEN=' %s '" % ( ctx . path . find_node ( 'setpebble.token' ) . read () . strip ())

Conclusion

With UglifyJS2 in your build process, you get plenty of benefits, including a slightly better user experence to give you an edge over the next guy writing a competing app. Also, just in case it seems otherwise, I have no affiliation with UgilfyJS2. I just found it when I was doing research for a previous job and liked how well it did its job.