Introduction

The asset-pipeline provides first class static and 'not so static' asset management for the JVM including many native framework integrations. It is highly extensible and provides processing of dynamic languages like LESS, SASS, Coffee, Typescript, and more. This goes beyond simple asset packaging and processing (making tools like grunt or webpack unnecessary for building high quality single page apps on the JVM), but also takes it a step further by providing features for optimally serving assets from your application as well.

This guide documents configuration and usage examples of the asset-pipeline plugin.

Release History

  • March 19, 2019

    • - 3.0.10 - Added Grails 4 and Spring Boot 2+ Support due to new gradle tasks, Upgraded GrooCSS To The Latest.

  • February 28, 2019

    • - 3.0.9 - Bug fix related to asset resolution of common.js module syntax

    • - 3.0.8 - Compass Cleanup, Improvements to i18n, Closure excluded from runtime dependencies, Build fails on minify failures correctly now.

  • August 24, 2018

    • - 3.0.6 - compass-asset-pipeline cleanup, fixes for jdk7+ running when not using Ecmascript 6. micronaut 1.0.0.M4 build support.

  • August 23, 2018

    • - 3.0.5 - Fix for Security Vulnerability discovered in Grails/Spring filters related to Jetty only.

  • July 26, 2018

    • - 3.0.3 - Regression fix in Gradle

    • - 3.0.2 - Further Improvements For ECMAScript 2017, Various bug fixes, Micronaut 1.0.0.M3 support

  • May 29, 2018

    • - 3.0.0 - Enhanced ECMAScript 2017,2018 Support and BabelJs Processing. CommonJs Module Support Dramatically Enhanced. Module loading file-loader support for images. JDK 8+ Requirement (Nashorn needed for BabelJs).

  • May 23, 2018

    • - 2.15.0 - Added Micronaut Framework Support for 1.0.0-SNAPSHOT. Added Support for deduplication of asset script and stylesheet tags support for grails.

  • December 6, 2017

    • 2.14.7 - Grails filter now handles resolving index.html if at directory root. Also grails asset mapping can now be set to root level with '' to resolve at root level. if this is in place we can mix and match static documents with grails actions.

  • November 7, 2017

    • 2.14.5 - Fix with decoding accepts-encoding to ensure proper use of gzip based decryption

  • November 6, 2017

    • 2.14.4 - Fixed issues with storagePath permission enforcement, and performance optimizations

  • February 27, 2017

    • 2.13.2 - Spring Boot 1.5 Support, commonJs function parsing improvements, and fixes to potential concurrency access issue on config.

  • January 28, 2017

    • 2.13.1 - AssetCompiler now defaults to number of available cores for thread count, fixes to grails 2 plugin as well.

  • January 26, 2017

    • 2.13.0 - AssetCompiler is now Concurrent with a default maxThreads of 4, this can be adjusted in build.gradle via assets{} block

  • January 24, 2017

    • 2.12.10 - Fix a cache manager concurrancy bug and more jsx improvements

  • January 16, 2017

    • 2.12.7 - Fixes to jsx-asset-pipeline parsing in comment handling in expressions

    • 2.12.6 - Fixes to jsx-asset-pipeline parsing in comment handling in expressions

    • * January 12, 2017

    • 2.12.5 - Bug fixes in jsx processor

  • January 2, 2017

    • 2.12.2 - Bug fixes for Spring Boot and CompileStatic issue in AbstractAssetFile

  • November 27, 2016

    • 2.12.0 - Ecmascript 6 Module Loading Support using (.js.es6 or .jsx.es6) file extensions. Improved CommonJs support. Released at 30k ft!

  • October 28, 2016

    • 2.11.6 - Fixing gradle bug with multiproject builds including assets from other projects.

  • October 27, 2016

    • 2.11.5 - Improvements to JSX Transpiler with attribute value parsing

  • October 20, 2016

    • 2.11.4 - Resolving Issue with tomcat grails container mode and assetResourceLocator

  • October 12, 2016

    • 2.11.3 - Improvements to ratpack as well as several merged bug fixes.

  • August 27, 2016

    • 2.11.2 - Resolving issue with jsass not expiring cache dependencies. Also fixing bug in commonJs lib and allowing a config option to turn off commonJs.

  • August 14, 2016

    • 2.11.0 - Fixing Relative CommonJS Resolution and improving React JSX Parser to support newlines in assignment expressions. Also added JSX Parser Exception error handling

  • August 5, 2016

    • 2.10.2 - Improvements to CommonJS Cache Dependencies, Fixes to Cache Headers, and Relative URL replacement in HTML files

    • August 1, 2016

    • 2.10.1 - Fixes for JSX and CommonJS

  • July 28, 2016

    • 2.10.0 - Added JSX Asset Pipeline Module and ES6 Support

  • June 16, 2016

    • 2.9.4 Release - Bug fixes with grails bootup and copying files to external folder.

    • 2.9.3 Release - Bug fixes with grails bootup and copying files to external folder.

    • 2.9.2 Release - Bug fixes with grails bootup and copying files to external folder.

  • June 3, 2016

    • 2.10.0 Release - Adds asset_url() method to javascript processing for resolving assets with properly digested names within your javascript files. See Concepts section on Relative Urls.

  • May 24, 2016

    • 2.9.1 Release - Fixes asset resolution issues with libsass in sass-asset-pipeline

  • May 23, 2016

    • 2.9.0 Release - sass-asset-pipeline moved to compass-asset-pipeline and new sass-asset-pipeline using jsass (libsass) added. New Documentation and Website release.

  • April 22, 2016

    • 2.8.0 Release - Bug fixes, New classpath resolver for binary plugin require_tree support. New asset-defer option for javascript taglib.

    • 3.2.0 Release - Exploded plugin support, and jarTaskName support. Bug fixes, New classpath resolver for binary plugin require_tree support. New asset-defer option for javascript taglib.

    • April 5, 2016

    • 3.1.3 , 2.7.4 Release - Fixes for webjar asset resolution or any binary dependency

    • March 24, 2016

    • 3.1.2 Release - More aggressive closing of open file handles in Servlet filter for more efficient use of resources

    • 2.7.2 Release - More aggressive closing of open file handles in Servlet filter for more efficient use of resources

    • March 9, 2016

    • 3.1.1 Release - Improvements to compile time performance,fixed bootstrap bug, reducing open file handles

    • 2.7.2 Release - Improvements to compile time performance, fixed bootstrap bug, and moving of .asscache to target folder

    • March 4, 2016

    • 2.7.0 Release - Fixes bugs in filter and compiler

    • 3.1.0 Release - 2.7.0 release of core for grails 3.1

  • Nobember 24, 2015

    • 2.6.7 Release - Core version bump with fixes for less4j and regex excludes support

    • 3.0.15 (grails3) Release - Core version bump with fixes for less4j and regex excludes support

  • November 16, 2015

    • 3.0.14 (grails3) Release - Fixes a bug in SpringClassPath Resolver related to require_tree

    • 2.6.6 Release - Fixes a bug in SpringClassPath resolver related to require_tree

    • 3.0.12 (grails3) Release - Fixes to compile time performance and runtime using stream buffers

    • 2.6.4 Release - Improved compile time performance

  • November 11, 2015

    • 3.0.11 (grails3) Release - Fixes to thread safety, and compile static fixes.

  • October 22, 2015

    • 3.0.10 (grails3) Release - Cleaned up debug output

    • 3.0.9 (grails3) Release - Asset Compiler is now streaming for binary files. Lower memory usage, faster builds. Production asset serving now caches resources for more speed. Fixed AssetResourceLocator in production mode

  • October 21, 2015

    • 2.6.2 Release - less4j dependency updates for less Module

  • October 6, 2015

    • 2.5.9 Release - More fixes to AssetResourceLocator

  • October 5, 2015

    • 2.5.8 Release - More features for Last Modified date and If-Last-Modified headers. Also core bump with fixes for build phase

  • October 3, 2015

    • 2.5.6 Release - Fixing bug in asset filter for cache

    • 2.5.6 Release - Fixing bug in asset filter for cache

    • 2.5.5 Release - Asset Compiler is now streaming for binary files. Lower memory usage, faster builds. Production asset serving now caches resources for more speed. Fixed AssetResourceLocator in production mode

  • October 2, 2015

    • 2.5.4 Release - More enhancements to Last Modified headers, also fixes for jar resolution.

  • September 29, 2015

    • 2.5.2 Release - Add Last Modified Header as well as fixes to assetPathExists taglib

  • August 21, 2015

    • 3.0.8 Release - Added bundle=true attribute option to javascript and stylesheet taglib.

    • 2.5.1 Release - Added bundle=true attribute option to javascript and stylesheet taglib.

  • August 13, 2015

    • 2.5.0 Release - Development Runtime cache is now persisted for faster load times! Gradle enhancements for resolvers and deduplicated plugins. Can also now include non digested files in war if needed. Added skipNonDigests config option.

  • July 30, 2015

    • 3.0.6 Grails3 Release - Fixes issues with extracted WAR containers

  • July 29, 2015

    • 2.4.3 Release - Fixes for gradle plugin portal only

    • 2.4.2 Release - Fixes bug in CSS and HTML Relative URL digest replacement being inaccurate

  • July 28, 2015

    • 2.4.0 Release - Fixes bug in asset resolvers being overly aggressive about matching files with wrong extension type.

  • July 2, 2015

    • 2.3.9 Release - Fix in _AssetCompile gant script for buildDir, bad quotation used around string

  • June 30, 2015

    • 2.3.8 Release - Fixes Jar asset injection for gradle and grails 3.x when assets.compileDir is non standard.

    • 3.0.2 Grails 3 Module - Updated to work with changes to public class directives in core.

  • June 27, 2015

    • 2.3.7 Release - Fixes to relative path replacement in css/html. Improvements in Angular @ngInject minification. Support for custom directives on files (i.e. //=wrapped)

  • June 19, 2015

    • 2.3.2 Release - Fixes for html processor and performance improvements in css processor

  • June 18, 2015

    • 2.3.0 Release - HtmlProcessor can now recalculate relative urls with digests on processing of static html. Fixed issues with classpath file resolution and cache. Better gradle support, boot support, and ratpack support.

  • June 16, 2015

    • 2.2.5 Release - Fixing classpath resolution bug with cache.

  • May 31, 2015

    • 2.2.3 Release - No longer storing non digest versions in war file, cutting overhead in half. Also removed Commons i/o dependency. Faster byte stream.

  • May 29, 2015

    • 2.2.2 Release - Also now scans 'provided' jar dependencies

    • 2.2.1 Release - Fixed issue with binary plugins in war build not including assets

  • March 5, 2015

    • 2.1.4 Release - Removed some debug printlns. Whoops!

  • February 25, 2015

    • 2.1.3 Release - Fixed bug preventing images / non processable entities from being loaded from binary plugins.

    • 2.1.2 Release - Performance Improvement on scanning classpath for binary plugin assets

  • January 28, 2015

    • 2.1.1 Release - Fixed Binary / Classpath Resolver Support. Now scans META-INF/assets, META-INF/static, and META-INF/resources (yes that means webjars).

  • January 5, 2015

    • 2.1.0 Release - Fixed bug in CSS Processor breaking asset compile

  • December 31, 2014

    • 2.0.21 Release - Nested Grails related asset-pipeline classes into the asset.pipeline.grails package so that the asset.pipeline package does not get marked reloadable

  • December 29, 2014

    • 2.0.20 Release - Fixed bug in CSSProcessor and cache digest names, Fixed absolute path issue, documentation improvements, resolver improvements

  • December 10, 2014

    • 2.0.17 Release - Fixed bug in CSSProcessor for recalculating relative paths

    • 2.0.16 Release - Fixed bug on 2nd war compile with Windows Platforms

  • December 5, 2014

    • 2.0.14 Release - Fixed compileDir for maven based builds

  • December 3, 2014

    • 2.0.13 Release - Faster Dev Mode, Fixed bug in war build, Fixed Css Processor with image paths. Fixed files with spaces.

    • 2.0.11 Release - We no longer export groovy,groovy-templates dependency since grails uses groovy-all

    • 2.0.10 Release - Fixed a bug in require_tree directive being a little too grabby. Also fixed windows platform support.

  • December 2, 2014

    • 2.0.8 Release - Moved to the new Asset-Pipeline-Core library with 2x faster compiling, binary plugin support, sourcemaps and more.

  • July 29, 2014

    • 1.9.6 Release - Whoops I had a typo. Thanks Travis.ci

  • July 29, 2014

    • 1.9.5 Release - Fixed Windows Platform Bug in CSS. Upgraded to UglifyJS2.0

  • July 11, 2014

    • 1.9.3 Release - Fixed ETag Headers for non-digested files, and updated project docs.

  • July 10, 2014

    • 1.9.2 Release - Fixed bug in cache manager, updated docs.

    • 1.9.1 Release - Added commons-io dependency for more recent versions of grails.

  • June 28, 2014

    • 1.9.0 Release - Added Absolute Image Support, Various bug fixes. Documentation Improvements.

  • June 6, 2014

    • 1.8.11 Release - require_tree directive now falls back to absolute references if path doesn’t exist relatively

  • June 1, 2014

    • 1.8.10 Release - Added Support for comma delimited require lists

  • April 30, 2014

    • 1.8.7 Release - ETag Header Support and Vary: Accept-Encoding

  • April 29, 2014

    • 1.8.6 Release - AssetResourceLocator fix

  • April 28, 2014

    • 1.8.5 Release - GGTS and STS Eclipse Groovy Compiler Fixes

    • 1.8.4 Release - GGTS and STS Eclipse Groovy Compiler Fixes

  • October 13, 2013

    • 1.0.1 release

  • October 28, 2013

    • 1.0.2 release

  • November 22, 2013

    • 1.1.2 Release

    • 1.1.3 Release

Concepts

Asset-pipeline is a highly extensible asset processing library that is easy to integrate into your jvm based application or to even use standalone with gradle. It works by defining AssetFile definitions for different file types in which Processors can be chained for processing and conversion to different target content types. But to use it you don’t really need to know about any of this (It really does just work).

CommonJs

Asset-pipeline now fully supports the commonJs method of loading modules using the require() function. It is important to note, however that these calls are preprocessed outside of a js runtime such that the string being passed to require must be a constant string and not a variable..

Note
Loading image files as modules uses a base url prefix by default of /assets/ The mapping config option can often be set to an empty string to move this to the root level of your application (very common in Micronaut apps).

ECMAScript 2017,2018, beyond

Asset-pipeline now supports processing Modern ECMAScript specifications utilizing BabelJs. By default this is disabled for performance but can be enabled by two ways. One is simply by adding .es6 , .es7, .es8 to the end of your javascript file extensions. This can also be done with jsx files (i.e. .jsx.es6) when using jsx-asset-pipeline.

If it is desired to process ALL javascript assets through BabelJs this can be done as well by setting the configuration property in both development runtime and gradle for assets called enableES6:true.

Note
BabelJs processing is new and will more than likely be enhanced in the future.

Directives

Similar to other packaging libraries like webpack or grunt, asset-pipeline provides a means to require other files or "modules" into your javascript and/or css. We will call these require patterns directives.

Note
Asset-pipeline chooses to be dependency syntax agnostic. This makes it very easy to add in extension modules for things like CommonJS.

In asset-pipeline it is a common pattern to list all requirements of your file at the top in a comments section but this is not required. Example:

//This is a JavaScript file with its top level require directives
//= require jquery
//= require app/models.js
//= require_tree views
//= require_self

console.log("This is my javascript manifest");

Or similarly a CSS file might look like this:

/*
This is a comment block in the top of a css file
*= require bootstrap
*= require font-awesome
*= require navigation/header.css
*= require_tree components
*/

body {
  font-size: 12px;
}

This may look very simple at first but can become really quite powerful. For example, these directives can be nested in other files that you might include. This allows the asset-pipeline to build a dependency-graph, eliminating duplicate requirements, and ensuring everything is loaded in the most optimal order possible.

Important
Requirement directives are recursively scanned within each required file, not just the first file.

One other cool aspect of these require directives is the extension is not mandatory. Asset-pipeline will automatically look for any file matching that name (minus the extension) that has a registered AssetFile capable of converting it to the intended target file type. i.e. that Bootstrap directive could be including a LESS file or the javascript could easily include a Coffeescript file.

Encoding

In some cases it may be necessary to specify an encoding for your assets. An example might be for Japanese characters in a JavaScript file. To do this, two things must be done. First, we simply set the charset attribute when we include are JavaScript file:

<script src="application.js" charset="utf-8"/>

This should take care of testing in development mode and debugging. However, when we move to production/WAR mode the precompiler has no way to infer the desired encoding for compilation. To accomplish this, we have the //= encoding directive. This can be placed at the top of your application.js to define the desired compilation encoding.

//=encoding UTF-8
//=require_self
//=require_tree .

That’s all there is to it.

Common Js

As of asset-pipeline 2.10.x CommonJs support was added. This is automatically detected in any javascript file and assets are automatically resolved within the project structure of asset-pipeline just like any other directive processor (i.e. var React = require('react');). If your project has conflicts with commonJs it can be turned off by setting the configOptions in gradle and in grails.assets config.

build.gradle
assets {
  configOptions = [commonJs: false] //useful for custom config on extension libraries
}

or:

application.groovy
grails {
  assets {
    commonJs = false
  }
}

Project Structure

Depending on which framework integration is being used (or even if no framework integration is being used) the project of the asset-pipeline files can slightly differ. However, typically this only affects the source folder location while all other aspects of the project structure remain the same.

By default assets live in the src/assets directory of the project (except in the case of grails where these live in grails-app/assets). However, files should not live at this direct root level and by default asset-pipeline will not detect them if they were. It is a conventional practice that files be organized in one level down subfolders. For example, in a base grails app the following folder structure is used and encouraged:

  • assets/javascripts

  • assets/stylesheets

  • assets/images

  • assets/libs — common naming for components that contain both css/javascript/images

Asset-pipeline will automatically add any subfolder in the assets directory as a FileResolver. Meaning each folder is treated as a root level resolver path. It is also important to note that asset-pipeline does not actually care what file types go where. This is purely for the organizational benefit of the project.

Important
When requiring files using require directives do not include these directory names, treat them as root level traversal.

Search Paths

When a file is referenced via a require directive or web request, the asset-pipeline checks for the file in several locations.

First it tries to find the file relative to the manifest including it. For example "admin/application.js" looking for "table.js"

// FileName: admin/application.js
//= require table

The first place we will look is within src/assets/javascripts/admin/* We will proceed to do this within all of the asset sub folders across plugins after the main application is searched.

The next place we will look is the root of all src/assets plugin sub folders (e.g. src/assets/*/table.js).

