Compiled Assets

In a more minimalistic setup, asset retrieval is fairly straightforward. We might just keep all of our Javascript files in a js/ directory directly under our public document root directory. Then the URL is simply http://example.com/js/whatever.js, and our webserver matches the URL path /js/whatever.js to the filesystem path /path/to/document/root/js/whatever.js, and places the contents of that file in the HTTP response. In most web servers this happens so transparently, that a lot of new developers assume that they're somehow giving direct access to the server's file system. In reality the web server is mediating the interaction, and generating an HTTP response using the contents of these files.

In UserFrosting, the Sprinkle system makes this a little more complicated. Each Sprinkle can contribute its own assets to the application (under its assets/ subdirectory), and it should be possible for a Sprinkle to override assets in another Sprinkle that was loaded earlier in the stack.

Configuring the web server to handle all of the extra logic would be tedious, error-prone, and could easily introduce security risks. For these reasons, UserFrosting has the ability to serve assets through the application, rather than relying on the web server to handle these requests directly.

You may be thinking "won't raw assets add a lot of overhead and slow down my application?" If so, you would be absolutely, 100% correct. This is why raw assets are only meant to be used in development. When you're ready to deploy your application to the live server, one of the tasks that must be done is to compile your assets and asset bundles.

Compiling assets basically does three main things:

  1. It copies assets from individual Sprinkles to the public web directory, so that they can be served directly by the web server instead of having to go through UserFrosting;
  2. It minifies CSS and Javascript assets, so that they are smaller and can be loaded more quickly by the client;
  3. It concatenates assets within each asset bundle into a single file, reducing the number of requests that the client needs to make. Again, this makes the page load more quickly for the client.

To compile assets for a production environment, simply use the Bakery CLI tool:

$ php bakery build-assets --compile

If you have shell access (for example, using a VPS), you can run this directly on your host server as part of your deployment process. Otherwise, you can run Bakery locally before transferring your application to the host server.

Configuration

Once the compiled asset files have been generated, we can easily configure the asset manager to substitute the urls for raw assets in our pages with urls for compiled assets. Simply set the configuration value for assets.use_raw to false. The default production config file has this already configured for you.

If you reload your page and view the source, you'll see that references to the compiled assets are now being used instead:

<!-- Include main CSS asset bundle -->
<link rel="stylesheet" type="text/css" href="http://owlfancy.com/myUserFrostingProject/public/assets/css/main-2c1912c984.css" >

<!-- Page-group-specific CSS asset bundle -->
<link rel="stylesheet" type="text/css" href="http://owlfancy.com/myUserFrostingProject/public/assets/css/guest-5a16771b5a.css" >

The AssetManager service pulls the names of these compiled assets from build/bundle.result.json, which was generated when we ran the build-assets command.

Under the hood

Bakery uses a suite of tools based on Node.js to build and compile assets. Fortunately for you, you should already have Node.js installed if you completed the UserFrosting installation process successfully!

Why do we use Node.js, anyway, instead of a PHP-based asset management tools?

Honestly, it's because the Node.js tools are better. Since every web application needs to minify and process assets, it makes sense to develop the tools for these tasks in a common language. No matter which server-side technology a developer uses, they all have to deal with Javascript sooner or later. Thus, Javascript-based tools are the most popular and actively developed and maintained options.

Gulp

Gulp is a tool used to automate Javascript tasks. The basic idea is that you pass your file(s) through a number of plugins, each of which can perform some kind of transformation on your data. Thus, the output from one plugin becomes the input to the next.

Gulp should have been automatically installed for you during the installation process.

All build tasks are defined in build/gulpfile.js. UserFrosting ships with three preconfigured tasks for building assets:

  1. uf-bundle-build
  2. uf-bundle
  3. uf-bundle-clean

The uf-bundle-build task combines the asset-bundles.json files in each loaded Sprinkle (as per your sprinkles.json file), respecting the collision rules defined in each bundle. This combined bundle file is written to build/asset-bundles.json.

The uf-bundle task uses gulp-bundle-assets to minify and concatenate the assets referenced in each bundle in build/asset-bundles.json into a single file per bundle. These compiled bundles will be placed in the public/assets/ directory by default. It also copies fonts, images, and other files from your Sprinkles to the public/assets/ directory, so that your web server can directly serve these files as well.