= Juicer
Official URL: http://github.com/cjohansen/juicer/tree/master
Christian Johansen (http://www.cjohansen.no) and contributors:
* Morgan Roderick (http://roderick.dk)
* Elliott Wood (http://two-fish.com)
* Pavel Valodzka (http://github.com/valodzka)
* Daniel Stockman (http://evocateur.org/)
* Aaron Suggs (http://ktheory.com)
* Olle Jonsson (http://ollehost.dk/blog/)
* Jakub Pawlowicz (http://blog.jakubpawlowicz.com)
* stereobooster (https://github.com/stereobooster)
== DESCRIPTION:
Juicer is a command line tool that helps you ship frontend code for production.
High level overview; Juicer can
* figure out which files depend on each other and merge them together, reducing
the number of http requests per page view, thus improving performance
* use YUI Compressor or Google Closure Compiler to compress code, thus improving performance
* verify that your JavaScript is safe to minify/compress by running JsLint on it
* cycle asset hosts in CSS files
* add "cache busters" to URLs in CSS files
* recalculate relative URLs in CSS files, as well as convert them to absolute
(or convert absolute URLs to relative URLs)
* embed images into stylesheets using data-uris
== FEATURES:
=== Merging and minifying
Juicer can read @import statements in CSS files and use them to combine all your
stylesheets into a single file. This file may be minified using the YUI
Compressor or Google Closure Compiler. Eventually it will support other minifying tools too.
Juicer can treat your JavaScript files much the same way too, parsing a comment
switch @depend, as this example shows:
/**
* My script file
*
* @depend jquery-1.4.1.js
*/
var myNS = {
myObject = {}
};
Running juicer merge on this file will result in a minified file
filename.min.js containing the file jquery-1.4.1.js (located in the
same directory) and the code above.
You can use @import (CSS files) and @depend (JavaScript) recursively, effectively
creating a dependency chain for Juicer to climb and merge.
=== Paths
When merging CSS files, you may want to merge CSS files in different directories.
You may also want the resulting CSS file to end up in another directory as well.
Juicer automatically recalculates referenced URLs to reflect this change.
Absolute URLs are not changed by default, but if you provide juicer merge
with --document-root [DIR]
and --relative-urls
then
absolute URLs are converted to URLs relative to output directory. You can also
use --absolute-urls
to convert all URLs to absolute ones.
=== Cache busters
Juicer supports so-called cache busters. A cache buster is a pattern in a path
whose only purpose is to "trick" browsers to redownload a file when it has
changed in cases where a far future expires header is used.
There are three types of cache busters. Soft cache busters (the default) add a
parameter to the URL, like so: http://assets/images/1.png?jcb=1234567890, ie the
letters "jcb" (as in Juicer Cache Buster) and then the timestamp of the files mtime.
Rails cache busters are similar, except they leave out the parameter name. This
mimics the behavior of the image_tag helper in the Ruby on Rails framework, so that
images called from both CSS and from Rails views will have identical URLs.
Unfortunately, the popular web proxy Squid shipped for some time with a default
configuration which would not treat a URL as a "new" URL if the only thing changed
was the GET parameters. For this reason Juicer provides hard cache busters.
Hard cache busters result in URLs such as: http://assets/images/1-jcb1234567890.png,
ie URLs that require either renaming of files, or (more conveniently) a web
server configuration that will forward URLs to the right files anyway.
=== Embed images in stylesheets
Juicer supports embedding images into stylesheets using data uri's.
In order to be as unobtrusive as possible, you must indicate for each image that it
can be embedded in the stylesheet by using the embed url suffix.
Example:
h1 { background: url(/somepath/someimage.png?embed=true); }
When merging the stylesheets, you must specify which type of embedding you want,
currently only 'none' and 'data_uri' are supported.
$ juicer merge my-stylesheet.css -o my-stylesheet.merged.css --embed-images data_uri
The result will look similar to this:
h1 { background: url(data:image/png;base64,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); }
Embedding images in stylesheets should be used very carefully, as they will bloat
your css files considerably, and you will loose some of the fine grained control
over caching of images.
Before using Juicer to embed images into your stylesheets, you should have control
over cache expiration of stylesheets.
You should also have gzip enabled, as base64 encoded images take up about 30% more
space than serving them normally. With gzip enabled this is reduced to about 5-10% bloat.
==== Usage scenario: Modern browsers and IE7, IE6 support
As all of the major browsers in their later incarnations support the data uri scheme,
it is possible to just create two merged stylesheets, although you can create as many
as you want really.
Let's assume you have the following files:
* reset.css
* layout.css
* typography.css
/* layout.css */
body { background: url( /images/my-body-background.png?embed=true); }
And let's assume that you want to merge these, and embed the flagged images in
layout.css into the stylesheet.
To do this, you would have a master stylesheet that imports the other stylesheets using
the @import statemet. It could look like this:
/* master.css */
@import url(reset.css)
@import url(layout.css)
@import url(typography.css)
Now, in order to create the two versions of the stylesheet, you would simply run
Juicer with and without the option to embed images
$ juicer merge master.css -o master.embedded.css --embed-images data_uri
$ juicer merge master.css -o master.normal.css
In order to only load the relevant stylesheet to supporting browsers, and preventing
doubling the effective payload of the background images, you would reference the
stylesheets like this:
master.embedded.css
stylesheet.
The second conditional comment allows only IE8+ browsers to fetch the
master.embedded.css
stylesheet.
The last conditional comment allows only IE7 and lower to fetch the
master.normal.css
stylesheet.
Unfortunately, you will need to use three conditional comments, or you will end
up with an unsightly "-->" displayed at the top of your pages in IE.
==== Browser support
Windows Vista, Windows 2003, Windows 7
Tests needed
Windows XP
* IE8 - up to 32kb IS supported
* Firefox 3.5.2 - OK
* Opera 9.64 - OK
* Chrome 3 - OK
* Safari 4 - OK
OS X 10.6.1
* Firefox 3.5 - OK
* Firefox 2.0.20 - OK
* Safari 4 - OK
* Opera 10 - OK
* Opera 9.27 - OK
* Opera 8.0 - OK
* Camino 1.6.10 - OK
Debian
* Opera 10 - OK
Android
* WebKit - OK
iPhone
* Safari - OK
The test used is located at http://roderick.dk/experiments/data-uri-limits/
The test used tests loading of styles using the href attribute, it is ASSUMED
that loading images from data-uri in css has similar support.
Soon[tm] there will be a testpage with images as well.
If you have funky browser / OS combinations not listed above, please visit the
test and send a screenshot and details about browser and OS to morgan@roderick.dk
==== Support for really old user-agents
If you plan to support user-agents that do understand conditional comments and
does not support the data uri scheme, you will have to do some kind of detection
of support. These user-agents would be very rare, as our browser testing shows,
most modern user agents support data-uris.
* http://weston.ruter.net/2009/05/07/detecting-support-for-data-uris/
Please note that image embedding is NOT a silver bullet, but can help reduce
the amount of HTTP connections needed for a page.
==== MHTML Support
Due to MHTML being very awkward in it's implementation (having the absolute url
including protocol inside the stylesheet itself), it was decided to be of very
little real value and attempts at supporting it has been dropped.
==== Further reading
* http://www.websiteoptimization.com/speed/tweak/inline-images/
* http://en.wikipedia.org/wiki/Data_URI_scheme
== PLANNED FEATURES:
* Support more minifiers, JsMin (Ruby port), Packer and possibly others
* Add support for CssTidy to compress CSS files
If you have any ideas, feature requests, want to contribute or whatever, fork
the project on github, or get in touch through christian (at) cjohansen.no.
== SYNOPSIS:
juicer merge myfile.css
-> Produces myfile.min.css which may contain several CSS files, minified
juicer merge myfile.js
-> Produces myfile.min.js, minified and combined
juicer help
== REQUIREMENTS:
In order to use YUI Compressor, Closure Compiler or JsMin (requires Rhino) you need Java
installed and the java executable available on your path.
== INSTALL:
$ gem install juicer
$ juicer install yui_compressor
$ juicer install closure_compiler
$ juicer install jslint
You need Java installed and available on your PATH. During gem installation,
Juicer will download and install YUI Compressor, Closure Compiler, JsLint and Rhino for you.
rake check_dependencies:development
Will tell you about other dependencies that might be missing on your system.
== Contributors
* Morgan Roderick (http://roderick.dk)
Added support for embedding graphics using data-uri
Several small fixes and optimizations
* Pavel Valodzka (http://github.com/valodzka)
Added Google Closure Compiler support
* Daniel Stockman (http://evocateur.org/)
Fixed `juicer list` when running it against files with no dependencies
* Aaron Suggs (http://ktheory.com)
Added support for depending on directories
Fixed installers on 1.0 branch
* Elliott Wood (http://two-fish.com)
Added "rails" cache buster type
Fixed issues with asset host cycling
* Jakub Pawlowicz (http://blog.jakubpawlowicz.com)
Fixed issue with embedding images that weren't found
== LICENSE:
(The MIT License)
Copyright (c) 2008-2010 Christian Johansen
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.