Finally all binary plugins are scanned in the classpath:META-INF/assets folder, classpath:META-INF/static and classpath:META-INF/resources.

In all cases, the applications assets folder takes precedence between the search paths, but plugins get scanned as well.

Build Structure

When the project is built (in Gradle that’s the assetCompile task) these folders are flattened and merged. Meaning this first level subdirectory structure disappears and all files are copied into build/assets. These files also get md5 digested names for cache busting as well as GZIP versions for compressed file serving (except already compressed images).

In a Java based framework any type of WAR or JAR packaging typically gets detected and assets are automatically moved into classpath:assets along with a file classpath:assets/manifest.properties. This manifest is a list of every file that was packaged by the asset-pipeline as the key, and the digested name equivalent as the value. This facilitates easy differential syncing between CDNS (like an s3 bucket for cloudfront) as well as fast and easy generation of ETag headers when serving assets from the application (more on this later).

Relative Urls

With all this renaming of assets with digested names as well as slight restructuring between source and build, what happens with url references specified in CSS as well as HTML. This is where some familiar with the Ruby on Rails sprockets based asset-pipeline might remember the need for erb helpers to specify path replacement. The asset-pipeline for the JVM, however, takes a different more automatic approach:

All CSS type files go through a processor called the CssProcessor. This processor looks for any url(../path/to/file.png) type patterns and automatically resolves the asset from the asset-pipeline. If it finds the matching file, the url is automatically replaced with the correct url pattern including the digest name: url(path/file-dadvbfgdaf123e.png).

