The Grails asset-pipeline
is a plugin used for managing/processing static assets. These include processing, and minification of both css, and javascript files. It is also capable of being extended to compile custom static assets, such as coffeescript.
Asset Pipeline is intended to replace the defacto Grails equivalent (resources-plugin
) with a more efficient, developer friendly architecture (similar to rails asset-pipeline). The asset-pipeline levereges the latest in minification (UglifyJS) to reduce your asset sizes as much as possible. A few differences between the resources plugin and asset-pipeline include:
- On the fly processing - No more waiting for your assets to reload after making a change
- Compiled assets by generator - No more hanging up application boot times while processing files.
grails asset-precompile
- Reduced Dependence - The plugin has compression, minification, and cache-digests built in.
- Easy Debugging - Makes for easy debugging by keeping files seperate in development mode.
- Simpler manifests and taglibs - Read on for more information.
Asset-Pipeline automatically creates a series of folders within your grails-app directory: grails-app\assets\javascript , grails-app\assets\images, grails-app\assets\stylesheets
Place your static assets in those directories and simply include them into your layouts. Asset pipeline supports setting up manifests using these files.
Example grails-app/javascripts/application.js
:
//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");
The above is an example of some of the require directives that can be used. Custom directives can be created and overridden into the DirectiveProcessor
class.
Optionally, assets can be excluded from processing if included by your require tree. This can dramatically reduce compile time for your assets. To do so simply leverage the excludes configuration option:
grails.assets.excludes = ["tiny_mce/src/*.js"]
Asset pipeline provides several new tag libs for including javascript and css into your gsp files.
Example:
<head>
<asset:javascript src="application.js"/>
<asset:stylesheet src="application.css"/>
</head>
These helpers will automatically adjust to point to the cache-digested versions of the files when running in a non-development environment.
NOTE: In development mode your stylesheets and javascripts will be included as individual script tags. This is intended to make it easier for debugging. Bundling is enabled in all other environments and can be forced in development mode by adding grails.assets.bundle=true
to your Config.groovy
.
There are several advantages to using the asset-pipeline instead of the standard grails resources plugin.
- File dependencies are in the top of your assets. (No Resources.groovy)
- Assets in plugins become level with your app.
- On the fly processing in Development mode (No more waiting for reloads)
- Coffeescript, LESS, and others become first class citizens ( debuggable )
- Require entire folder trees with one line
- Better minification (UglifyJs) , and compiling before the WAR is built
- Faster application startup time
- Easy extensibility
Asset pipeline makes it easy to serve assets from within plugins. It's actually quite simple. The grails-app/assets
, and 'web-app' (for legacy plugin support) folders from all plugins are considered include paths. Essentially, when a file is requested (i.e. jquery.js
) The asset pipeline first will check the local applications assets folder. If it is not found it will scan through all the install plugins and serve the requested file. This has the added benefit of allowing you to override a plugins copy of the js file in your local project.
NEW: Asset Pipeline now automatically tries to convert relative urls specified in your css file to absolute paths. This makes it easier to use third party libraries within the asset-pipeline stack.
Assets should be compiled before building a war file. This can be done by running grails asset-precompile
Asset Pipeline can be configured to copy your assets files out into an external storage path. This can be useful for setting up your web server (i.e. Nginx) to directly server your static assets. To do so, simply define a config variable in your Config.groovy
environment block
environments {
production {
grails.assets.storagePath = "/full/path/to/storage"
}
}
It is also possible to configure a custom CDN asset url for serving this assets:
environments {
production {
grails.assets.url = "http://s3.amazonaws.com/asset-pipe/assets/"
}
}
Asset Pipeline uses classes of type AssetFile
. By default, this plugin comes with a JsAssetFile
, and CssAssetFile
. These define the match pattern syntax for understanding requires directives, known extensions, processors, and content-type. The application bases its file look-up on content-type of the request rather than extension. This allows the user to maybe define a CoffeeAssetFile
with the javascript content type and a request to localhost/assets/app.js
would be able to find assets/app.coffee
. To add custom file definitions you must add the definition in 2 locations:
- Add the reference to your AssetHelper.assetSpecs static property in your plugins startup or Bootstrap:
def doWithDynamicMethods = { ctx ->
AssetHelper.assetSpecs << HandlebarsAssetFile
}
- Add an
_Events.groovy
file and register an event listener foreventAssetPrecompileStart
:
eventAssetPrecompileStart = {
asset.pipeline.AssetHelper.assetSpecs << asset.pipeline.handlebars.HandlebarsAssetFile
}
We do this instead of Artefacts so that we do not have to load up the full application stack during precompile. This saves > 30% of memory usage right off the bat and reduces compile time significantly.
- Add more configuration options.
- Tests would be good.
All contributions are of course welcome as this is an ACTIVE project. Any help with regards to reviewing platform compatibility, adding more tests, and general cleanup is most welcome. Thanks to several people for suggestions throughout development. Notably: Rick Jensen (@cdeszaq), and more to follow I'm sure...