assets

assets

The asset module is library to validate, concatenate, minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages and process/compile them to another language. Finally, it help you to write high quality code by validate JavaScript and CSS too.

A variety of processors are available: (jshint, clean-css, jscs, uglify, closure-compiler, etc..), but also you might want to write your owns.

dependency

<dependency>
  <groupId>org.jooby</groupId>
  <artifactId>jooby-assets</artifactId>
  <version>1.1.0</version>
</dependency>

getting started

The first thing you need to do is to define your assets. Definition is done in your .conf file or in a special file: assets.conf.

assets.conf

assets {
 fileset {
   home: [assets/home.js, assets/home.css]
 }
}

App.java

{
  use(new Assets());
}

The assets module will publish 4 request local variables for home fileset: _css and _js each of these variables is a list of string with the corresponding files. There are two more variables: _styles and _scripts :

 <html>
 <head>
   {{& home_styles}}
 <body>
   ...
   {{& home_scripts}}
 </body>
 </head>
 </html>

The variables: _styles and _scripts produces one ore more link and script tags. The example above, shows you how to render these variables in the template engine of your choice (handlebars, here).

Now, let’s see how to configure the Maven plugin to process our assets at build-time:

pom.xml

 <plugin>
   <groupId>org.jooby</groupId>
   <artifactId>jooby-maven-plugin</artifactId>
   <executions>
     <execution>
       <goals>
         <goal>assets</goal>
       </goals>
     </execution>
   </executions>
 </plugin>

The plugin will process all your assets and include them to the final .jar, .zip or .war.

Cool, isn’t?

how it works?

The assets.fileset defines all your assets. In dev assets are rendered/processed at runtime. In prod at built-time.

Assets are rendered at runtime using *_styles or *_scripts variables. So you define your assets in one single place: assets.conf.

Also, at build-time, the asset compiler concatenates all the files from a fileset and generate a fingerprint. The fingerprint is a SHA-1 hash of the content of the fileset. Thanks to the fingerprint an asset can be cached it for ever! Defaults cache max age is: 365 days.

That isn’t all! the *_styles and *_scripts are updated with the fingerprint version of assets, so you don’t have to do or change anything in your views! It just works!!!

fileset

A fileset is a group of assets within a name. The fileset name is expanded into 4 request local variables, for example:

assets {
 fileset {
   home: [assets/home.js, assets/home.css]
   pageA: [assets/pageA.js, assets/pageA.css]
 }
}

Produces 4 variables for home:

  • home_css: a list of all the css files
  • home_styles: a string, with all the css files rendered as link tags
  • home_js: a list of all the js files
  • home_scripts: a string, with all the js files rendered as script tags

Another 4 variables will be available for the pageA fileset!

extending filesets

Extension or re-use of filesets is possible via the: < operator:

assets {
 fileset {
   base: [js/lib/jquery.js, css/normalize.css]
   home < base: [js/home.js]
   pageA < base: [js/pageA.js]
 }
}

processors

An AssetProcessor usually checks or modify an asset content in one way or another. They are defined in the assets.conf files using the pipeline construction:

assets {
 fileset {
   home: [js/home.js, css/home.css]
 }
 pipeline {
   dev: [jshint, jscs, csslint, sass]
   dist: [uglify, sass, clean-css]
 }
}

Example above, defines a pipeline for development (dev) and one generic for prod (dist).

In dev the code will be checked it against js-hint, jscs and csslint! But also, we want to use sass for css!!

The generic dist will be used it for any other environment and here we just want to optimize our javascript code with uglify, compile sass to css and then optimize the css using clean-css!!

live compiler and error report

This module comes with a live compiler which is enabled by default in dev:

live compiler

If you want to turn it off, just set the assets.watch = false.

The fancy error page is generated by whoops, here is an example on how to unable whoops:

{
  // required
  use(new Whoops());

  use(new Assets());
}

asset processor

Checks, validate and/or modify asset contents. An AssetProcessor is usually provided as a separated dependency.

how to use it?

First thing to do is to add the dependency:

  <dependency>
    <groupId>org.jooby</groupId>
    <artifactId>jooby-assets-my-processor</artifactId>
    <scope>provided</scope>
  </dependency>

Did you see the provided scope? We just need the processor for development, because assets are processed at runtime. In prod, assets are processed at built-time via Maven/Gradle plugin, so we don’t need this library/dependency. This also, helps to keep our dependencies and the jar size small.

Now we have the dependency all we have to do is to add it to our pipeline:

assets {
  pipeline: {
    dev: [my-processor]
  }
}

configuration

It is possible to configure or set options too:

assets {
  pipeline: {
    dev: [my-processor]
    dist: [my-processor]
  }
  my-processor {
    foo: bar
  }
}

Previous example, set a foo property to bar! Options can be set per environment too:

assets {
  pipeline: {
    dev: [my-processor]
    dist: [my-processor]
  }
  my-processor {
    dev {
      bar: bar
    }
    dist {
      foo: bar
    }
    foo: foo
  }
}

Here, in dev processor has two properties: foo:foo and bar:bar, while in dist the processor only has foo:bar

binding

The my-processor will be resolved it to: org.jooby.assets.MyProcessor class. The processor name is converted to MyProcessor, it converts the hyphenated name to upper camel and by default processors are defined in the org.jooby.assets package.

A custom binding is provided via the class property:

assets {
  pipeline: {
    dev: [my-processor]
    dist: [my-processor]
  }
  my-processor {
    class: whatever.i.Want
  }
}

asset aggregator

Contributes new or dynamically generated content to a fileset. Content generated by an aggregator might be processed by an {@link AssetProcessor}.

how to use it?

First thing to do is to add the dependency:

<dependency>
    <groupId>org.jooby</groupId>
    <artifactId>jooby-assets-dr-svg-sprites</artifactId>
    <scope>provided</scope>
  </dependency>

Did you see the provided scope? We just need the aggregator for development, because assets are processed at runtime. In prod, assets are processed at built-time via Maven/Gradle plugin, so we don’t need it. This also, helps to keep our dependencies and the jar size small.

Now we have the dependency all we have to do is to add the svg-sprites aggregator to a fileset:

assets {
  fileset {

    home: [
      // 1) Add the aggregator to a fileset
      svg-sprites,
      css/style.css,
      js/app.js
    ]
  }

  svg-sprites {
    // 2) The `css/sprite.css` file is part of the `home` fileset.
    spritePath: "css/sprite.css"
    spriteElementPath: "images/svg",
  }

}

Here for example, the svg-sprites aggregator contributes the css/sprite.css file to the home fileset. The fileset then looks like:

assets {
  fileset {
    home: [
      css/sprite.css,
      css/style.css,
      js/app.js
    ]
  }
}

It replaces the aggregator name with one or more files from AssetAggregator.fileset method.

available processors

css processors

js processors

  • props: replace application properties in JavaScript files.

  • jscs: JavaScript code style checker.

  • jshint: JavaScript linter, helps to detect errors and potential problems in code..

  • babel: Ecma6 now via Babel.

  • rollup: rollup.js the next-generation ES6 module bundler.

  • ng-annotate: Add, remove and rebuild AngularJS dependency injection annotations.

  • closure-compiler: Google JavaScript optimizer and minifier.

  • uglify: uglify.js optimizer.

  • requirejs: r.js optimizer.

  • yui-js: YUI JS optimizer.