This relative url replacement is really handy because any external css library that is included in your project (i.e. bootstrap) can be used as is without any need to sift through its code and replace url patterns to match. This feature is also performend on HTML files.

Tip
HTML files automatically get relative url replacement making it easy to generate 100% static websites without any need for a dynamic templating engine.

Currently javascript is not scanned for relative path replacement. It is now possible to reference assets from your javascript using a precompiler directive in your code called asset_url. It is not relative and always produces an absolute path response which can be configured via the mapping config option which defaults to 'assets':

---
var logo = asset_url('grails_logo.png');

Note
This method is not evaluated in javascript but rather replaced like a precompiler directive.

Gradle

This section of the documentation discusses configuration and setup of the asset-pipeline gradle plugin. Most framework integrations use this plugin for builds so it is important to know how it works.

Getting Started

Getting started with gradle is very quick and easy. The asset-pipeline-gradle plugin is included in both the gradle plugin repository as well as jcenter and maven. For those of you not familiar with gradle it is a very powerful build tooling system that you should definitely check out.

Important
Asset-Pipelines build classes are not directly dependent on gradle and can be easily integrated into other build tooling frameworks like Maven.

In its simplest form the build.gradle file might look like this:

build.gradle
buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "com.bertramlabs.plugins:asset-pipeline-gradle:3.0.10"
  }
}

