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.