← Home

Serve CSS and Javascript even faster: improved Rails bundled_assets plugin

JS compression: up to 10 times faster!

Witold pointed me to the fact that the Javascript compression through JSMin might take a significant amount of time. On my own machine that's:


$ time ruby vendor/plugins/bundled_assets/lib/jsmin.rb <tmp/in.js > tmp/out.js
real    0m2.851s
user    0m2.813s
sys     0m0.017s

... with in.js being ~ 250kb large. 250kb of Javascript definitely aren't that unusual these days and Witold reported even worse execution times.

This may or may not be a problem for your app in production mode. In most cases it won't be a problem, I guess, because Rails' page caching mechanism makes sure that this lag will only occur once immediately after you've published changes to your Javascript and cleared your public/bundles cache directory. On the other hand on high-performance sites every bit of performance may count and you probably don't want to wonder a lot about this bit.

Furthermore, depending on how large your Javascript files are and how snappy you expect your application to be in development mode you might definitely want to do something about this: Rails' page caching mechanism won't cache anything when in development mode (which of course is the right thing to do) ... and that means for our bundles that they will be re-created with every single request. So that lag occurs with every page request that includes our Javascript bundle, too.

Now, there are two general approaches here to speed things up a bit, of course:

  • completely turn of Javascript compression
  • use a faster implementation of the compressor

Turning stuff off through a configuration option is done easily, but how about a faster implementation? Good news is that there's a C version of JSMin. It will compress the same Javascript libraries from the benchmark above in the blink of an eye:


$ time vendor/plugins/bundled_assets/lib/jsmin <tmp/in.js > tmp/out.js
real    0m0.030s
user    0m0.023s
sys     0m0.007s

So this should work well even in development mode. On the other hand you might reportedly have problems to get this thing compiled, deployed or running (usually it's very easy!) depending on your provider or type of webspace.

Thus, you can now choose between these both options and customize the plugin to your needs. See below for the configuration options.

Keep CSS comments for your scary CSS hacks

If you are using some scary CSS hacks that rely on CSS comments the regular expressions that are used for CSS compression will probably prove a bit too greedy for you:

@content.gsub!(/\/\*(.*?)\*\/ /, "")

So far you had to comment out that line of code. Now you can conveniently use a config option here, too. See below.

New config options for your asset bundles

You can specify any of the following config options e.g. at the end of your environment.rb or environments/development.rb.

To completely turn compression for CSS and Javascript on/off use this:

AssetsBundle.options[:compress] = [:css, :js]

(Valid values are: false, [], :css, :js, [:css], [:js], [:css, :js].)

Also, you can provide a path to a Javascript compressor that will be used by the plugin. E.g. instead of the default setting which points the Ruby interpreter to the Ruby implementation of JSMin you might want to use:


AssetsBundle.options[:jsmin] = %W(#{RAILS_ROOT}/vendor/plugins/bundled_assets/lib/jsmin)

This way the plugin would use a compiled C version of JSMin placed in the lib directory. Of course you can specify whatever path might work for you.

To turn off stripping comments from CSS you can say:

AssetsBundle.options[:css_keep_comments] = true

I've also made the CSS and Javascript source paths configurable. You can specify them like this:


AssetsBundle.options[:paths][:css] = '/public/stylesheets/my_css'
AssetsBundle.options[:paths][:js] = '/public/stylesheets/my_js'

Like described in the inital post about this article you can also specify hole subdirectories to include all CSS/Javascript files that are found therein. So together with these config options this should prove flexible enough, I guess.

A nasty typo in the Ruby JSMin implementation

It's really only a typo but it's a potentially nasty one. You might have noticed it in the Ruby JSMin implementation yourself if your Javascript code contains lines that aren't terminated by a semicolon (which of course is perfectly legal in Javascript). In this case you might have gotten Javascript syntax errors.

I've patched the version of JSMin that comes with the Rails plugin, the patch has been added to the repository and Douglas Crockford has been notified.

Where is it?

Like before you can check this stuff out from here:

http://svn.artweb-design.de/stuff/rails/bundled_assets.

Feedback?

What do you think? Suggestions? Improvements? Problems?