apply plugin: "com.bertramlabs.asset-pipeline"

Or even simpler:

build.gradle
//gradle 2.1+
plugins {
  id "com.bertramlabs.asset-pipeline" version "3.0.10"
}

When this plugin is added a couple tasks and extensions are automatically registered:

  • assetCompile - Compiles and packages assets from src/assets into build/assets

  • assetPluginPackage - Copies all assets unprocessed into an archive useful for plugin jars similar to webjars.

  • assetClean - Cleans and resets the build/assets directory.

Note
All of these tasks support Gradle’s incubating continuous build feature. Simply prefix the task with -t.

Create a folder src/assets and subfolders:

  • javascripts

  • stylesheets

  • html

  • images

  • …​

Now assets can be packaged and processed with ease and performance far outpacing other static asset build tooling.

Tip
Including javascript libraries and css libraries from npm is super easy as well. Check out @craigburke's awesome gradle plugin client-dependencies-gradle

Configuration

The asset-pipeline gradle plugin provides several very useful configuration options. An extension called assets is automatically added to Gradle when the plugin is applied that provides several options:

build.gradle
assets {
  minifyJs = true
  minifyCss = true
  enableSourceMaps = true
  maxThreads = 4 //useful for concurrent asset processing during build
  configOptions = [:] //useful for custom config on extension libraries

  minifyOptions = [
    optimizationLevel: 'SIMPLE',
    angularPass: true // Can use @ngInject annotation for Angular Apps
  ]

  includes = []
  excludes = ['**/*.less'] //Example Exclude GLOB pattern

  //for plugin packaging
  packagePlugin=false //set to true if this is a library

  //developmentRuntime can be turned off
  developmentRuntime=true

  //if you want to customize the jar task this task runs on you can specify a jarTaskName
  jarTaskName=null

  // Can add custom asset locations (directories or individual jar files)
  from '/vendor/lib'
  from '/path/to/file.jar'
}

The default settings will work for most projects, but if one wanted to add a custom source file location the from directive can be quite useful. Asset-pipeline’s broad Resolver support makes it easy to include assets from a diverse set of locations (even webjars).

A common configuration change for assets may be the includes/excludes patterns. These allow one to specify specific files that should not be individually required or compiled because it does not make sense for them to be individually compiled. In the example above: all .less files are excluded. This does not exclude processing however when a require directive is used making it very easy to only package and bundle assets that the final website needs. It is also important to note that these patterns do not have to be GLOB patterns but can also be regular expressions by prefixing the pattern with regex:. By default all files prefixed with an underscore are automatically excluded from individual processing (similar to SASS).

Tip
Tuning the includes/excludes patterns can dramatically reduce the build time of your project.

Some additional options are also provided for configuring what types of output assets are generated:

build.gradle
assets {
  enableDigests = true
  skipNonDigests = false
  enableGzip = true
  maxThreads = 4
}

By default both digested named versions of assets as well as non digested named versions are generated as well as the gzip equivalents. Some frameworks dont actually need the non digested named versions ( ratpack, grails, spring-boot, and servlets). These frameworks take advantage of the manifest.properties file discussed earlier in the documentation to properly map requests for non digested file names to the digested equivalent and preserve compatibility with libraries that may not work well with digested file names.

Tip
Change skipNonDigests to true to help reduce the file size of your final generated project.

JVM Based Dependencies

When working on any jvm based gradle project, the asset-pipeline plugin automatically detects all dependencies on the classpath and scans them for assets. This makes it easy to package assets in shared libraries for multiple apps to use or to even include assets that are included from a webjar dependency.

Note
If a library is simply a webjar or asset library, these packages are not needed in your final application package and can be set to provided or compileOnly.

When using asset-pipeline plugins for processing LESS files or other custom extensions they typically need to be both in the development runtime classpath as well as the build classpath. This is because asset-pipeline provides on the fly asset compilation during development for more rapid iteration cycles. To make this simpler a configuration dependency type is automatically added called assets:

build.gradle
dependencies {
  // Example additional LESS support
  assets 'com.bertramlabs.plugins:less-asset-pipeline:3.0.10'
}

Grails 3

This section of the documentation discusses configuration and setup of the asset-pipeline plugin for Grails 3 based applications. Grails is the first jvm framework the asset-pipeline supported and was officially made standard in Grails 2.4 in order to replace the deprecated resources plugin. This also covers differences in the asset-pipeline configuration for development runtime as well as provided taglibs.

Getting Started

In Grails 3 most assets live in the grails-app/assets directory. This folder is automatically generated for you along with the organizational subdirectories javascripts, images, and stylesheets.

To get started simply add asset-pipeline to your build.gradle file (See Gradle usage). And the grails plugin to the dependencies block:

dependencies {
  runtime 'com.bertramlabs.plugins:asset-pipeline-grails:3.0.10'
}

Development Runtime

A great feature built into grails is its development runtime support. All assets that are used in your gsp files are automatically generated and processed on the fly when requested. This means when a change is made in a css file or javascript file, the results are instant. A refresh of the page will reflect the changes without needing to wait for any type of FileWatcher. These results are also cached making it highly performant as your project grows.

Another great aspect of development runtime with grails is each file will individually be required in the gsp. A dependency graph is generated and each file is required individually to make it easy to debug javascript errors and stylesheet errors. While sourcemap support is integrated for javascript, this is not supported in a lot of browsers developers still target compatibility for. It is possible to disable this feature via configuration or simply testing your app with the embedded tomcat war to see how post minified and bundled assets will behave.

Configuration

Asset-Pipeline has several configuration options for use with grails 3. For most cases the configuration options listed in the Gradle guide pertain to grails3. Some subtle differences is the concept of development runtime vs. build time. If you want settings to apply to both development runtime and build time the properties have to be duplicated in your applications application.yml project. This is currently a limitation in how grails reads configuration files and gradle conveys properties.

Most build time options like skipNonDigests or enableGzip will never need specified in this YAML file as they will never be used during development runtime. It is also possible to disable development runtime entirely. In this scenario assets will always be packaged with the application and not monitored for changes unless gradle continuous build is utilized. Simply set assets { developmentRuntime = false} in your build.gradle file.

Example yaml:

grails:
  assets:
    bundle: true #dont individually require files in development
    storagePath: /path/to/store #automatically copy assets to external folder on startup

Mappings and Asset Taglib URLs

In many cases you may want to change the URL for which to include your static assets. This can be useful when using a CDN or perhaps even using nginx to serve your static assets.

To change the URL for your taglibs use the following configuration option:

grails:
  assets:
    url: http://cdn.example.com/

Now your files are going to reference the CDN when running in the production environment. To go with this feature, you can have your application automatically copy your asset files out of your base WAR file on startup of your application. Optionally the asset URL config can also be defined as a closure that takes a request argument.

grails.assets.url = { request ->
        if(request.isSecure()) {
                return "https://cdn.example.com/"
        } else {
                return "http://cdn.example.com/"
        }
}

