Skip to content

Instantly share code, notes, and snippets.

@toolmantim
Created July 26, 2012 19:39
Show Gist options
  • Save toolmantim/3184063 to your computer and use it in GitHub Desktop.
Save toolmantim/3184063 to your computer and use it in GitHub Desktop.
Minification with newlines using the Rails 3 asset pipeline

Retaining line numbers with the Rails asset pipeline

By default the Rails 3 asset pipeline uses the Uglifier gem to optimize and minify your Javascript. One of its many optimisations is to remove all whitespace, turning your Javascript into one very long line of code.

Whist removing all the newlines helps to reduce the file size, it has the disadvantage of making your Javascript harder to debug. If you've tried to track down Javascript errors in minified Javascript files you'll know the lack of whitespace does make life harder.

Luckily there is a simple solution: to configure Uglifier to add newlines back into the code after it performs its optimisations. And if you're serving your files correctly gzip'd, the newlines add only a small increase to the final file size.

You can configure Uglifier to add the newlines by setting the following in your Rails 3 config/production.rb file:

# Ensure this only runs in the :assets bundler group
if defined? Uglifier
  config.assets.js_compressor = Uglifier.new(
    # Add new lines
    :beautify => true,
    # Don't add indentation
    :beautify_options => {:indent_level => 0}
  )
end

You'll notice we have to tell Uglifier not to indent the code. By default it reindents the entire source using 4 spaces per indent, which can add many more bytes than simply adding newlines. And though indentation does make it more readable, it won't really make your life easier when tracking down production errors, and as you'll see below is probably not worth the cost.

YUI Compressor can also be configured to do this by specifying --line-break 0.

Google Closure Compiler unfortunately has no such option.

Affect on file size

Below are comparisons of the different Uglifier options against a fairly large Rails application consisting of 142 Javascript and Coffeescript assets.

For this application the assets:precompile task generates a single application.js file, as well as a corresponding application.js.gz file. We use the .gz version to compare file sizes, as this represents the final download size that the end-user will experience (that is, assuming your server is set up correctly).

No compression

First we find the baseline file size by disabling Uglifier optimizations altogether:

config.assets.compress = false

The resulting application.js.gz size: 244KB

Rails default

If left to its own devices, Uglifier removes all the whitespace. This is the default Rails configuration, and results in the smallest file size, but with all your code on a single line.

The resulting application.js.gz size: 140KB

With newlines

Telling Uglifier to add only newlines:

if defined? Uglifier
  config.assets.js_compressor = Uglifier.new(
    :beautify => true,
    :beautify_options => {:indent_level => 0}
  )
end

The resulting application.js.gz size: 148KB

The cost of adding the newlines was 8KB which, considering the benefits, is probably worth it.

What about tabs too?

If you wanted really pretty code you can omit the :indent_level option and let Uglifier reindent the code using 4 spaces per indent:

if defined? Uglifier
  config.assets.js_compressor = Uglifier.new(
    :beautify => true
  )
end

The resulting application.js.gz size: 159 KB

The cost of retaining newlines /and/ indenting the code was 19KB, as opposed to 8KB for newlines only.

My recommendation

If you are using a Javascript error tracker, or you want to more easily debug Javascript code on production, then configuring Uglifier (or your asset compiler of choice) to include newlines gains you quite a lot for a relatively small size increase.

In the future source maps might save the day, allowing you to minify and recompile your Javascript to your heart's content, but given neither Uglifier nor CoffeeScript can generate source maps (yet) your best option is to simply include those newlines.

Configuring Jammit

If you're using Jammit instead of the Rails asset pipeline you can add one of the following to your assets.yml file.

Uglify

javascript_compressor: uglifier
compressor_options:
  beautify: true
  beautify_options:
    indent_level: 0

YUI

javascript_compressor: yui
compressor_options:
  line_break: 0
# Uncompressed
$ rake assets:precompile; ls -l public/assets/application.js* ~/Dropbox/Codez/amen/amen
-rw-r--r-- 1 tlucas staff 1085829 26 Jul 12:52 public/assets/application.js
-rw-r--r-- 1 tlucas staff 249038 26 Jul 12:52 public/assets/application.js.gz
# Standard
$ rake assets:precompile; ls -l public/assets/application.js*
-rw-r--r-- 1 tlucas staff 534897 26 Jul 12:52 public/assets/application.js
-rw-r--r-- 1 tlucas staff 143074 26 Jul 12:52 public/assets/application.js.gz
# :beautify => true
$ rake assets:precompile; ls -l public/assets/application.js*
-rw-r--r-- 1 tlucas staff 785503 26 Jul 12:52 public/assets/application.js
-rw-r--r-- 1 tlucas staff 162218 26 Jul 12:52 public/assets/application.js.gz
# :beautify => true,
# :beautify_options => {:indent_level => 0}
$ rake assets:precompile; ls -l public/assets/application.js*
-rw-r--r-- 1 tlucas staff 617419 26 Jul 12:52 public/assets/application.js
-rw-r--r-- 1 tlucas staff 151255 26 Jul 12:52 public/assets/application.js.gz
@DavidBennettPIO
Copy link

Looks like there is a new options api, so I think now you want:

  if defined? Uglifier
    config.assets.js_compressor = Uglifier.new(
      :output => {
        # Add new lines
        :beautify => true,
        # Don't add indentation
        :indent_level => 0
      }
    )
  end

There is also the :preserve_line => true option but it seems useless in the assets pipeline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment