/jekyll-asset-pipeline

Powerful asset pipeline for Jekyll that collects, converts and compresses JavaScript and CSS assets

Primary LanguageRubyMIT LicenseMIT

Jekyll Asset Pipeline

Jekyll Asset Pipeline is a powerful asset pipeline that automatically collects, converts and compresses/minifies your site's JavaScript and CSS assets when you compile your Jekyll site.

Table of Contents

Features

  • Declarative dependency management via asset manifests
  • Asset preprocessing/conversion (supports CoffeeScript, Sass/Scss, Less, Erb, etc.)
  • Asset compression (supports YUI Compressor, Closure Compiler, etc.)
  • Fingerprints bundled asset filenames with MD5 hashes for better browser caching
  • Automatic generation of HTML "link" and "script" tags that point to bundled assets
  • Integrates seamlessly into Jekyll's workflow, including auto site regeneration

How It Works

Jekyll Asset Pipeline's workflow can be summarized as follows:

  1. Review site markup for instances of the css_asset_tag and javascript_asset_tag Liquid tags. Each occurrence of either of these tags identifies when a new bundle needs to be created and outlines (via a manifest) which assets to include in the bundle.
  2. Collect raw assets based on the manifest and run them through converters/preprocessors (if necessary) to convert them into valid CSS or JavaScript.
  3. Combine the processed assets into a single bundle, compress the bundled assets (if desired), and save the compressed bundle to the "_site" output folder.
  4. Replace css_asset_tag and javascript_asset_tag Liquid tags with HTML "link" and "script" tags, respectively, that link to finished bundles.

Getting Started

Jekyll Asset Pipeline is extremely easy to add to your Jekyll project and has no incremental dependancies beyond those required by Jekyll. Once you have a basic Jekyll site up and running, follow the steps below to install and configure Jekyll Asset Pipeline.

  1. Install the "jekyll-asset-pipeline" gem via Rubygems.
gem install jekyll-asset-pipeline

If you are using Bundler to manage your project's gems, you can just add "jekyll-asset-pipeline" to your Gemfile and run bundle install.

  1. Add a "_plugins" folder to your project if you do not already have one. Within the "_plugins" folder, add a file named "jekyll_asset_pipeline.rb" with the following require statement as its contents.
require 'jekyll_asset_pipeline'
  1. Move your assets into a Jekyll ignored folder (i.e. a folder that begins with an underscore "_") so that Jekyll won't include these raw assets in the site output. I recommend using an "_assets" folder to hold your site's assets.

  2. Add the following Liquid blocks to your site's HTML "head" section. These blocks will be converted into HTML "link" and "script" tags that point to bundled assets. Within each block is a manifest of assets to include in the bundle. Assets are included in the same order that they are listed in the manifest. Replace the "foo" and "bar" assets with your site's assets. At this point we are just using plain old javascript and css files (hence the ".js" and ".css" extensions). See the Asset Preprocessing section to learn how to include files that must be preprocessed (e.g. CoffeeScript, Sass, Less, Erb, etc.). Name the bundle by including a string after the opening tag. We've named our bundles "global" in the below example.

{% css_asset_tag global %}
- /_assets/foo.css
- /_assets/bar.css
{% endcss_asset_tag %}

{% javascript_asset_tag global %}
- /_assets/foo.js
- /_assets/bar.js
{% endjavascript_asset_tag %}

Asset manifests must be formatted as YAML arrays and include full paths to each asset from the root of the project. YAML does not allow tabbed markup, so you must use spaces when indenting your YAML manifest or you will get an error when you compile your site. If you are using assets that must be preprocessed, you should append the appropriate extension (e.g. '.js.coffee', '.css.less') as discussed in the Asset Preprocessing section.

  1. Run the jekyll command to compile your site. You should see an output that includes the following Jekyll Asset Pipeline status messages.
Asset Pipeline: Processing 'css_asset_tag' manifest 'global'
Asset Pipeline: Saved 'global-md5hash.css' to 'yoursitepath/assets'
Asset Pipeline: Processing 'javascript_asset_tag' manifest 'global'
Asset Pipeline: Saved 'global-md5hash.js' to 'yoursitepath/assets'

If you do not see these messages, check that you have not set Jekyll's "safe" option to "true" in your site's "_config.yml". If the "safe" option is set to "true", Jekyll will not run plugins.

That is it! You should now have bundled assets. Look in the "_site" folder of your project for an "assets" folder that contains the bundled assets. HTML tags that point to these assets have been placed in the HTML output where you included the Liquid blocks. You may notice that your assets have not been converted or compressed-- we will add that functionality next.

Asset Preprocessing

Asset preprocessing (i.e. conversion) allows us to write our assets in languages such as CoffeeScript, Sass, Less, Erb or any other language. One of Jekyll Asset Pipeline's key strengths is that it works with any preprocessing library that has a ruby wrapper. Adding a preprocessor is straightforward, but requires a small amount of additional code.

In the following example, we will add a preprocessor that converts CoffeeScript into JavaScript.