This allows more fine grained control of your asset URLs based on the incoming request. An example, might be SSL detection or even changing CDN region by source IP.

Note
To use an asset closure config must be placed in an application.groovy file instead of application.yml
grails:
  assets:
    storagePath: /var/cdn/path

You can also change the default Tomcat path for both debugging and file inclusion using the mapping config option.

grails:
  assets:
    mapping: assets

For all these configuration options, you will want to put these config values in the appropriate environment in application.yml or in groovy format in application.groovy.

Organization

Asset pipeline organization occurs within the "grails-app/assets" folder. This folder can exist within both the main application as well as a plugin.

Note
A plugin will also include its web-app directory to better deal with plugins that wish to support both the resources plugin as well as asset-pipeline.

Within the grails-app/assets directory are several subfolders

  • grails-app/assets/javascripts

  • grails-app/assets/stylesheets

  • grails-app/assets/images

A common folder that gets added to this set of organization is a "lib" folder. This folder can be useful in organizing third party libraries like jQuery, or Bootstrap.

Plugins

Plugins also can have the same "grails-app/assets" folder and their URL mapping is also the same. This means it can be more important to ensure unique naming / path mapping between plugins. This is also powerful in the sense that a plugin can add helper manifests to be used within your apps like jquery, bootstrap, font-awesome, and more.

Plugins should make sure that the assets { packagePlugin } property is set to true in their build.gradle file otherwise assets will not properly be packaged into the plugin for use by the application.

Important
Plugins with assets must have the packagePlugin property set to true on the assets extension in build.gradle

If, in the event, a file within a plugin needs to be overridden within your application, simply create the same file with the same relative path to grails-app/assets and it will override / take precedence over the plugin. More on that later.

Note
Since plugins share the same file structure for assets, as well as web-app. It can become more important to "namespace" your plugins by creating further nested folders. (i.e. the plugin SpudCore puts its application.js file within grails-app/assets/javascripts/spud/admin/application.js).

Search Paths

When a file is referenced via a taglib or a manifest require directive, the asset-pipeline checks for the file in several locations.

First it tries to find the file relative to the manifest including it. For example "admin/application.js" looking for "table.js"

// FileName: admin/application.js
//= require table

The first place we will look is within grails-app/assets/javascripts/admin/* We will proceed to do this within all of the asset sub folders across plugins after the main application is searched.

The next place we will look is the root of all grails-app/assets plugin sub folders (e.g. grails-app/assets//table.js, and web-app//table.js for plugins).

Finally all binary plugins are scanned in the classpath:META-INF/assets folder, classpath:META-INF/static and classpath:META-INF/resources.

In all cases, the applications assets folder takes precedence between the search paths, but plugins get scanned as well.

Note
These same conditions should be implemented on any preprocessor extension plugin, e.g. LESS-asset-pipeline follows the same scan for @import directives.

Taglibs

Asset Pipeline adds a few new taglibs to properly reference your assets. These taglibs automatically handle swapout of cache digest names during production use as well as any custom URL mapping changes.

<head>
  <asset:javascript src="application.js"/>
  <asset:stylesheet src="application.css"/>
</head>

The primary include tags, as shown above, are quite useful for including your relevant JavaScript or stylesheet files. Notice that you do not have to prefix with '/assets', as this is handled automatically by the tag.

In GSP views, you can also reference images included in the asset-pipeline with the following tag:

<asset:image src="logo.png" width="200" height="200"/>

Assets can also be referenced within subdirectories if required and simply require the use of the relative path.

<asset:image src="icons/delete.png"/>

It is also possible to return an assetPath as a string for injection in your own tags:

<link href="${assetPath(src: 'manifest.json')}"/>

It is also possible to execute a code section only if an asset exists or to simply test for existence

<asset:assetPathExists src="test.js">
This will only be displayed if the asset exists
</asset:assetPathExists>

or

asset.assetPathExists(src: 'test.js') //returns true or false

Getting Resource

As of version 0.8.2 a new bean exists called assetResourceLocator This can be used to find assets by URI in both development and production mode.

class ExampleService {
  def assetResourceLocator

  def someMethod() {
    Resource myResource = assetResourceLocator.findAssetForURI('test.css')
  }
}

Deferred Scripts

Asset-Pipeline provides a set of tags that can be used to ensure script blocks are deferred to the end of your page. This is not recommended as it is obtrusive, but has been added to help newcomers upgrade existing apps from resources.

<asset:javascript src="application.js" asset-defer="true"/>
<asset:script type="text/javascript">
  console.log("Hello World");
</asset:script>
<asset:script type="text/javascript">
  console.log("Hello World 2");
</asset:script>

Now to render the output of these scripts simply use the following:

<asset:deferredScripts/>

Grails 2

This section of the documentation discusses configuration and setup of the asset-pipeline plugin for Grails 2 based applications. Grails is the first jvm framework the asset-pipeline supported and was officially made standard in Grails 2.4 in order to replace the deprecated resources plugin. This also covers differences in the asset-pipeline configuration and build process as it pertains to GANT instead of Gradle.

Getting Started

In Grails 2 most assets live in the grails-app/assets directory. On any Grails 2.4 or newer application this folder is automatically generated for you along with the organizational subdirectories javascripts, images, and stylesheets.

To get started with asset-pipeline for grails simply add asset-pipeline to the BuildConfig.groovy

BuildConfig.groovy
plugins {
  compile ":asset-pipeline:3.0.10"
  // Uncomment these to enable additional asset-pipeline capabilities
  //compile ":sass-asset-pipeline:3.0.10"
  //compile ":less-asset-pipeline:3.0.10"
  //compile ":coffee-asset-pipeline:3.0.10"
  //compile ":handlebars-asset-pipeline:3.0.10"
}
Note
While there are grails 2 plugins for most add on modules like LESS and SASS, It is not required and the binary asset-pipeline module dependencies can be used instead in the dependencies{} block of BuildConfig.groovy.

Development Runtime

A great feature built into grails is its development runtime support. All assets that are used in your gsp files are automatically generated and processed on the fly when requested. This means when a change is made in a css file or javascript file, the results are instant. A refresh of the page will reflect the changes without needing to wait for any type of FileWatcher. These results are also cached making it highly performant as your project grows.

Another great aspect of development runtime with grails is each file will individually be required in the gsp. A dependency graph is generated and each file is required individually to make it easy to debug javascript errors and stylesheet errors. While sourcemap support is integrated for javascript, this is not supported in a lot of browsers developers still target compatibility for. It is possible to disable this feature via configuration or simply testing your app with grails dev run-war to see how post minified and bundled assets will behave.

Tip
Use grails dev run-war to test your apps behavior once assets are minified and bundled .

Configuration

Asset-Pipeline has several customizable options to tweak the compiler to suit your needs. Below is a list of the various configuration options and explanations for how to use them.

Excludes and Includes

Certain files are not needed for compilation in production. This can be configured globally or for a specific plugin by using the provided configuration options:

Table 1. Table Exclude Properties

Property

Value

grails.assets.excludes

["tiny_mce/src/\*.js"]

grails.assets.plugin."twitter-bootstrap".excludes

["*\*/\*.less"]

