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.
assets {
configOptions = [commonJs: false] //useful for custom config on extension libraries
}
or:
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:
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:
//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 fromsrc/assets
intobuild/assets
-
assetPluginPackage
- Copies all assets unprocessed into an archive useful for plugin jars similar to webjars. -
assetClean
- Cleans and resets thebuild/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:
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:
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:
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
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:
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.
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.
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:
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.
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:
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.
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':
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.
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.
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.