Overview

What is Shaker?

Shaker is a powerful static asset manager for Mojito applications. It gives users absolute control in transforming, validating, uploading, organizing, and combining resources in order to build maximize performance and build dynamic applications.

Shaker provides contextualization of resources in order to serve different resources based on dimensions such as region, language, and device. The user can specify custom or default tasks such as JS/CSS minification and linting for transforming and validating resources. These resources can then be combined (or rolled up) in order to reduce HTTP requests and take advantage of caching. And finally resources can be uploaded to one or more CDN locations to optimize the delivery of resources to clients.

Why Shaker?

By default, application and mojit assets must be added on the page manually by using the ac.assets addon. This becomes especially challenging when particular assets are needed for different contexts.

Example

// Controller logic
var device = ac.context.device,
    path = '/static/device/assets/simple';

if (device === 'iphone') {
    path += '.' + device;
}

path += '.css';
ac.assets.addCss(path, 'top');

Shaker takes care of all this logic through the assets naming convention. In addition, Shaker gives the user the ability to validate, transform, and combine (roll up) resources and deploy to CDNs. All this is done through a simple configuration, which the user can customize to maximize the performance of their application.

Features

Assets Organization and Contextualization

Shaker automatically adds mojit and application level assets and picks the right versions based on the context. See Organizing Resources.

Transformation/Validation Tasks

Users can specify what kinds of transformation or validation tasks should be applied to each resource. They can define their own custom tasks or use the default tasks including JS/CSS linting and minification. See Tasks Configuration.

Rollups

A rollup consists of many resources and their dependencies that are combined (or rolled up) to produce one or more files. Since the number of files is reduced, rollups can drastically reduce the number of HTTP request and allows browsers to cache the most common resources. See Route Rollups Configuration.

CDN Locations

Rollups and transformed resources can be uploaded to one or more CDN locations. This allows resources to be served from fast servers designed to quickly deliver resources to clients. See Locations Configuration.

Settings and Runtime API

The user has absolute control in deciding where on the page particular resources should appear, whether they should be combo-loaded, and which CDN location should be used. During runtime, the Shaker API can be used on the server side to change any of these settings and data on the page such as the title and html class. See Settings and Runtime API.

Getting Started

Prerequisites

Note

To use Shaker with other versions of Mojito, see Compatibility Table.