grails.assets.plugin."twitter-bootstrap".includes

["bootstrap.less"]

Above you will notice the use of an includes. An includes allows you to override a specific file after the excludes scan has already been performed. The above example makes sure the @bootstrap.less@ file can be compiled from the @twitter-bootstrap plugin@.

Minification

The Asset-pipeline comes with the newer version of Closure Compiler to minify your JavaScript assets. This is great for compression and a few options are provided to tune the minifier. Closure can be enabled/disabled entirely as well as configured via various options.

Table 2. Table Build Options

Property

Value

Default

grails.assets.minifyJs

true or false

true

grails.assets.minifyCss

true or false

true

grails.assets.enableSourceMaps

true or false

true

grails.assets.minifyOptions

Map

(see below)

grails.assets.skipNonDigests

true or false

true

Note
It is normally not necessary to turn off 'skipNonDigests'. Tomcat will automatically still serve files by non digest name and will copy them out using storagePath via the manifest.properties alias map. This simply cuts storage in half. However, if you are attempting to do things like upload to a cdn outside of the cdn-asset-pipeline plugin and via the contents of 'target/assets'. This may still be useful.
grails.assets.minifyOptions = [
    languageMode: 'ES5',
    targetLanguage: 'ES5', //Can go from ES6 to ES5 for those bleeding edgers
    optimizationLevel: 'SIMPLE' //Or ADVANCED or WHITESPACE_ONLY
]

Above are the default values for the majority of Closure Compiler. For specifics on what these options do please refer to the documentation for Closure Compiler.

Mappings and Asset Taglib URLs

In many cases you may want to change the URL for which to include your static assets. This can be useful when using a CDN or perhaps even using nginx to serve your static assets.

To change the URL for your taglibs use the following configuration option:

grails.assets.url = "http://cdn.example.com/"

Now your files are going to reference the CDN when running in the production environment. To go with this feature, you can have your application automatically copy your asset files out of your base WAR file on startup of your application. Optionally the asset URL config can also be defined as a closure that takes a request argument.

grails.assets.url = { request ->
        if(request.isSecure()) {
                return "https://cdn.example.com/"
        } else {
                return "http://cdn.example.com/"
        }
}

This allows more fine grained control of your asset URLs based on the incoming request. An example, might be SSL detection or even changing CDN region by source IP.

grails.assets.storagePath = "/var/cdn/path"

You can also change the default Tomcat path for both debugging and file inclusion using the mapping config option.

grails.assets.mapping = 'assets'

For all these configuration options, you will want to put these config values in the appropriate environment in Config.groovy.

Organization

Asset pipeline organization occurs within the "grails-app/assets" folder. This folder can exist within both the main application as well as a plugin.

Note
A plugin will also include its web-app directory to better deal with plugins that wish to support both the resources plugin as well as asset-pipeline.

Within the grails-app/assets directory are several subfolders

  • grails-app/assets/javascripts

  • grails-app/assets/stylesheets

  • grails-app/assets/images

A common folder that gets added to this set of organization is a "lib" folder. This folder can be useful in organizing third party libraries like jQuery, or Bootstrap.

Plugins

Plugins also can have the same "grails-app/assets" folder and their URL mapping is also the same. This means it can be more important to ensure unique naming / path mapping between plugins. This is also powerful in the sense that a plugin can add helper manifests to be used within your apps like jquery, bootstrap, font-awesome, and more.

These plugins also differ in the fact that the assets within their web-app directory also become available under a similar structure

  • web-app/css

  • web-app/js

  • web-app/img

These paths also get flattened just like the grails-app/assets folder does.

If, in the event, a file within a plugin needs to be overridden within your application, simply create the same file with the same relative path to grails-app/assets and it will override / take precedence over the plugin. More on that later.

Note
Since plugins share the same file structure for assets, as well as web-app. It can become more important to "namespace" your plugins by creating further nested folders. (i.e. the plugin SpudCore puts its application.js file within grails-app/assets/javascripts/spud/admin/application.js).

Search Paths

When a file is referenced via a taglib or a manifest require directive, the asset-pipeline checks for the file in several locations.

First it tries to find the file relative to the manifest including it. For example "admin/application.js" looking for "table.js"

// FileName: admin/application.js
//= require table