CoffeeScript

  1. In the "jekyll_asset_pipeline.rb" file that we created in the "Getting Started" section, add the following code to the end of the file (i.e. after the "require" statement).
module JekyllAssetPipeline
    class CoffeeScriptConverter < JekyllAssetPipeline::Converter
      require 'coffee-script'

      def self.filetype
        '.coffee'
      end

      def convert
        return CoffeeScript.compile(@content)
      end
    end
end

The above code adds a CoffeeScript converter. You can name a converter anything as long as it inherits from "JekyllAssetPipeline::Converter". The "self.filetype" method defines the type of asset a converter will process (e.g. ".coffee" for CoffeeScript) based on the extension of the raw asset file. A "@content" instance variable that contains the raw content of our asset is made available within the converter. The converter should process this content and return the processed content (as a string) via a "convert" method.

  1. If you haven't already, you should now install any dependancies that are required by your converter. In our case, we need to install the "coffee-script" gem.
gem install coffee-script

If you are using Bundler to manage your project's gems, you can just add "coffee-script" to your Gemfile and run bundle install.

  1. Append a ".coffee" extension to the filename of any asset that should be converted with the CoffeeScriptConverter. For example, "foo.js" would become "foo.js.coffee".

  2. Run the jekyll command to compile your site.

That is it! Your asset pipeline has converted any CoffeeScript assets into JavaScript before adding them to a bundle.

SASS/SCSS

You probably get the gist of how converters work, but I thought I'd add an example of a SASS converter for quick reference.

module JekyllAssetPipeline
  class SassConverter < JekyllAssetPipeline::Converter
    require 'sass'

    def self.filetype
      '.scss'
    end

    def convert
      return Sass::Engine.new(@content, syntax: :scss).render
    end
  end
end

Don't forget to install the "sass" gem before you run the jekyll command since the above SASS converter requires the "sass" library as a dependency.

Successive Preprocessing

If you would like to run an asset through multiple preprocessors successively, you can do so by naming your assets with nested file extensions. Nest the extensions in the order (right to left) that the asset should be processed. For example, .css.scss.erb would first be processed by an "erb" preprocessor then by a "scss" preprocessor before being rendered. This convention is very similar to the convention used by the Ruby on Rails asset pipeline.

Don't forget to define preprocessors for the extensions you use in your filenames, otherwise Jekyll Asset Pipeline will not process your asset.

Asset Compression

Asset compression allows us to decrease the size of our assets and increase the speed of our site. One of Jekyll Asset Pipeline's key strengths is that it works with any compression library that has a ruby wrapper. Adding asset compression is straightforward, but requires a small amount of additional code.

In the following example, we will add a compressor that uses Yahoo's YUI Compressor to compress our CSS and JavaScript assets.

Yahoo's YUI Compressor

  1. In the "jekyll_asset_pipeline.rb" file that we created in the "Getting Started" section, add the following code to the end of the file (i.e. after the "require" statement).

    module JekyllAssetPipeline
      class CssCompressor < JekyllAssetPipeline::Compressor
        require 'yui/compressor'
    
        def self.filetype
          '.css'
        end
    
        def compress
          return YUI::CssCompressor.new.compress(@content)
        end
      end
    
      class JavaScriptCompressor < JekyllAssetPipeline::Compressor
        require 'yui/compressor'
    
        def self.filetype
          '.js'
        end
    
        def compress
          return YUI::JavaScriptCompressor.new(munge: true).compress(@content)
        end
      end
    end

The above code adds a CSS and a JavaScript compressor. You can name a compressor anything as long as it inherits from "JekyllAssetPipeline::Compressor". The "self.filetype" method defines the type of asset a compressor will process (either '.js' or '.css'). The "compress" method is where the magic happens. A "@content" instance variable that contains the raw content of our bundle is made available within the compressor. The compressor should process this content and return the processed content (as a string) via a "compress" method.

  1. If you haven't already, you should now install any dependencies that are required by your compressor. In our case, we need to install the "yui-compressor" gem.
gem install yui-compressor

If you are using Bundler to manage your project's gems, you can just add "yui-compressor" to your Gemfile and run bundle install.

  1. Run the jekyll command to compile your site.

That is it! Your asset pipeline has compressed your CSS and JavaScript assets. You can verify that this is the case by looking at the contents of the bundles generated in the "_site/assets" folder of your project.

Google's Closure Compiler

You probably get the gist of how compressors work, but I thought I'd add an example of a Google Closure Compiler compressor for quick reference.

class JavaScriptCompressor < JekyllAssetPipeline::Compressor
  require 'closure-compiler'

  def self.filetype
    '.js'
  end

  def compress
    return Closure::Compiler.new.compile(@content)
  end
end

Don't forget to install the "closure-compiler" gem before you run the jekyll command since the above compressor requires the "closure-compiler" library as a dependency.

Templates