Installation Steps

  1. Go to your Mojito application directory:

    $ cd myApp

  2. Get Shaker from the npm registry and install it under your Mojito application (or globally using -g option) so that it can be run from the command line.

    $ npm install mojito-shaker [-g]

  3. Confirm that Shaker has been installed correctly by running the shaker command:

    $ mojito-shake

  4. If you get an error, check if you have correctly configured the Node environment. Also, try setting the right $NODE_PATH to the modules using the following:

    $ export NODE_PATH=:$NODE_PATH:\`npm root -g\`

Running Shaker

Within a Mojito application root folder:

  1. Edit the application.json file to configure and use the ShakerHTMLFrameMojit. It should look like the following:

    [
      {
        "settings": [ "master" ],
        "specs": {
          "htmlframe": {
            "type": "ShakerHTMLFrameMojit"
          }
        }
      }
    ]
    
  2. Execute the Shaker compiler and start the application:

    $ mojito-shake [--context "{key1}:{value1}[,{key2}:{value2}]"] [--run]

    The option --run will make the application start automatically.

  3. Go to following URL: http://localhost:8666

When to Run Shaker Compiler

The Shaker compiler does not need to be ran every time the application starts. If the “locations” option is not being used, then the compiler only needs to be ran if an asset is renamed or added after an intial compilation. If the “locations” option is used, then the compiler must be ran whenever there is a change in a resource that is stored in a location, otherwise the stored resource would be out of date (note that resources with server affinity are never processed by Shaker). Also, changing a runtime setting, does not require re-compilation unless a serve location, that was not used during compilation, is specified.

Compatibility Table

Since Mojito 3.x.x, many API’s have changed, and a lot of new features were introduced, so Shaker has had to adapt to be compatible with Mojito. Thus, you may encounter some incompatibilities depending upon the version of Mojito you’re using.

To avoid compatibility issues, refer to the table below to use the most stable version of Shaker for each Mojito minor version:

Mojito Version Shaker Most Stable Version
0.3.x 0.8.x
0.4.x 2.0.32
0.5.x 4.0.x

Configuration and API

Shaker can be configured through the “shaker” option in application.json. Shaker’s behavior can be customized per context by defining the “shaker” option under multiple context settings. See Using Context Configuration in the Mojito Documentation.

Shaker HTML Frame

Shaker uses a specialized version of Mojito’s HTML frame. The specialized frame, ShakerHTMLFrameMojit, must be specified in application.json.

{
    "settings": [ "master" ],
    "specs": {
        "htmlframe": {
            "type": "ShakerHTMLFrameMojit",
            "config": {
                "deploy": <true_or_false>,
                "title": "<optional_title>",
                "child": {
                    <required_child_configuration>
                }
            }
        }
   }
}

Default Configuration

By default, Shaker assumes the configuration below. The user only needs to specify options that differ from the defaults.

{
    "settings": ["master"],
    "shaker": {
        "resources": {
            "assets": {
                "js": true,
                "css": true
            },
            "binders": true,
            "controllers": true,
            "views": true,
            "langs": true
        },
        "tasks": false,
        "rollups": false,
        "locations": false,
        "settings": {
            "serveLocation": "default",
            "serveJs": {
                "position": "top",
                "combo": false
            },
            "serveCss": {
                "position": "top",
                "combo": false
            }

        }
    }
}

Compilation Options

There are four compilation options, “resources”, “tasks”, “routeRollups”, and “locations”. All these options are optional and give the user absolute control on which resources to process, how to transform/combine them, and where to store them. If any of the options is changed then the Shaker compiler must be ran (see Running Shaker). If any resource change then a re-compilation may be necessary (see When to Run Shaker Compiler).

Resources

The “resources” option specify which type of resources to process. If a property is set to false, then that type of resource is ignored by the Shaker compilation and is handled in Mojito’s default manner.

Property Value Type Default Value Description
assets boolean/object true Whether to include application or mojit level assets. Further configuration can be defined through an object (see Assets Configuration).
binders boolean true Whether to process binders.
controllers boolean true Whether to process controllers.
views boolean true Whether to process views.
langs boolean true Whether to process language modules.

Assets Configuration

Property Value Type Default Value Possible Values Description
js boolean/string true boolean, “inline” Whether to include application or mojit level JS assets. If “inline” is specified, then all JS assets will be inlined.
css boolean/string true boolean, “inline” Whether to include application or mojit level CSS assets. If “inline” is specified, then all CSS assets will be inlined.

Note

Inlined assets can be disabled during runtime through the “settings” option (see Settings).

Tasks

The “tasks” option specifies the list of tasks that should be applied to each type of resource. This option is a map where each key is a resource type and the value is a map of task modules that will be applied to each resource that matches the type. The keys of this map is the name of the task module and each value is a configuration object (a false value disables the task, a true value is the equivalent of an empty configuration object).

Example

"tasks": {
    "js": {
        "jslint": true,
        "jsminify": {
            "squeeze": true,
            "mangle": true
        }
    },
    "css": {
        "csslint": true,
        "cssminify": {
            "yuicompress": true
        }
    }
}

If more than one resource type specified matches a particular resource, then the most specific type is used. The most specific type is determined through the type hierarchy shown below. Any of the resource type below is valid.

https://s.yimg.com/oo/cms/products/shaker/images/resources_hierarchy_703201a83.png

Note

Since both rollup and loader type resources are generated during compilation, they each respectively have default rollup and loader tasks predefined. Rollups have a empty list of tasks and loaders are only js-minified. To overwrite this behavior, explicitly specify custom rollup and loader tasks.

Predefined Task Modules

Task Value Type Description
jslint boolean/object Performs jslint validation. Uses jslint node module.
jsminify boolean/object

Performs minification of js files. Uses uglify-js node module. Options Include:

  • squeeze (boolean): does various optimizations that result in smaller, less readable code.
  • mangle (boolean): whether to mangle variable names
csslint boolean/object Performs csslint validation. Uses csslint node module.
cssminify boolean/object

Performs minification of css files. Uses ‘less’ node module. Options Include:

  • yuicompress (boolean)

See Error Handling for error handling configuration.

Note

If any task transform resources, then at least one location should be defined under “locations”, otherwise transformed resources would not be stored. See Locations.

See Creating Custom Tasks to learn how to create custom tasks modules.

Route Rollups

The “routeRollups” option specifies a rollup module that is used to combine resources after tasks have been applied. Each output rollup is associated with an application route (Shaker loads the rollup for pages matching this route). Only one rollup module can be specified. Rollups are useful for reducing HTTP requests by combining the most common assets for a particular route. Rollups can take advantage of browser caching such that subsequent page requests load rapidly since the most common resources are cached.

Predefined Rollup Module: ‘mojitrollup’

Route Rollup Value Type Description
mojitrollup object Creates css and js rollups for each specified route, which including all resources in the mojit actions specified and application level assets. Resources specifically specified to be ignored through the “resources” option will not appear. All yui-modules will include all levels of dependencies in the rollup.

Mojitrollup Configuration

Property Value Type Description
<route> array<string> Each element in the array follows the pattern “<mojit_name>” or “<mojit_name>.<mojit_action>”. If no action is specified, then assets of all actions are included.

Example

"routeRollups": {
    "module": "mojitrollup",
    "rollups": {
        "<route1>": [
            "<Mojit1>",
            "<Mojit2.action1>",
            "<Mojit2.action2>"
        ],
        "<route2>": [
            "<Mojit1.action3>"
        ]
    }
}

Note

If “routeRollups” is defined, then at least one location must be defined under “locations”, in order to store the generated rollups (see Locations).

See Creating custom Route Rollups to learn how to create custom route rollup modules.

Locations

The “locations” option lists the different locations that will be used to store the processed resources and rollups. To set the location that is used during runtime set the “serveLocation” under “settings” (see Settings). The “locations” option is a map, where each key is a location module and the value is a configuration object (a false value disables the location, a true value is the equivalent of an empty configuration object).

Predefined Location Module: ‘local’

Location Value Type Description
local boolean Stores processed resources and rollups under “<app_dir>/assets/compiled”.

See Error Handling for error handling configuration.

Example

"locations": {
    "local": true,
    "<custom_module>": {
        <custom_module_config>
    }
}

See Creating Custom Locations to learn how to create custom tasks modules.

Error Handling

By default each task or location is considered non-critical, which means that if a particular task or location fails, there will be a warning but compilation will continue. However there might be tasks or locations that are critical and compilation would not make sense if they fail. To make sure compilation stops due to an error, specify the option ‘errorStop’ as true under the task’s or location’s configuration. ‘errorStop’ can also be an integer value, specifying how many errors can occur before compilation is forced to stop.

Runtime Settings and API

There is only one runtime option, “settings”. This option can be modified without the need of re-compilation.

Settings

The “settings” object is only used during runtime and allows the user to customize how assets should be deployed, without the need of re-compiling.

Property Value Type Default Value Description
serveLocation string “default” Whether to include application or mojit level js assets.
serveJs boolean/object true Whether to serve JS resources. See ServeJs Configuration for object configuration.
serveCss boolean/object true Whether to serve CSS assets. See ServeCss Configuration for object configuration.
inline boolean true Whether to serve inline assets. See Assets Configuration for how to inline all css or js assets during compilation.

ServeJs and ServeCss Configuration

Property Value Type Default Value Possible Values Description
position string “top” “top”, “bottom” Where on the page to position the type of asset.
combo boolean false boolean Whether to combo-load the type of asset. Note: rollups are not comob-loaded since they should be cached separately for performance.

Runtime API

Shaker provides a runtine API to modify the behavior of Shaker per request. To use the Shaker API include “mojito-shaker-addon”. To use the shakerInline API include “shaker-inline-addon”.

API Arguments Returns Description
shaker.set [string, boolean/string/object] The value set, null if error. Sets property under “settings” or html data. Valid html data include “title”, “html_class”, and “html_attributes”.
shaker.get [string] The value of the the property, null if no such property. Gets the property value, valid properties correspond to properties that can be set as described above.
shakerInline.inlineFile [string[, type (“css” or “js”)]] Boolean, whether the file was found. Inlines a particular application level asset. See Inlining Using inlineFile.

Organizing Resources

Resources are automatically processed by Shaker as long as they follow Mojito’s standard organization and naming conventions. Shaker has additional conventions to facilitate certain features such as inlining and ignoring particular resources.

Naming Conventions

Contextualization

Resources such as controllers, views, assets, and binders can be contextualize by appending the selector of a dimension to a resource’s filename. For example: a mojit may have two assets, styles.css and styles.iphone.css. styles.css is served for all contexts that do not contain the iphone dimension, while styles.iphone.css only appears in contexts that include the ‘iphone’ dimension. The dimension must be defined in dimensions.json (see Creating Custom Contexts). The selector must be defined in application.json (see Selector Property).

Action Specific Resources

In order to associate a resource with a particular mojit action, the asset’s name should match the mojit action. For example the assets home.css and home-base.js would only appear in the ‘home’ action. Resources that do not match the name of any action within the mojit, appear in all actions. This applies to assets, views, and binders.

Ordering Resources

Since the order of assets on the page can matter, Shaker always processes resources in alphabetical order. This ensures that assets of any particular type appear on the page in the same order regardless of inlining or the presence of rollups.

Inlining Assets

Assets can be inlined by appending ‘-inline’ to the filename. For example, the asset style-inline.css lets Shaker know that it should be inlined. To inline a group of assets, the assets can be moved to a directory called ‘inline’ within ‘assets’, without the need of renaming any asset. See Inlining Assets for more details on inlining.

Voiding Assets

Assets can be ignored by appending ‘-void’ to the filename. For example, the asset style-void.css lets Shaker know that it should be ignored. To ignore a group of assets, the assets can be moved to a directory called ‘void’ within ‘assets’, without the need of renaming any asset. This gives the user flexibility on how to handle certain assets manually.

Inlining

Inlining an asset means that the asset’s content appears on the page’s html instead of being downloaded. This presents performance tradeoffs by increasing the size of the html page in order to reduce HTTP requests. Also an inlined asset can be placed right next to a mojit’s html (css appears above, and js appears below). Since the asset appears immediately next to the mojit, no jittering occurs, which can happen when styling or scripts affecting a mojit is downloaded. To ensure that inlined mojit assets appear next to the mojit’s html, the mojit’s controller must include ‘mojito-shaker-mobstor’, otherwise the inline asset will appear next to the closest ancestor that includes the addon, or the application itself.

Assets can be inlined by naming convention as described in Inlining Assets above. To inline all JS or CSS assets the ‘inline’ value can be specified for the type of resource under the ‘resources’ -> ‘assets’ configuration (see Assets Configuration). Finally an application level asset can be inlined by a mojit by using the shakerInline.inlineFile API as described below.

Note

Assets that appear in a route rollup are never inlined on the page since they are already present in a rollup.

Inlining Using inlineFile

A mojit can request to inline a particular application level resource by using the shakerInline.inlineFile API method. This is useful if a particular asset exists in a separate package and a mojit would like the asset to appear inline next to its HTML. The application level asset must have the ‘-inline’ marker in its filename (see Inlining Assets). The mojit that needs the asset must call shakerInline.inlineFile, with the first argument being the file’s basename, without the ‘-inline’ marker. The second argument is optional string specifying the type (‘css’, or ‘js’). The mojit must include the ‘shaker-inline-addon’ to use the method.

Shaker Components

Shaker is comprised of three components that work together in various ways depending on the environment. This chapter provides an nontechnical overview focusing on the three components and how they work together.

Shaker Core

https://s.yimg.com/oo/cms/products/shaker/images/shaker_core_67416d774.png

Shaker Core is the foundation of Shaker. It alone has the ability to parse shaker.json configuration files, compute and resolve rollup dimensions and dependencies, and ultimately generate rollup metadata.

Shaker Compiler

https://s.yimg.com/oo/cms/products/shaker/images/shaker_compiler_503f457a3.png

The Shaker Compiler is the build-time component of Shaker. It uses Shaker Core to generate the metadata and to create the rollups for the application.

Shaker Compiler can be configured for each environment (staging, test, production) in application.json, and you can also decide what actions to apply to the rollups (minify, js/css lint, etc). Moreover, you can set what we call deployment tasks, which allow developers to deploy rollups to different places:

  • raw - Deploy the assets without doing rollups. This mode will pick the files directly from disk (handy for development).
  • local - Compile rollups and output files locally.
  • s3 - Compile rollups and upload them to Amazon S3 CDN.

Note

Shaker Compiler has nothing to do with any Cocktails product; it is just a name of a Shaker internal component.

Shaker Addon and ShakerHTMLFrame

https://s.yimg.com/oo/cms/products/shaker/images/shaker_addon_beef46a90.png

The Shaker Mojito addon is the runtime component of Shaker. It reads the metadata generated by the Shaker Compiler, and subsequently, manages assets within the application, picking the correct rollup based on the current environment.

The ShakerHTMLFrame is a copy of the Mojito HTMLFrameMojit, but it additionally includes the addon and executes Shaker automatically.

Putting The Pieces Together

This is how all the components work together:

Build time

  1. The shaker command gets executed, invoking Shaker Compiler.
  2. Shaker Compiler gets the application configuration and calls Shaker Core.
  3. Shaker Core analyzes all the application resources and returns a metadata object with all the information necessary to create the rollups.
  4. Shaker Compiler takes the previously generated metadata, generates the minified precomputed rollups, and outputs them as files or to a CDN.
  5. Shaker Compiler outputs a new metadata file which will be picked up at runtime to use the correct rollup based on the context.

Runtime

  1. The Mojito server automatically selects the generated metadata file.
  2. When a request arrives, Mojito computes everything normally until it reaches the ShakerHTMLFrame. Then the Shaker Addon gets executed.
  3. Shaker Addon checks the current context and the executed mojits and chooses from the metadata the proper rollup to include in the page, overriding the required default Mojito assets.
  4. All resources are deployed transparently to the developer.

Introducing Shaker

Shaker is a powerful static asset manager for Mojito applications. It gives users absolute control in transforming, validating, uploading, organizing, and combining resources in order to build robust applications and maximize performance.

Shaker provides contextualization of resources in order to serve different resources based on dimensions such region, language, and device. The user can specify custom or default tasks such as JS/CSS minification and linting for transforming and validating resources. These resources can then be combined (or rolled up) in order to reduce HTTP requests and take advantage of caching. And finally resources can be uploaded to one or more CDN locations to optimize the delivery of resources to clients.