The first place we will look is within grails-app/assets/javascripts/admin/* We will proceed to do this within all of the asset sub folders across plugins after the main application is searched.

The next place we will look is the root of all grails-app/assets plugin sub folders (e.g. grails-app/assets//table.js, and web-app//table.js for plugins).

Finally all binary plugins are scanned in the classpath:META-INF/assets folder, classpath:META-INF/static and classpath:META-INF/resources.

In all cases, the applications assets folder takes precedence between the search paths, but plugins get scanned as well.

Note
These same conditions should be implemented on any preprocessor extension plugin, e.g. LESS-asset-pipeline follows the same scan for @import directives.

Taglibs

Asset Pipeline adds a few new taglibs to properly reference your assets. These taglibs automatically handle swapout of cache digest names during production use as well as any custom URL mapping changes.

<head>
  <asset:javascript src="application.js"/>
  <asset:stylesheet src="application.css"/>
</head>

The primary include tags, as shown above, are quite useful for including your relevant JavaScript or stylesheet files. Notice that you do not have to prefix with '/assets', as this is handled automatically by the tag.

In GSP views, you can also reference images included in the asset-pipeline with the following tag:

<asset:image src="logo.png" width="200" height="200"/>

Assets can also be referenced within subdirectories if required and simply require the use of the relative path.

<asset:image src="icons/delete.png"/>

It is also possible to return an assetPath as a string for injection in your own tags:

<link href="${assetPath(src: 'manifest.json')}"/>

It is also possible to execute a code section only if an asset exists or to simply test for existence

<asset:assetPathExists src="test.js">
This will only be displayed if the asset exists
</asset:assetPathExists>

or

asset.assetPathExists(src: 'test.js') //returns true or false

Getting Resource

As of version 0.8.2 a new bean exists called assetResourceLocator This can be used to find assets by URI in both development and production mode.

class ExampleService {
  def assetResourceLocator

  def someMethod() {
    Resource myResource = assetResourceLocator.findAssetForURI('test.css')
  }
}

Deferred Scripts

Asset-Pipeline provides a set of tags that can be used to ensure script blocks are deferred to the end of your page. This is not recommended as it is obtrusive, but has been added to help newcomers upgrade existing apps from resources.

<asset:javascript src="application.js" asset-defer="true"/>
<asset:script type="text/javascript">
  console.log("Hello World");
</asset:script>
<asset:script type="text/javascript">
  console.log("Hello World 2");
</asset:script>

Now to render the output of these scripts simply use the following:

<asset:deferredScripts/>

Micronaut

This section of the documentation discusses configuration and setup of the asset-pipeline plugin for Micronaut based applications. Micronaut functionality includes both development runtime and production asset serving capabilities. All features provided by asset-pipeline are available for use with Micronaut framework except template url replacement helpers (as this has not been standardized in the framework yet). Assets are served at the root url by default instead of /assets to facilitate single page app styles better. If a file is not found, the request will proceed through the Micronaut chain finding any controller endpoints that are registered.

Getting Started

In Micronaut assets live in the same place as a standard gradle implementation src/assets directory. This folder should contain organizational subdirectories javascripts, images, and stylesheets. Another option is to use a standard folder within assets called something like src/assets/frontend. Useful if simply using asset-pipeline to render and properly digest generated apps from React or other view frameworks.

To get started simply add asset-pipeline to your build.gradle file (See Gradle usage). And the micronaut plugin to the dependencies block:

plugins {
	id "com.bertramlabs.asset-pipeline" version "3.0.10"
}

dependencies {
  runtime 'com.bertramlabs.plugins:asset-pipeline-micronaut:3.0.10'
  //Example LESS or Sass Integration
  //assets 'com.bertramlabs.plugins:less-asset-pipeline:3.0.10'
  //assets 'com.bertramlabs.plugins:sass-asset-pipeline:3.0.10'
}
Note
Asset-Pipeline requires groovy when running in the development runtime mode.

By default, the asset-pipeline gradle plugin will automatically attach itself to the shadowJar gradle task as a build dependency. The development runtime will also auto engage so long as no assets/manifest.properties file exists on the classpath.

Configuration

Configuration handling is still being worked on for micronaut but currently one option is configurable. By default assets in micronaut are rendered at the root url. If a file is not found, the standard micronaut filter chain is used. This is great for serving a Single Page app all in one application all while still using micronaut @Controller endpoints. To change asset-pipelines default mapping to the standard /assets endpoint simply add the following configuration to your Micronaut configuration.

application.groovy
assets {
  mapping = "assets"
}

Development Runtime

A great feature built into asset-pipeline is its development runtime support. All assets that are used in your application are automatically generated and processed on the fly when requested. This means when a change is made in a css file or javascript file, the results are instant. A refresh of the page will reflect the changes without needing to wait for any type of FileWatcher. These results are also cached making it highly performant as your project grows.

Micronaut has great native continuous build integration support with Gradle and in some cases developers may not want to use the development runtime and instead rely on this continuous build flow. To do that simply disable developmentRuntime:

build.gradle
assets {
  developmentRuntime = false
}

By doing this one can use gradle -t run to kick off continuous runs of the application.

Ratpack

This section of the documentation discusses configuration and setup of the asset-pipeline plugin for Ratpack based applications. Ratpack functionality includes both development runtime and production asset serving capabilities. All features provided by asset-pipeline are available for use with Ratpack framework except template url replacement helpers (as this has not been standardized in the framework yet).

Getting Started

In Ratpack assets live in the same place as a standard gradle implementation src/assets directory. This folder should contain organizational subdirectories javascripts, images, and stylesheets.

To get started simply add asset-pipeline to your build.gradle file (See Gradle usage). And the ratpacks plugin to the dependencies block:

dependencies {
  runtime 'com.bertramlabs.plugins:ratpack-asset-pipeline:3.0.10'
  //Example LESS or Sass Integration
  //assets 'com.bertramlabs.plugins:less-asset-pipeline:3.0.10'
  //assets 'com.bertramlabs.plugins:sass-asset-pipeline:3.0.10'
}
Note
Asset-Pipeline requires ratpack-groovy to be used in a ratpack application.

Finally the AssetPipelineModule needs added to the Ratpack Registry.

ratpack.groovy
import static ratpack.groovy.Groovy.ratpack
import asset.pipeline.ratpack.AssetPipelineModule

ratpack {
  bindings {
    module(AssetPipelineModule) { cfg ->
                // by default /assets, change url mapping
            // cfg.url("/")
            cfg.sourcePath("../../../src/assets")
    }
  }
}

This module does support a few configuration options and due to how the runtime works the sourcePath does need to be specified depending on if ratpack is being run in development from an IDE or gradle directly.

By default a Handler will be registered to the url /assets but it is also possible to simply use the AssetPipelineHandler directly or even change the base mapping config as seen in the example above.

Development Runtime

A great feature built into asset-pipeline is its development runtime support. All assets that are used in your application are automatically generated and processed on the fly when requested. This means when a change is made in a css file or javascript file, the results are instant. A refresh of the page will reflect the changes without needing to wait for any type of FileWatcher. These results are also cached making it highly performant as your project grows.

Ratpack has great native continuous build integration support with Gradle and in some cases developers may not want to use the development runtime and instead rely on this continuous build flow. To do that simply disable developmentRuntime:

build.gradle
assets {
  developmentRuntime = false
}

Spring Boot

This section of the documentation discusses configuration and setup of the asset-pipeline plugin for Spring Boot based applications. Spring functionality includes both development runtime and production asset serving capabilities. All features provided by asset-pipeline are available for use with Ratpack framework except template url replacement helpers.

Getting Started

In Spring Boot assets live in the same place as a standard gradle implementation src/assets directory. This folder should contain organizational subdirectories javascripts, images, and stylesheets.

To get started simply add asset-pipeline to your build.gradle file (See Gradle usage). And the ratpacks plugin to the dependencies block:

dependencies {
  runtime 'com.bertramlabs.plugins:asset-pipeline-spring-boot:3.0.10'
  //Example LESS or Sass Integration
  //assets 'com.bertramlabs.plugins:less-asset-pipeline:3.0.10'
  //assets 'com.bertramlabs.plugins:sass-asset-pipeline:3.0.10'
}
Note
Asset-Pipeline requires at least groovy 2.0.7 to be used within a spring boot application.

It is also necessary to add ComponentScan to your application class in order for the asset-pipeline plugin to properly initialize.

package demo

@Configuration
@ComponentScan(['demo','asset.pipeline.springboot'])
@EnableAutoConfiguration
class Application {

    static void main(String[] args) {
        SpringApplication.run Application, args
    }
}

By default a servlet filter will be registered to the url /assets from which all assets will be served.

Development Runtime

A great feature built into asset-pipeline is its development runtime support. All assets that are used in your application are automatically generated and processed on the fly when requested. This means when a change is made in a css file or javascript file, the results are instant. A refresh of the page will reflect the changes without needing to wait for any type of FileWatcher. These results are also cached making it highly performant as your project grows.

Plugin Modules

Asset-pipeline comes out of the box with modules for processing several common asset types. These include LESS, SASS, Coffeescript, Handlebars, and more. There are also several third party modules created by developers for Typescript, JSX, etc.

LESS

With the LESS module it is easy to automatically compile LESS files down to the native browser CSS format. By default the less-asset-pipeline plugin takes advantage of Less4j for highly performant LESS compilation. If the standard compiler is desired this can also be enabled however it is dramatically slower due to running in a javascript environment.

build.gradle
dependencies {
  assets 'com.bertramlabs.plugins:less-asset-pipeline:3.0.10'
}

Configuration

To switch to the standard compiler simply adjust the less.compiler property to 'standard':

build.gradle
assets {
    configOptions = [
      less: [
        compiler: 'standard'
      ]
    ]
}
Tip
During war build your less files are compiled into css files. This is all well and good but sometimes you dont want each individual less file compiled, but rather your main base less file. It may be best to add a sub folder for those LESS files and exclude it in your precompile config.

SASS

The SASS module provides easy processing and implementation of scss and sass files. It uses libsass for highly performant compiling of SASS files and automatically handles asset resolution correctly.

build.gradle
dependencies {
  assets 'com.bertramlabs.plugins:sass-asset-pipeline:3.0.10'
}
Important
Before 2.9.0 sass-asset-pipeline used jruby and compass. This version of the plugin has been renamed to compass-asset-pipeline.

Coffeescript

The Coffee Asset-Pipeline module provides coffeescript compilation support for the jvm based asset-pipeline. Simply add this file to your buildscript classpath or development environment and they are automatically processed.

build.gradle
dependencies {
  assets 'com.bertramlabs.plugins:coffee-asset-pipeline:3.0.10'
}
Note
If the native coffeescript binary is detected in the build environment. Coffeescript processing will take advantage of this over internal javascript processing. Simply npm install -g coffeescript.

Extending

The asset-pipeline is extremely extensible and easy to customize to suit one’s needs. You might extend the asset-pipeline to handle a new type of asset that may need to be preprocessed before being served to the browser, or you may want to define a new custom directive. This guide will go over the basics of how to perform those tasks with ease.

Asset File Definition

The AssetFile definition is where our journey begins. This is the defining file for various file types. Without this definition, the asset-pipeline will treat an unknown file type as a standard passthrough resource. As an example, lets first look at the CssAssetFile definition.

class CssAssetFile extends AbstractAssetFile {
  static final String contentType = 'text/css'
  static extensions = ['css']
  static compiledExtension = 'css'
  static processors = [CssProcessor]

  String directiveForLine(String line) {
    line.find(/\*=(.*)/) { fullMatch, directive -> return directive }
  }
}

This file definition is pretty short but allows us to define some very useful information. First, we look at the static definitions at the top of the class. These static definitions are fairly easy to meta-override with Groovy and add additional processors or adjust with added plugins.

The contentType property is used to match a file definition with an incoming file request. When the browser requests a text/css content-type file , this file is matched and files matching this definition are scanned. The extensions list tells asset-pipeline which file extensions to scan through and match. In this case it is just 'css', but in the case of LESS for example, we may be looking for extensions less, or css.less.

The compiledExtension property tells asset-pipelines precompiler what the final file extension should be.

Finally, the processors array determines the list of processors that need be run on the file contents before returning a result. This array is executed in order. In this case, we have the CssProcessor (a processor for converting the relative image paths and replacing with their cache digested version).

Directive Definition

An assetFile can specify a REGEXP pattern for require directives. These directives are used to bundle assets together. Some file types don’t utilize these require directives and simply returning a null value will cancel directive processing.

  Pattern directivePattern = ~/(?m)\*=(.*)/
Note
Used to there was a directiveForLine that matched on each individual line. This was changed to support a multiline regex pattern for faster processing.

The example above shows a match pattern for CSS files. This allows it to match require directives for the following example:

/*
*= require_self
*= require_file example_b
*= require_tree .
*/