When Jekyll Asset Pipeline creates a bundle, it returns an HTML tag that points to the bundle. This tag is either a "link" tag for CSS or a "script" tag for JavaScript. Under most circumstances the default tags will suffice, but you may want to customize this output for special cases (e.g. if you want to add a CSS media attribute).

In the following example, we will override the default CSS link tag by adding a custom template that produces a link tag with a "media" attribute.

  1. In the "jekyll_asset_pipeline.rb" file that we created in the "Getting Started" section, add the following code.

    module JekyllAssetPipeline
      class CssTagTemplate < JekyllAssetPipeline::Template
        def self.filetype
          '.css'
        end
    
        def html
          "<link href='/#{@path}/#{@filename}' rel='stylesheet' type='text/css' media='screen' />\n"
        end
      end
    end

If you already added a compressor and/or a converter, you can include your template class alongside your compressor and/or converter within the same JekyllAssetPipeline module.

The “self.filetype” method defines the type of bundle a template will target (either ".js" or ".css"). The “html” method is where the magic happens. “@path” and "@filename" instance variables are available within the class and contain the path and filename of the generated bundle, respectively. The template should return a string that contains an HTML tag pointing to the generated bundle via an "html" method.

  1. Run the jekyll command to compile your site.

That is it! Your asset pipeline used your template to generate an HTML "link" tag that includes a media attribute with the value "screen". You can verify that this is the case by viewing the generated source within your project's "_site" folder.

Configuration

Jekyll Asset Pipeline provides the following configuration options that can be controlled by adding the following to the end of your project's "_config.yml" file.

asset_pipeline:
  bundle: true            # Default = true
  compress: true          # Default = true
  output_path: assets     # Default = assets
  display_path: nil       # Default = nil
  gzip: false             # Default = false

If you don't have a "_config.yml" file, consider reading the configuration section of the Jekyll documentation.

  • The "bundle" setting controls whether Jekyll Asset Pipeline bundles the assets defined in each manifest. If "bundle" is set to false, each asset will be saved individually and individual html tags pointing to each unbundled asset will be produced when you compile your site. It is useful to set this to false while you are debugging your site.
  • The "compress" setting tells Jekyll Asset Pipeline whether or not to compress the bundled assets. It is useful to set this setting to "false" while you are debugging your site.
  • The "output_path" setting defines where generated bundles should be saved within the "_site" folder of your project.
  • The "display_path" setting overrides the path to assets in generated html tags. This is useful if you are hosting your site at a path other than the root of your domain (e.g. "http://example.com/blog/").
  • The "gzip" setting controls whether Jekyll Asset Pipeline saves gzipped versions of your assets alongside un-gzipped versions.

Octopress

Octopress is a popular framework for Jekyll that can help you get a blog up and running quickly. Jekyll Asset Pipeline can be added to an Octopress site using the Getting Started steps above with the following modifications:

  1. Octopress uses Bundler to manage your site's dependencies. You should add gem "jekyll-asset-pipeline" to your Gemfile and then run bundle install to install.

  2. Instead of adding a "_plugins" folder, you should put "jekyll_asset_pipeline.rb" in the "plugins" folder included by default in the root of your Octopress site.

  3. You should still store your assets in an Jekyll ignored folder (i.e. a folder that begins with an underscore "_"), but note that this folder should be located within the "source" folder of your Octopress site (e.g. "source/_assets").

  4. No change to this step.

  5. Instead of running the jekyll command to compile your site, you should use Octopress' rake commands (e.g. rake generate) as outlined here.

If you have any difficulties using Jekyll Asset Pipeline with Octopress, please open an issue.

Contribute

You can contribute to the Jekyll Asset Pipeline by submitting a pull request via GitHub. I have identified the following areas for improvement:

  • Tests, tests, tests. I'm embarrassed to say that I didn't write a single test while building Jekyll Asset Pipeline. This started as a hack for my blog and quickly grew into a library as I tweaked it to support my own needs. This project is now fully tested.
  • Handle remote assets. Right now, Jekyll Asset Pipeline does not provide any way to include remote assets in bundles unless you save them locally before generating your site. Moshen's Jekyll Asset Bundler allows you to include remote assets, which I thought was pretty interesting. That said, I think it is generally better to keep remote assets separate so that they load asynchronously. After some thought, I've decided that this is not a priority. If you disagree, let me know.
  • Successive preprocessing. Currently you can only preprocess a file once. It would be better if you could run an asset through multiple preprocessors before it gets compressed and bundled. As of v0.1.0, Jekyll Asset Pipeline now supports successive preprocessing.

Feel free to message me on Twitter or Facebook.

Community

Code Status

  • Build Status
  • Dependency Status
  • Code Climate

Credits

As I was building Jekyll Asset Pipeline, I came across a number of tools that I was able to draw inspiration and best practices from, but one stood out in particular... I have to give credit to Moshen for creating the Jekyll Asset Bundler.

I also have to give credit to Mojombo for creating Jekyll in the first place.

License

Jekyll Asset Pipeline is released under the MIT License.


Like this project? You may want to read my blog.