body {
  margin-top:25px;
}

Processing Data Streams

Processors are used to precompile certain assets, and/or adjust the file path contents. The Processor class itself will get a more in depth explanation in the next section. For now, the part we want to look at is the processedStream function.

String processedStream(Boolean precompiler) {
  def fileText
  def skipCache = precompiler ?: (!processors || processors.size() == 0)

  if(baseFile?.encoding || encoding) {
    fileText = file?.getText(baseFile?.encoding ? baseFile.encoding : encoding)
  } else {
    fileText = file?.text
  }

  def md5 = AssetHelper.getByteDigest(fileText.bytes)
  if(!skipCache) {
    def cache = CacheManager.findCache(file.canonicalPath, md5)
    if(cache) {
      return cache
    }
  }
  for(processor in processors) {
    def processInstance = processor.newInstance(precompiler)
    fileText = processInstance.process(fileText, this)
  }

  if(!skipCache) {
    CacheManager.createCache(file.canonicalPath,md5,fileText)
  }

  return fileText
}

The example above iterates over all of the processor classes defined in our static processors variable. This creates a new instance and informs the processor whether this is a developer mode request or being issued by the precompiler (useful for determining if file replacements need to be cache digested or not). The processedStream method is now a part of the AbstractAssetFile definition and handles cache management if there are processors.

Adding the Asset definiton to the list of AssetFiles

Originally we had to add these classes on startup in both runtime and build phases to the AssetHelper.assetSpecs array. Thanks to contributions by Graeme Rocher we have been able to simplify this process. Simply adding a list file META-INF/asset-pipeline/asset.specs to the classpath will automatically get scanned.

Example:

asset.pipeline.HtmlAssetFile
asset.pipeline.JsAssetFile
asset.pipeline.CssAssetFile

Another autoscanning file allows us to tack on Processors to already registered AssetFile specifications. This is called the processor.specs file and goes in the same META-INF/asset-pipeline folder. This is a Properties file with the key being the class path of the Processor and the value being a comma delimited list of AssetFile classes you want the processor added to.

Example:

asset.pipeline.CssProcessor=asset.pipeline.CssAssetFile,asset.pipeline.LessAssetFile

Processors

Processors are where the real power of asset-pipeline comes into play. These are the driving force behind making compileable assets such as LESS, and CoffeeScript first class citizens. Gone is the need to run a compiler on the side, and gone is the delay between making changes in development.

A Processor is an implementation of the Processor interface via the AbstractProcessor class. It must have a constructor with an AssetCompiler argument, and it must have a process method. The rest is up to the developer. The reason the AssetCompiler is passed is for giving the processor access to manipulate the precompiler phase. If a null precompiler is passed, than development mode is assumed and the processor can infer that. An example use case for this is the SassProcessor in the SASS/SCSS Asset Pipeline Plugin. Image sprite generation causes additional image files to be created that need added to the list of files to process.

class CoffeeScriptProcessor extends AbstractProcessor {

  Scriptable globalScope
  ClassLoader classLoader

  CoffeeScriptProcessor(AssetCompiler precompiler){
    super(precompiler)
  }

  String process(String input, AssetFile assetFile) {
    try {
      def cx = Context.enter()
      def compileScope = cx.newObject(globalScope)
      compileScope.setParentScope(globalScope)
      compileScope.put("coffeeScriptSrc", compileScope, input)
      def result = cx.evaluateString(compileScope, "CoffeeScript.compile(coffeeScriptSrc)", "CoffeeScript compile command", 0, null)
      return result
    } catch (Exception e) {
      throw new Exception("""
        CoffeeScript Engine compilation of coffeescript to javascript failed.
        $e
        """)
    } finally {
      Context.exit()
    }
  }
}

Above is an excerpt of the CoffeeScriptProcessor plugin. This plugin takes advantage of RhinoJS to use the CoffeeScript compiler and provide the application with direct support for CoffeeScript files. The process method takes an input, as well as a reference to the asset file definition, and returns a result. To use your processor simply add it to your 'static processors' array on the AssetFile definition.

The LESSProcessor was not used in this example as it’s more complicated due to supporting the @import LESS directive and cache dependencies on the cache manager. However, it is a great example to look at and highly recommended.

Post-Processors

Currently, PostProcessor extensibility is not available. This is currently a feature we are implementing to provide easier dropin for custom minifiers and compressors.